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