A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
int64x64-cairo.h
Go to the documentation of this file.
1// NOLINTBEGIN
2/*
3 * Copyright (c) 2010 INRIA
4 *
5 * SPDX-License-Identifier: GPL-2.0-only
6 */
7
8#ifndef INT64X64_CAIRO_H
9#define INT64X64_CAIRO_H
10
11#include "ns3/core-config.h"
12
13#if defined(INT64X64_USE_CAIRO) && !defined(PYTHON_SCAN)
14/** Using the ns3::int64x64_t based on Cairo 128-bit integers. */
15
17
18#include <cmath> // pow
19
20/**
21 * @file
22 * @ingroup highprec
23 * Declaration of the ns3::int64x64_t type using the Cairo implementation.
24 */
25
26namespace ns3
27{
28
29/**
30 * @internal
31 * The implementation documented here uses cairo 128-bit integers.
32 */
33class int64x64_t
34{
35 /// High bit of fractional part
36 static const uint64_t HPCAIRO_MASK_HI_BIT = (((uint64_t)1) << 63);
37 /// Mask for fraction part
38 static const uint64_t HP_MASK_LO = 0xffffffffffffffffULL;
39
40 public:
41 /// Floating point value of HP_MASK_LO + 1
42 static constexpr long double HP_MAX_64 = (static_cast<uint64_t>(1) << 63) * 2.0L;
43
44 /**
45 * Type tag for the underlying implementation.
46 *
47 * A few testcases are sensitive to implementation,
48 * specifically the double implementation. To handle this,
49 * we expose the underlying implementation type here.
50 */
52 {
53 int128_impl, //!< Native int128_t implementation.
54 cairo_impl, //!< cairo wideint implementation
55 ld_impl, //!< long double implementation
56 };
57
58 /// Type tag for this implementation.
59 static const enum impl_type implementation = cairo_impl;
60
61 /// Default constructor
62 inline int64x64_t()
63 {
64 _v.hi = 0;
65 _v.lo = 0;
66 }
67
68 /**
69 * @name Construct from a floating point value.
70 */
71 /**
72 * @{
73 * Constructor from a floating point.
74 *
75 * @param [in] value Floating value to represent.
76 */
77 inline int64x64_t(const double value)
78 {
79 const int64x64_t tmp((long double)value);
80 _v = tmp._v;
81 }
82
83 inline int64x64_t(const long double value)
84 {
85 const bool negative = value < 0;
86 const long double v = negative ? -value : value;
87
88 long double fhi;
89 long double flo = std::modf(v, &fhi);
90 // Add 0.5 to round, which improves the last count
91 // This breaks these tests:
92 // TestSuite devices-mesh-dot11s-regression
93 // TestSuite devices-mesh-flame-regression
94 // TestSuite routing-aodv-regression
95 // TestSuite routing-olsr-regression
96 // Setting round = 0; breaks:
97 // TestSuite int64x64
98 const long double round = 0.5;
99 flo = flo * HP_MAX_64 + round;
101 const cairo_uint64_t lo = (cairo_uint64_t)flo;
102 if (flo >= HP_MAX_64)
103 {
104 // conversion to uint64 rolled over
105 ++hi;
106 }
107 _v.hi = hi;
108 _v.lo = lo;
109 _v = negative ? _cairo_int128_negate(_v) : _v;
110 }
111
112 /**@}*/
113
114 /**
115 * @name Construct from an integral type.
116 */
117 /**@{*/
118 /**
119 * Construct from an integral type.
120 *
121 * @param [in] v Integer value to represent
122 */
123 inline int64x64_t(const int v)
124 {
125 _v.hi = v;
126 _v.lo = 0;
127 }
128
129 inline int64x64_t(const long int v)
130 {
131 _v.hi = v;
132 _v.lo = 0;
133 }
134
135 inline int64x64_t(const long long int v)
136 {
137 _v.hi = v;
138 _v.lo = 0;
139 }
140
141 inline int64x64_t(const unsigned int v)
142 {
143 _v.hi = v;
144 _v.lo = 0;
145 }
146
147 inline int64x64_t(const unsigned long int v)
148 {
149 _v.hi = v;
150 _v.lo = 0;
151 }
152
153 inline int64x64_t(const unsigned long long int v)
154 {
155 _v.hi = v;
156 _v.lo = 0;
157 }
158
159 /**@}*/
160 /**
161 * Construct from explicit high and low values.
162 *
163 * @param [in] hi Integer portion.
164 * @param [in] lo Fractional portion, already scaled to HP_MAX_64.
165 */
166 explicit inline int64x64_t(const int64_t hi, const uint64_t lo)
167 {
168 _v.hi = hi;
169 _v.lo = lo;
170 }
171
172 /**
173 * Copy constructor.
174 *
175 * @param [in] o Value to copy.
176 */
177 inline int64x64_t(const int64x64_t& o)
178 : _v(o._v)
179 {
180 }
181
182 /**
183 * Assignment.
184 *
185 * @param [in] o Value to assign to this int64x64_t.
186 * @returns a copy of \pname{o}
187 */
189 {
190 _v = o._v;
191 return *this;
192 }
193
194 /** Explicit bool conversion. */
195 inline explicit operator bool() const
196 {
197 return (_v.hi != 0 || _v.lo != 0);
198 }
199
200 /**
201 * Get this value as a double.
202 *
203 * @return This value in floating form.
204 */
205 inline double GetDouble() const
206 {
207 const bool negative = _cairo_int128_negative(_v);
208 const cairo_int128_t value = negative ? _cairo_int128_negate(_v) : _v;
209 const long double fhi = static_cast<long double>(value.hi);
210 const long double flo = value.lo / HP_MAX_64;
211 long double retval = fhi;
212 retval += flo;
213 retval = negative ? -retval : retval;
214 return static_cast<double>(retval);
215 }
216
217 /**
218 * Get the integer portion.
219 *
220 * @return The integer portion of this value.
221 */
222 inline int64_t GetHigh() const
223 {
224 return (int64_t)_v.hi;
225 }
226
227 /**
228 * Get the fractional portion of this value, unscaled.
229 *
230 * @return The fractional portion, unscaled, as an integer.
231 */
232 inline uint64_t GetLow() const
233 {
234 return _v.lo;
235 }
236
237 /**
238 * Truncate to an integer.
239 * Truncation is always toward zero,
240 * @return The value truncated toward zero.
241 */
242 int64_t GetInt() const
243 {
244 const bool negative = _cairo_int128_negative(_v);
245 const cairo_int128_t value = negative ? _cairo_int128_negate(_v) : _v;
246 int64_t retval = value.hi;
247 retval = negative ? -retval : retval;
248 return retval;
249 }
250
251 /**
252 * Round to the nearest int.
253 * Similar to std::round this rounds halfway cases away from zero,
254 * regardless of the current (floating) rounding mode.
255 * @return The value rounded to the nearest int.
256 */
257 int64_t Round() const
258 {
259 const bool negative = _cairo_int128_negative(_v);
260 cairo_uint128_t value = negative ? _cairo_int128_negate(_v) : _v;
261 cairo_uint128_t half{1ULL << 63, 0}; // lo, hi
262 value = _cairo_uint128_add(value, half);
263 int64_t retval = value.hi;
264 retval = negative ? -retval : retval;
265 return retval;
266 }
267
268 /**
269 * Multiply this value by a Q0.128 value, presumably representing an inverse,
270 * completing a division operation.
271 *
272 * @param [in] o The inverse operand.
273 *
274 * @see Invert
275 */
276 void MulByInvert(const int64x64_t& o);
277
278 /**
279 * Compute the inverse of an integer value.
280 *
281 * Ordinary division by an integer would be limited to 64 bits of precision.
282 * Instead, we multiply by the 128-bit inverse of the divisor.
283 * This function computes the inverse to 128-bit precision.
284 * MulByInvert() then completes the division.
285 *
286 * (Really this should be a separate type representing Q0.128.)
287 *
288 * @param [in] v The value to compute the inverse of.
289 * @return A Q0.128 representation of the inverse.
290 */
291 static int64x64_t Invert(const uint64_t v);
292
293 private:
294 /**
295 * @name Arithmetic Operators
296 * Arithmetic operators for int64x64_t.
297 */
298 /**
299 * @{
300 * Arithmetic operator.
301 * @param [in] lhs Left hand argument
302 * @param [in] rhs Right hand argument
303 * @return The result of the operator.
304 */
305
306 friend inline bool operator==(const int64x64_t& lhs, const int64x64_t& rhs)
307 {
308 return _cairo_int128_eq(lhs._v, rhs._v);
309 }
310
311 friend inline bool operator<(const int64x64_t& lhs, const int64x64_t& rhs)
312 {
313 return _cairo_int128_lt(lhs._v, rhs._v);
314 }
315
316 friend inline bool operator>(const int64x64_t& lhs, const int64x64_t& rhs)
317 {
318 return _cairo_int128_gt(lhs._v, rhs._v);
319 }
320
321 friend inline int64x64_t& operator+=(int64x64_t& lhs, const int64x64_t& rhs)
322 {
323 lhs._v = _cairo_int128_add(lhs._v, rhs._v);
324 return lhs;
325 }
326
327 friend inline int64x64_t& operator-=(int64x64_t& lhs, const int64x64_t& rhs)
328 {
329 lhs._v = _cairo_int128_sub(lhs._v, rhs._v);
330 return lhs;
331 }
332
333 friend inline int64x64_t& operator*=(int64x64_t& lhs, const int64x64_t& rhs)
334 {
335 lhs.Mul(rhs);
336 return lhs;
337 }
338
339 friend inline int64x64_t& operator/=(int64x64_t& lhs, const int64x64_t& rhs)
340 {
341 lhs.Div(rhs);
342 return lhs;
343 }
344
345 /** @} */
346
347 /**
348 * @name Unary Operators
349 * Unary operators for int64x64_t.
350 */
351 /**
352 * @{
353 * Unary operator.
354 * @param [in] lhs Left hand argument
355 * @return The result of the operator.
356 */
357 friend inline int64x64_t operator+(const int64x64_t& lhs)
358 {
359 return lhs;
360 }
361
362 friend inline int64x64_t operator-(const int64x64_t& lhs)
363 {
364 int64x64_t tmp = lhs;
365 tmp._v = _cairo_int128_negate(tmp._v);
366 return tmp;
367 }
368
369 friend inline int64x64_t operator!(const int64x64_t& lhs)
370 {
371 return (lhs == int64x64_t()) ? int64x64_t(1, 0) : int64x64_t();
372 }
373
374 /** @} */
375
376 /**
377 * Implement `*=`.
378 *
379 * @param [in] o The other factor.
380 */
381 void Mul(const int64x64_t& o);
382 /**
383 * Implement `/=`.
384 *
385 * @param [in] o The divisor.
386 */
387 void Div(const int64x64_t& o);
388 /**
389 * Unsigned multiplication of Q64.64 values.
390 *
391 * Mathematically this should produce a Q128.128 value;
392 * we keep the central 128 bits, representing the Q64.64 result.
393 * We assert on integer overflow beyond the 64-bit integer portion.
394 *
395 * @param [in] a First factor.
396 * @param [in] b Second factor.
397 * @return The Q64.64 product.
398 *
399 * @internal
400 *
401 * It might be tempting to just use \pname{a} `*` \pname{b}
402 * and be done with it, but it's not that simple. With \pname{a}
403 * and \pname{b} as 128-bit integers, \pname{a} `*` \pname{b}
404 * mathematically produces a 256-bit result, which the computer
405 * truncates to the lowest 128 bits. In our case, where \pname{a}
406 * and \pname{b} are interpreted as Q64.64 fixed point numbers,
407 * the multiplication mathematically produces a Q128.128 fixed point number.
408 * We want the middle 128 bits from the result, truncating both the
409 * high and low 64 bits. To achieve this, we carry out the multiplication
410 * explicitly with 64-bit operands and 128-bit intermediate results.
411 */
412 static cairo_uint128_t Umul(const cairo_uint128_t a, const cairo_uint128_t b);
413 /**
414 * Unsigned division of Q64.64 values.
415 *
416 * @param [in] a Numerator.
417 * @param [in] b Denominator.
418 * @return The Q64.64 representation of `a / b`
419 */
420 static cairo_uint128_t Udiv(const cairo_uint128_t a, const cairo_uint128_t b);
421 /**
422 * Unsigned multiplication of Q64.64 and Q0.128 values.
423 *
424 * @param [in] a The numerator, a Q64.64 value.
425 * @param [in] b The inverse of the denominator, a Q0.128 value
426 * @return The product `a * b`, representing the ration `a / b^-1`
427 *
428 * @see Invert
429 */
430 static cairo_uint128_t UmulByInvert(const cairo_uint128_t a, const cairo_uint128_t b);
431
432 cairo_int128_t _v; //!< The Q64.64 value.
433};
434
435} // namespace ns3
436
437#endif /* defined(INT64X64_USE_CAIRO) && !defined(PYTHON_SCAN) */
438#endif /* INT64X64_CAIRO_H */
439// NOLINTEND
cairo_x function declarations, which provide the fallback high precision arithmetic implementation.
int cairo_I _cairo_int128_lt(cairo_int128_t a, cairo_int128_t b)
cairo_uint128_t cairo_I _cairo_uint128_add(cairo_uint128_t a, cairo_uint128_t b)
#define _cairo_int128_add(a, b)
#define _cairo_int128_eq(a, b)
#define _cairo_int128_negative(a)
#define _cairo_int128_gt(a, b)
#define _cairo_int128_sub(a, b)
#define _cairo_int128_negate(a)
High precision numerical type, implementing Q64.64 fixed precision.
int64_t GetHigh() const
Get the integer portion.
friend bool operator==(const int64x64_t &lhs, const int64x64_t &rhs)
Arithmetic operator.
static const uint64_t HP_MASK_LO
Mask for fraction part.
impl_type
Type tag for the underlying implementation.
@ int128_impl
Native int128_t implementation.
@ ld_impl
long double implementation.
@ cairo_impl
Cairo wideint implementation.
friend int64x64_t & operator-=(int64x64_t &lhs, const int64x64_t &rhs)
Arithmetic operator.
friend int64x64_t operator+(const int64x64_t &lhs)
Unary operator.
static constexpr long double HP_MAX_64
Floating point value of HP_MASK_LO + 1.
int64_t Round() const
Round to the nearest int.
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...
friend bool operator<(const int64x64_t &lhs, const int64x64_t &rhs)
Arithmetic operator.
int64x64_t(const long double value)
Constructor from a floating point.
static uint128_t UmulByInvert(const uint128_t a, const uint128_t b)
Unsigned multiplication of Q64.64 and Q0.128 values.
static enum impl_type implementation
Type tag for this implementation.
friend int64x64_t operator!(const int64x64_t &lhs)
Unary operator.
int128_t _v
The Q64.64 value.
cairo_int128_t _v
The Q64.64 value.
friend int64x64_t & operator*=(int64x64_t &lhs, const int64x64_t &rhs)
Arithmetic operator.
int64x64_t(const int64_t hi, const uint64_t lo)
Construct from explicit high and low values.
void Div(const int64x64_t &o)
Implement /=.
static const uint64_t HPCAIRO_MASK_HI_BIT
High bit of fractional part.
int64x64_t(const unsigned long long int v)
Construct from an integral type.
int64x64_t(const unsigned int v)
Construct from an integral type.
int64x64_t(const long int v)
Construct from an integral type.
int64x64_t(const long long int v)
Construct from an integral type.
double GetDouble() const
Get this value as a double.
friend int64x64_t & operator+=(int64x64_t &lhs, const int64x64_t &rhs)
Arithmetic operator.
int64x64_t(const unsigned long int v)
Construct from an integral type.
int64_t GetInt() const
Truncate to an integer.
int64x64_t & operator=(const int64x64_t &o)
Assignment.
friend bool operator>(const int64x64_t &lhs, const int64x64_t &rhs)
Arithmetic operator.
int64x64_t(const int v)
Construct from an integral type.
uint64_t GetLow() const
Get the fractional portion of this value, unscaled.
friend int64x64_t operator-(const int64x64_t &lhs)
Unary operator.
int64x64_t(const double value)
Constructor from a floating point.
int64x64_t(const int64x64_t &o)
Copy constructor.
friend int64x64_t & operator/=(int64x64_t &lhs, const int64x64_t &rhs)
Arithmetic operator.
int64x64_t()
Default constructor.
static uint128_t Umul(const uint128_t a, const uint128_t b)
Unsigned multiplication of Q64.64 values.
static int64x64_t Invert(const uint64_t v)
Compute the inverse of an integer value.
Every class exported by the ns3 library is enclosed in the ns3 namespace.