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 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation;
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 *
17 */
18
19#include "int64x64-128.h"
20
21#include "abort.h"
22#include "assert.h"
23#include "log.h"
24
25/**
26 * \file
27 * \ingroup highprec
28 * Implementation of the ns3::int64x64_t type using a native int128_t type.
29 */
30
31namespace ns3
32{
33
34// Note: Logging in this file is largely avoided due to the
35// number of calls that are made to these functions and the possibility
36// of causing recursions leading to stack overflow
37NS_LOG_COMPONENT_DEFINE("int64x64-128");
38
39/**
40 * \ingroup highprec
41 * Compute the sign of the result of multiplying or dividing
42 * Q64.64 fixed precision operands.
43 *
44 * \param [in] sa The signed value of the first operand.
45 * \param [in] sb The signed value of the second operand.
46 * \param [out] ua The unsigned magnitude of the first operand.
47 * \param [out] ub The unsigned magnitude of the second operand.
48 * \returns \c true if the result will be negative.
49 */
50static inline bool
51output_sign(const int128_t sa, const int128_t sb, uint128_t& ua, uint128_t& ub)
52{
53 bool negA = sa < 0;
54 bool negB = sb < 0;
55 ua = negA ? -static_cast<uint128_t>(sa) : sa;
56 ub = negB ? -static_cast<uint128_t>(sb) : sb;
57 return negA != negB;
58}
59
60void
62{
63 uint128_t a;
64 uint128_t b;
65 bool negative = output_sign(_v, o._v, a, b);
66 uint128_t result = Umul(a, b);
67 if (negative)
68 {
69 NS_ASSERT_MSG(result <= HP128_MASK_HI_BIT, "overflow detected");
70 _v = -result;
71 }
72 else
73 {
74 NS_ASSERT_MSG(result < HP128_MASK_HI_BIT, "overflow detected");
75 _v = result;
76 }
77}
78
81{
82 uint128_t al = a & HP_MASK_LO;
83 uint128_t bl = b & HP_MASK_LO;
84 uint128_t ah = a >> 64;
85 uint128_t bh = b >> 64;
86
87 // Let Q(x) be the unsigned Q64.64 fixed point value represented by the uint128_t x:
88 // Q(x) * 2^64 = x = xh * 2^64 + xl.
89 // (Defining x this way avoids ambiguity about the meaning of the division operators in
90 // Q(x) = x / 2^64 = xh + xl / 2^64.)
91 // Then
92 // Q(a) = ah + al / 2^64
93 // and
94 // Q(b) = bh + bl / 2^64.
95 // We need to find uint128_t c such that
96 // Q(c) = Q(a) * Q(b).
97 // Then
98 // c = Q(c) * 2^64
99 // = (ah + al / 2^64) * (bh + bl / 2^64) * 2^64
100 // = (ah * 2^64 + al) * (bh * 2^64 + bl) / 2^64
101 // = ah * bh * 2^64 + (ah * bl + al * bh) + al * bl / 2^64.
102 // We compute the last part of c by (al * bl) >> 64 which truncates (instead of rounds)
103 // the LSB. If c exceeds 2^127, we might assert. This is because our caller
104 // (Mul function) will not be able to represent the result.
105
106 uint128_t res = (al * bl) >> 64;
107 {
108 // ah, bh <= 2^63 and al, bl <= 2^64 - 1, so mid < 2^128 - 2^64 and there is no
109 // integer overflow.
110 uint128_t mid = ah * bl + al * bh;
111 // res < 2^64, so there is no integer overflow.
112 res += mid;
113 }
114 {
115 uint128_t high = ah * bh;
116 // If high > 2^63, then the result will overflow.
117 NS_ASSERT_MSG(high <= (static_cast<uint128_t>(1) << 63), "overflow detected");
118 high <<= 64;
119 NS_ASSERT_MSG(res + high >= res, "overflow detected");
120 // No overflow since res, high <= 2^127 and one of res, high is < 2^127.
121 res += high;
122 }
123
124 return res;
125}
126
127void
129{
130 uint128_t a;
131 uint128_t b;
132 bool negative = output_sign(_v, o._v, a, b);
133 int128_t result = Udiv(a, b);
134 _v = negative ? -result : result;
135}
136
139{
140 uint128_t rem = a;
141 uint128_t den = b;
142 uint128_t quo = rem / den;
143 rem = rem % den;
144 uint128_t result = quo;
145
146 // Now, manage the remainder
147 const uint64_t DIGITS = 64; // Number of fraction digits (bits) we need
148 const uint128_t ZERO = 0;
149
150 NS_ASSERT_MSG(rem < den, "Remainder not less than divisor");
151
152 uint64_t digis = 0; // Number of digits we have already
153 uint64_t shift = 0; // Number we are going to get this round
154
155 // Skip trailing zeros in divisor
156 while ((shift < DIGITS) && !(den & 0x1))
157 {
158 ++shift;
159 den >>= 1;
160 }
161
162 while ((digis < DIGITS) && (rem != ZERO))
163 {
164 // Skip leading zeros in remainder
165 while ((digis + shift < DIGITS) && !(rem & HP128_MASK_HI_BIT))
166 {
167 ++shift;
168 rem <<= 1;
169 }
170
171 // Cast off denominator bits if:
172 // Need more digits and
173 // LSB is zero or
174 // rem < den
175 while ((digis + shift < DIGITS) && (!(den & 0x1) || (rem < den)))
176 {
177 ++shift;
178 den >>= 1;
179 }
180
181 // Do the division
182 quo = rem / den;
183 rem = rem % den;
184
185 // Add in the quotient as shift bits of the fraction
186 result <<= shift;
187 result += quo;
188
189 digis += shift;
190 shift = 0;
191 }
192 // Did we run out of remainder?
193 if (digis < DIGITS)
194 {
195 shift = DIGITS - digis;
196 result <<= shift;
197 }
198
199 return result;
200}
201
202void
204{
205 bool negResult = _v < 0;
206 uint128_t a = negResult ? -static_cast<uint128_t>(_v) : _v;
207 uint128_t result = UmulByInvert(a, o._v);
208
209 _v = negResult ? -result : result;
210}
211
214{
215 // Since b is assumed to be the output of Invert(), b <= 2^127.
217
218 uint128_t al = a & HP_MASK_LO;
219 uint128_t bl = b & HP_MASK_LO;
220 uint128_t ah = a >> 64;
221 uint128_t bh = b >> 64;
222
223 // Since ah, bh <= 2^63, high <= 2^126 and there is no overflow.
224 uint128_t high = ah * bh;
225
226 // Since ah, bh <= 2^63 and al, bl < 2^64, mid < 2^128 and there is
227 // no overflow.
228 uint128_t mid = ah * bl + al * bh;
229 mid >>= 64;
230
231 // Since high <= 2^126 and mid < 2^64, result < 2^127 and there is no overflow.
232 uint128_t result = high + mid;
233
234 return result;
235}
236
238int64x64_t::Invert(const uint64_t v)
239{
240 NS_ASSERT(v > 1);
241 uint128_t a;
242 a = 1;
243 a <<= 64;
244 int64x64_t result;
245 result._v = Udiv(a, v);
246 int64x64_t tmp(v, 0);
247 tmp.MulByInvert(result);
248 if (tmp.GetHigh() != 1)
249 {
250 result._v += 1;
251 }
252 return result;
253}
254
255} // namespace ns3
NS_ABORT_x macro definitions.
NS_ASSERT() and NS_ASSERT_MSG() macro definitions.
High precision numerical type, implementing Q64.64 fixed precision.
Definition: int64x64-128.h:56
int64_t GetHigh() const
Get the integer portion.
Definition: int64x64-128.h:255
static const uint64_t HP_MASK_LO
Mask for fraction part.
Definition: int64x64-128.h:60
static const uint128_t HP128_MASK_HI_BIT
uint128_t high bit (sign bit).
Definition: int64x64-128.h:58
void Mul(const int64x64_t &o)
Implement *=.
Definition: int64x64-128.cc:61
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.
Definition: int64x64-128.h:468
void Div(const int64x64_t &o)
Implement /=.
static int64x64_t Invert(const uint64_t v)
Compute the inverse of an integer value.
static uint128_t Umul(const uint128_t a, const uint128_t b)
Unsigned multiplication of Q64.64 values.
Definition: int64x64-128.cc:80
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file,...
Definition: assert.h:66
#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:86
__uint128_t uint128_t
Some compilers do not have this defined, so we define it.
Definition: int64x64-128.h:37
__int128_t int128_t
Some compilers do not have this defined, so we define it.
Definition: int64x64-128.h:38
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.
Definition: int64x64-128.cc:51
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:202
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.