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