A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
int64x64-cairo.cc
Go to the documentation of this file.
1// NOLINTBEGIN
2/*
3 * Copyright (c) 2006 INRIA
4 *
5 * SPDX-License-Identifier: GPL-2.0-only
6 *
7 * Author: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
8 */
9#include "int64x64-cairo.h"
10
11#include "abort.h"
12#include "assert.h"
13#include "log.h"
14#include "test.h"
15
16#include <cmath>
17#include <iostream>
18
19#if defined(INT64X64_USE_CAIRO) && !defined(PYTHON_SCAN)
20
21// Include directly to allow optimizations within this compilation unit.
22extern "C"
23{
24#include "cairo-wideint.c"
25}
26
27/**
28 * @file
29 * @ingroup highprec
30 * Implementation of the ns3::int64x64_t type using the Cairo implementation.
31 */
32
33namespace ns3
34{
35
36// Note: Logging in this file is largely avoided due to the
37// number of calls that are made to these functions and the possibility
38// of causing recursions leading to stack overflow
39NS_LOG_COMPONENT_DEFINE("int64x64-cairo");
40
41/**
42 * @ingroup highprec
43 * Compute the sign of the result of multiplying or dividing
44 * Q64.64 fixed precision operands.
45 *
46 * @param [in] sa The signed value of the first operand.
47 * @param [in] sb The signed value of the second operand.
48 * @param [out] ua The unsigned magnitude of the first operand.
49 * @param [out] ub The unsigned magnitude of the second operand.
50 * @returns True if the result will be negative.
51 */
52static inline bool
54 const cairo_int128_t sb,
55 cairo_uint128_t& ua,
56 cairo_uint128_t& ub)
57{
58 bool negA = _cairo_int128_negative(sa);
59 bool negB = _cairo_int128_negative(sb);
62 ua = negA ? _cairo_uint128_negate(ua) : ua;
63 ub = negB ? _cairo_uint128_negate(ub) : ub;
64 return (negA && !negB) || (!negA && negB);
65}
66
67void
68int64x64_t::Mul(const int64x64_t& o)
69{
70 cairo_uint128_t a, b;
71 bool sign = output_sign(_v, o._v, a, b);
72 cairo_uint128_t result = Umul(a, b);
73 _v = sign ? _cairo_uint128_negate(result) : result;
74}
75
76cairo_uint128_t
77int64x64_t::Umul(const cairo_uint128_t a, const cairo_uint128_t b)
78{
79 cairo_uint128_t result;
80 cairo_uint128_t hiPart, loPart, midPart;
81 cairo_uint128_t res1, res2;
82
83 // Multiplying (a.h 2^64 + a.l) x (b.h 2^64 + b.l) =
84 // 2^128 a.h b.h + 2^64*(a.h b.l+b.h a.l) + a.l b.l
85 // get the low part a.l b.l
86 // multiply the fractional part
87 loPart = _cairo_uint64x64_128_mul(a.lo, b.lo);
88 // compute the middle part 2^64*(a.h b.l+b.h a.l)
89 midPart = _cairo_uint128_add(_cairo_uint64x64_128_mul(a.lo, b.hi),
90 _cairo_uint64x64_128_mul(a.hi, b.lo));
91 // compute the high part 2^128 a.h b.h
92 hiPart = _cairo_uint64x64_128_mul(a.hi, b.hi);
93 // if the high part is not zero, put a warning
94 NS_ABORT_MSG_IF(hiPart.hi != 0,
95 "High precision 128 bits multiplication error: multiplication overflow.");
96
97 // Adding 64-bit terms to get 128-bit results, with carries
98 res1 = _cairo_uint64_to_uint128(loPart.hi);
99 res2 = _cairo_uint64_to_uint128(midPart.lo);
100 result = _cairo_uint128_add(res1, res2);
101
102 res1 = _cairo_uint64_to_uint128(midPart.hi);
103 res2 = _cairo_uint64_to_uint128(hiPart.lo);
104 res1 = _cairo_uint128_add(res1, res2);
105 res1 = _cairo_uint128_lsl(res1, 64);
106
107 result = _cairo_uint128_add(result, res1);
108
109 return result;
110}
111
112void
114{
115 cairo_uint128_t a, b;
116 bool sign = output_sign(_v, o._v, a, b);
117 cairo_uint128_t result = Udiv(a, b);
118 _v = sign ? _cairo_uint128_negate(result) : result;
119}
120
121cairo_uint128_t
122int64x64_t::Udiv(const cairo_uint128_t a, const cairo_uint128_t b)
123{
124 cairo_uint128_t den = b;
126 cairo_uint128_t result = qr.quo;
127 cairo_uint128_t rem = qr.rem;
128
129 // Now, manage the remainder
130 const uint64_t DIGITS = 64; // Number of fraction digits (bits) we need
131 const cairo_uint128_t ZERO = _cairo_uint32_to_uint128((uint32_t)0);
132
133 NS_ASSERT_MSG(_cairo_uint128_lt(rem, den), "Remainder not less than divisor");
134
135 uint64_t digis = 0; // Number of digits we have already
136 uint64_t shift = 0; // Number we are going to get this round
137
138 // Skip trailing zeros in divisor
139 while ((shift < DIGITS) && !(den.lo & 0x1))
140 {
141 ++shift;
142 den = _cairo_uint128_rsl(den, 1);
143 }
144
145 while ((digis < DIGITS) && !(_cairo_uint128_eq(rem, ZERO)))
146 {
147 // Skip leading zeros in remainder
148 while ((digis + shift < DIGITS) && !(rem.hi & HPCAIRO_MASK_HI_BIT))
149 {
150 ++shift;
151 rem = _cairo_int128_lsl(rem, 1);
152 }
153
154 // Cast off denominator bits if:
155 // Need more digits and
156 // LSB is zero or
157 // rem < den
158 while ((digis + shift < DIGITS) && (!(den.lo & 0x1) || _cairo_uint128_lt(rem, den)))
159 {
160 ++shift;
161 den = _cairo_uint128_rsl(den, 1);
162 }
163
164 // Do the division
165 qr = _cairo_uint128_divrem(rem, den);
166
167 // Add in the quotient as shift bits of the fraction
168 result = _cairo_uint128_lsl(result, static_cast<int>(shift));
169 result = _cairo_uint128_add(result, qr.quo);
170 rem = qr.rem;
171 digis += shift;
172 shift = 0;
173 }
174 // Did we run out of remainder?
175 if (digis < DIGITS)
176 {
177 shift = DIGITS - digis;
178 result = _cairo_uint128_lsl(result, static_cast<int>(shift));
179 }
180
181 return result;
182}
183
184void
186{
187 bool sign = _cairo_int128_negative(_v);
188 cairo_uint128_t a = sign ? _cairo_int128_negate(_v) : _v;
189 cairo_uint128_t result = UmulByInvert(a, o._v);
190
191 _v = sign ? _cairo_int128_negate(result) : result;
192}
193
194cairo_uint128_t
195int64x64_t::UmulByInvert(const cairo_uint128_t a, const cairo_uint128_t b)
196{
197 cairo_uint128_t result;
198 cairo_uint128_t hi, mid;
199 hi = _cairo_uint64x64_128_mul(a.hi, b.hi);
201 _cairo_uint64x64_128_mul(a.lo, b.hi));
202 mid.lo = mid.hi;
203 mid.hi = 0;
204 result = _cairo_uint128_add(hi, mid);
205 return result;
206}
207
209int64x64_t::Invert(const uint64_t v)
210{
211 NS_ASSERT(v > 1);
212 cairo_uint128_t a, factor;
213 a.hi = 1;
214 a.lo = 0;
215 factor.hi = 0;
216 factor.lo = v;
217 int64x64_t result;
218 result._v = Udiv(a, factor);
219 int64x64_t tmp = int64x64_t(v, 0);
220 tmp.MulByInvert(result);
221 if (tmp.GetHigh() != 1)
222 {
223 cairo_uint128_t one = {1, 0};
224 result._v = _cairo_uint128_add(result._v, one);
225 }
226 return result;
227}
228
229} // namespace ns3
230
231#endif /* INT64X64_CAIRO_H */
232// NOLINTEND
NS_ABORT_x macro definitions.
NS_ASSERT() and NS_ASSERT_MSG() macro definitions.
cairo_uint128_t cairo_I _cairo_uint128_negate(cairo_uint128_t a)
int cairo_I _cairo_uint128_eq(cairo_uint128_t a, cairo_uint128_t b)
cairo_uint128_t cairo_I _cairo_uint128_add(cairo_uint128_t a, cairo_uint128_t b)
#define _cairo_int128_lsl(a, b)
#define _cairo_int128_to_uint128(i)
cairo_uint128_t cairo_I _cairo_uint32_to_uint128(uint32_t i)
cairo_uint128_t cairo_I _cairo_uint64x64_128_mul(cairo_uint64_t a, cairo_uint64_t b)
cairo_uint128_t cairo_I _cairo_uint128_lsl(cairo_uint128_t a, int shift)
#define _cairo_int128_negative(a)
cairo_uint128_t cairo_I _cairo_uint128_rsl(cairo_uint128_t a, int shift)
int cairo_I _cairo_uint128_lt(cairo_uint128_t a, cairo_uint128_t b)
cairo_uquorem128_t cairo_I _cairo_uint128_divrem(cairo_uint128_t num, cairo_uint128_t den)
#define _cairo_int128_negate(a)
cairo_uint128_t cairo_I _cairo_uint64_to_uint128(cairo_uint64_t i)
Implementation of the cairo_x functions which implement high precision arithmetic.
High precision numerical type, implementing Q64.64 fixed precision.
int64_t GetHigh() const
Get the integer portion.
void Mul(const int64x64_t &o)
Implement *=.
static uint128_t Udiv(const uint128_t a, const uint128_t b)
Unsigned division of Q64.64 values.
void MulByInvert(const int64x64_t &o)
Multiply this value by a Q0.128 value, presumably representing an inverse, completing a division oper...
static uint128_t UmulByInvert(const uint128_t a, const uint128_t b)
Unsigned multiplication of Q64.64 and Q0.128 values.
int128_t _v
The Q64.64 value.
void Div(const int64x64_t &o)
Implement /=.
static const uint64_t HPCAIRO_MASK_HI_BIT
High bit of fractional part.
static int64x64_t Invert(const uint64_t v)
Compute the inverse of an integer value.
int64x64_t()
Default constructor.
static uint128_t Umul(const uint128_t a, const uint128_t b)
Unsigned multiplication of Q64.64 values.
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file,...
Definition assert.h:55
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
Definition assert.h:75
#define NS_ABORT_MSG_IF(cond, msg)
Abnormal program termination if a condition is true, with a message.
Definition abort.h:97
static bool output_sign(const int128_t sa, const int128_t sb, uint128_t &ua, uint128_t &ub)
Compute the sign of the result of multiplying or dividing Q64.64 fixed precision operands.
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition log.h:191
Using the ns3::int64x64_t based on Cairo 128-bit integers.
Debug message logging.
Every class exported by the ns3 library is enclosed in the ns3 namespace.
ns3::TestCase, ns3::TestSuite, ns3::TestRunner declarations, and NS_TEST_ASSERT macro definitions.