A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
int64x64-128.cc
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#include "int64x64-128.h"
8
9#include "assert.h"
10#include "log.h"
11
12#if defined(INT64X64_USE_128) && !defined(PYTHON_SCAN)
13
14/**
15 * @file
16 * @ingroup highprec
17 * Implementation of the ns3::int64x64_t type using a native int128_t type.
18 */
19
20namespace ns3
21{
22
23// Note: Logging in this file is largely avoided due to the
24// number of calls that are made to these functions and the possibility
25// of causing recursions leading to stack overflow
26NS_LOG_COMPONENT_DEFINE("int64x64-128");
27
28/**
29 * @ingroup highprec
30 * Compute the sign of the result of multiplying or dividing
31 * Q64.64 fixed precision operands.
32 *
33 * @param [in] sa The signed value of the first operand.
34 * @param [in] sb The signed value of the second operand.
35 * @param [out] ua The unsigned magnitude of the first operand.
36 * @param [out] ub The unsigned magnitude of the second operand.
37 * @returns \c true if the result will be negative.
38 */
39static inline bool
40output_sign(const int128_t sa, const int128_t sb, uint128_t& ua, uint128_t& ub)
41{
42 bool negA = sa < 0;
43 bool negB = sb < 0;
44 ua = negA ? -static_cast<uint128_t>(sa) : sa;
45 ub = negB ? -static_cast<uint128_t>(sb) : sb;
46 return negA != negB;
47}
48
49void
51{
52 uint128_t a;
53 uint128_t b;
54 bool negative = output_sign(_v, o._v, a, b);
55 uint128_t result = Umul(a, b);
56 if (negative)
57 {
58 NS_ASSERT_MSG(result <= HP128_MASK_HI_BIT, "overflow detected");
59 _v = -result;
60 }
61 else
62 {
63 NS_ASSERT_MSG(result < HP128_MASK_HI_BIT, "overflow detected");
64 _v = result;
65 }
66}
67
70{
71 uint128_t al = a & HP_MASK_LO;
72 uint128_t bl = b & HP_MASK_LO;
73 uint128_t ah = a >> 64;
74 uint128_t bh = b >> 64;
75
76 // Let Q(x) be the unsigned Q64.64 fixed point value represented by the uint128_t x:
77 // Q(x) * 2^64 = x = xh * 2^64 + xl.
78 // (Defining x this way avoids ambiguity about the meaning of the division operators in
79 // Q(x) = x / 2^64 = xh + xl / 2^64.)
80 // Then
81 // Q(a) = ah + al / 2^64
82 // and
83 // Q(b) = bh + bl / 2^64.
84 // We need to find uint128_t c such that
85 // Q(c) = Q(a) * Q(b).
86 // Then
87 // c = Q(c) * 2^64
88 // = (ah + al / 2^64) * (bh + bl / 2^64) * 2^64
89 // = (ah * 2^64 + al) * (bh * 2^64 + bl) / 2^64
90 // = ah * bh * 2^64 + (ah * bl + al * bh) + al * bl / 2^64.
91 // We compute the last part of c by (al * bl) >> 64 which truncates (instead of rounds)
92 // the LSB. If c exceeds 2^127, we might assert. This is because our caller
93 // (Mul function) will not be able to represent the result.
94
95 uint128_t res = (al * bl) >> 64;
96 {
97 // ah, bh <= 2^63 and al, bl <= 2^64 - 1, so mid < 2^128 - 2^64 and there is no
98 // integer overflow.
99 uint128_t mid = ah * bl + al * bh;
100 // res < 2^64, so there is no integer overflow.
101 res += mid;
102 }
103 {
104 uint128_t high = ah * bh;
105 // If high > 2^63, then the result will overflow.
106 NS_ASSERT_MSG(high <= (static_cast<uint128_t>(1) << 63), "overflow detected");
107 high <<= 64;
108 NS_ASSERT_MSG(res + high >= res, "overflow detected");
109 // No overflow since res, high <= 2^127 and one of res, high is < 2^127.
110 res += high;
111 }
112
113 return res;
114}
115
116void
118{
119 uint128_t a;
120 uint128_t b;
121 bool negative = output_sign(_v, o._v, a, b);
122 int128_t result = Udiv(a, b);
123 _v = negative ? -result : result;
124}
125
128{
129 uint128_t rem = a;
130 uint128_t den = b;
131 uint128_t quo = rem / den;
132 rem = rem % den;
134
135 // Now, manage the remainder
136 const uint64_t DIGITS = 64; // Number of fraction digits (bits) we need
137 const uint128_t ZERO = 0;
138
139 NS_ASSERT_MSG(rem < den, "Remainder not less than divisor");
140
141 uint64_t digis = 0; // Number of digits we have already
142 uint64_t shift = 0; // Number we are going to get this round
143
144 // Skip trailing zeros in divisor
145 while ((shift < DIGITS) && !(den & 0x1))
146 {
147 ++shift;
148 den >>= 1;
149 }
150
151 while ((digis < DIGITS) && (rem != ZERO))
152 {
153 // Skip leading zeros in remainder
154 while ((digis + shift < DIGITS) && !(rem & HP128_MASK_HI_BIT))
155 {
156 ++shift;
157 rem <<= 1;
158 }
159
160 // Cast off denominator bits if:
161 // Need more digits and
162 // LSB is zero or
163 // rem < den
164 while ((digis + shift < DIGITS) && (!(den & 0x1) || (rem < den)))
165 {
166 ++shift;
167 den >>= 1;
168 }
169
170 // Do the division
171 quo = rem / den;
172 rem = rem % den;
173
174 // Add in the quotient as shift bits of the fraction
175 result <<= shift;
176 result += quo;
177
178 digis += shift;
179 shift = 0;
180 }
181 // Did we run out of remainder?
182 if (digis < DIGITS)
183 {
184 shift = DIGITS - digis;
185 result <<= shift;
186 }
187
188 return result;
189}
190
191void
193{
194 bool negResult = _v < 0;
195 uint128_t a = negResult ? -static_cast<uint128_t>(_v) : _v;
197
198 _v = negResult ? -result : result;
199}
200
203{
204 // Since b is assumed to be the output of Invert(), b <= 2^127.
206
207 uint128_t al = a & HP_MASK_LO;
208 uint128_t bl = b & HP_MASK_LO;
209 uint128_t ah = a >> 64;
210 uint128_t bh = b >> 64;
211
212 // Since ah, bh <= 2^63, high <= 2^126 and there is no overflow.
213 uint128_t high = ah * bh;
214
215 // Since ah, bh <= 2^63 and al, bl < 2^64, mid < 2^128 and there is
216 // no overflow.
217 uint128_t mid = ah * bl + al * bh;
218 mid >>= 64;
219
220 // Since high <= 2^126 and mid < 2^64, result < 2^127 and there is no overflow.
221 uint128_t result = high + mid;
222
223 return result;
224}
225
227int64x64_t::Invert(const uint64_t v)
228{
229 NS_ASSERT(v > 1);
230 uint128_t a;
231 a = 1;
232 a <<= 64;
234 result._v = Udiv(a, v);
235 int64x64_t tmp(v, 0);
236 tmp.MulByInvert(result);
237 if (tmp.GetHigh() != 1)
238 {
239 result._v += 1;
240 }
241 return result;
242}
243
244} // namespace ns3
245
246#endif /* INT64X64_128_H */
NS_ASSERT() and NS_ASSERT_MSG() macro definitions.
result rem
result quo
uint32_t v
return result
High precision numerical type, implementing Q64.64 fixed precision.
int64_t GetHigh() const
Get the integer portion.
static const uint64_t HP_MASK_LO
Mask for fraction part.
static const uint128_t HP128_MASK_HI_BIT
uint128_t high bit (sign bit).
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 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
__uint128_t uint128_t
Use uint128_t for int64x64_t implementation.
__int128_t int128_t
Use uint128_t for int64x64_t implementation.
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:194
Declaration of the ns3::int64x64_t type using a native int128_t type.
Debug message logging.
Every class exported by the ns3 library is enclosed in the ns3 namespace.