RandomNumber.hpp
19.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
/**
* \file RandomNumber.hpp
* \brief Header for RandomNumber
*
* Infinite precision random numbers.
*
* Copyright (c) Charles Karney (2006-2013) <charles@karney.com> and licensed
* under the MIT/X11 License. For more information, see
* http://randomlib.sourceforge.net/
**********************************************************************/
#if !defined(RANDOMLIB_RANDOMNUMBER_HPP)
#define RANDOMLIB_RANDOMNUMBER_HPP 1
#include <vector>
#include <iomanip>
#include <limits>
#include <cmath> // for std::pow
#include <RandomLib/UniformInteger.hpp>
namespace RandomLib {
/**
* \brief Infinite precision random numbers.
*
* Implement infinite precision random numbers. Integer part is non-random.
* Fraction part consists of any some number of digits in base
* 2<sup><i>b</i></sup>. If \e m digits have been generated then the
* fraction is uniformly distributed in the open interval
* ∑<sub><i>k</i>=1</sub><sup><i>m</i></sup>
* <i>f</i><sub><i>k</i>−1</sub>/2<sup><i>kb</i></sup> +
* (0,1)/2<sup><i>mb</i></sup>. When a RandomNumber is first constructed the
* integer part is zero and \e m = 0, and the number represents (0,1). A
* RandomNumber is able to represent all numbers in the symmetric open
* interval (−2<sup>31</sup>, 2<sup>31</sup>). In this implementation,
* \e b must one of 1, 2, 3, 4, 8, 12, 16, 20, 24, 28, or 32. (This
* restriction allows printing in hexadecimal and can easily be relaxed.
* There's also no essential reason why the base should be a power of 2.)
*
* @tparam bits the number of bits in each digit.
**********************************************************************/
template<int bits = 1> class RandomNumber {
public:
/**
* Constructor sets number to a random number uniformly distributed in
* (0,1).
**********************************************************************/
RandomNumber() throw() : _n(0), _s(1) {}
/**
* Swap with another RandomNumber. This is a fast way of doing an
* assignment.
*
* @param[in,out] t the RandomNumber to swap with.
**********************************************************************/
void swap(RandomNumber& t) throw() {
if (this != &t) {
std::swap(_n, t._n);
std::swap(_s, t._s);
_f.swap(t._f);
}
}
/**
* Return to initial state, uniformly distributed in (0,1).
**********************************************************************/
void Init() throw() {
STATIC_ASSERT(bits > 0 && bits <= w && (bits < 4 || bits % 4 == 0),
"RandomNumber: unsupported value for bits");
_n = 0;
_s = 1;
_f.clear();
}
/**
* @return the sign of the RandomNumber (± 1).
**********************************************************************/
int Sign() const throw() { return _s; }
/**
* Change the sign of the RandomNumber.
**********************************************************************/
void Negate() throw() { _s *= -1; }
/**
* @return the floor of the RandomNumber.
**********************************************************************/
int Floor() const throw() { return _s > 0 ? int(_n) : -1 - int(_n); }
/**
* @return the ceiling of the RandomNumber.
**********************************************************************/
int Ceiling() const throw() { return _s > 0 ? 1 + int(_n) : - int(_n); }
/**
* @return the unsigned integer component of the RandomNumber.
**********************************************************************/
unsigned UInteger() const throw() { return _n; }
/**
* Add integer \e k to the RandomNumber.
*
* @param[in] k the integer to add.
**********************************************************************/
void AddInteger(int k) throw() {
k += Floor(); // The new floor
int ns = k < 0 ? -1 : 1; // The new sign
if (ns != _s) // If sign changes, set f = 1 - f
for (size_t k = 0; k < Size(); ++k)
_f[k] = ~_f[k] & mask;
_n = ns > 0 ? k : -(k + 1);
}
/**
* Compare with another RandomNumber, *this < \e t
*
* @tparam Random the type of the random generator.
* @param[in,out] r a random generator.
* @param[in,out] t a RandomNumber to compare.
* @return true if *this < \e t.
**********************************************************************/
template<class Random> bool LessThan(Random& r, RandomNumber& t) {
if (this == &t) return false; // same object
if (_s != t._s) return _s < t._s;
if (_n != t._n) return (_s < 0) ^ (_n < t._n);
for (unsigned k = 0; ; ++k) {
// Impose an order on the evaluation of the digits.
const unsigned x = Digit(r,k);
const unsigned y = t.Digit(r,k);
if (x != y) return (_s < 0) ^ (x < y);
// Two distinct numbers are never equal
}
}
/**
* Compare RandomNumber with two others, *this > max(\e u, \e v)
*
* @tparam Random the type of the random generator.
* @param[in,out] r a random generator.
* @param[in,out] u first RandomNumber to compare.
* @param[in,out] v second RandomNumber to compare.
* @return true if *this > max(\e u, \e v).
**********************************************************************/
template<class Random> bool GreaterPair(Random& r,
RandomNumber& u, RandomNumber& v) {
// cmps is set to false as soon as u <= *this, and likewise for cmpt.
bool cmpu = this != &u, cmpv = this != &v && &u != &v;
if (!(cmpu || cmpv)) return true;
// Check signs first
if (cmpu) {
if (u._s > _s) return false; // u > *this
if (u._s < _s) cmpu = false;
}
if (cmpv) {
if (v._s > _s) return false; // v > *this
if (v._s < _s) cmpv = false;
}
if (!(cmpu || cmpv)) return true; // u <= *this && v <= *this
// Check integer parts
if (cmpu) {
if ((_s < 0) ^ (u._n > _n)) return false; // u > *this
if ((_s < 0) ^ (u._n < _n)) cmpu = false;
}
if (cmpv) {
if ((_s < 0) ^ (v._n > _n)) return false; // v > *this
if ((_s < 0) ^ (v._n < _n)) cmpv = false;
}
if (!(cmpu || cmpv)) return true; // u <= *this && v <= *this
// Check fractions
for (unsigned k = 0; ; ++k) {
// Impose an order on the evaluation of the digits. Note that this is
// asymmetric on interchange of u and v; since u is tested first, more
// digits of u are generated than v (on average).
const unsigned x = Digit(r,k);
if (cmpu) {
const unsigned y = u.Digit(r,k);
if ((_s < 0) ^ (y > x)) return false; // u > *this
if ((_s < 0) ^ (y < x)) cmpu = false;
}
if (cmpv) {
const unsigned y = v.Digit(r,k);
if ((_s < 0) ^ (y > x)) return false; // v > *this
if ((_s < 0) ^ (y < x)) cmpv = false;
}
if (!(cmpu || cmpv)) return true; // u <= *this && v <= *this
}
}
/**
* Compare with a fraction, *this < <i>p</i>/<i>q</i>
*
* @tparam Random the type of the random generator.
* @param[in,out] r a random generator.
* @param[in] p the numerator of the fraction.
* @param[in] q the denominator of the fraction (require \e q > 0).
* @return true if *this < <i>p</i>/<i>q</i>.
**********************************************************************/
template<class Random, typename IntType>
bool LessThan(Random& r, IntType p, IntType q) {
for (int k = 0;; ++k) {
if (p <= 0) return false;
if (p >= q) return true;
// Here p is in [1,q-1]. Need to avoid overflow in computation of
// (q-1)<<bits and (2^bits-1)*q
p = (p << bits) - Digit(r,k) * q;
}
}
/**
* Compare with a paritally sampled fraction
*
* @tparam Random the type of the random generator.
* @param[in,out] r a random generator.
* @param[in] p0 the starting point for the numerator.
* @param[in] c the stride for the fraction (require \e c > 0).
* @param[in] q the denominator of the fraction (require \e q > 0).
* @param[in,out] j the increment for the numerator.
* @return true if *this < (<i>p</i><sub>0</sub> + <i>cj</i>)/<i>q</i>.
**********************************************************************/
template<class Random, typename IntType>
bool LessThan(Random& r, IntType p0, IntType c, IntType q,
UniformInteger<IntType, bits>& j) {
for (int k = 0;; ++k) {
if (j. LessThanEqual(r, - p0, c)) return false;
if (j.GreaterThanEqual(r, q - p0, c)) return true;
p0 = (p0 << bits) - IntType(Digit(r,k)) * q;
c <<= bits;
}
}
/**
* @tparam Random the type of the random generator.
* @param[in,out] r a random generator.
* @param[in] k the index of a digit of the fraction
* @return digit number \e k, generating it if necessary.
**********************************************************************/
template<class Random> unsigned Digit(Random& r, unsigned k) {
ExpandTo(r, k + 1);
return _f[k];
}
/**
* Add one digit to the fraction.
*
* @tparam Random the type of the random generator.
* @param[in,out] r a random generator.
**********************************************************************/
template<class Random> void AddDigit(Random& r)
{ _f.push_back(RandomDigit(r)); }
/**
* @param[in] k the index of a digit of the fraction
* @return a const reference to digit number \e k, without generating new
* digits.
* @exception std::out_of_range if the digit hasn't been generated.
**********************************************************************/
const unsigned& RawDigit(unsigned k) const throw()
{ return (const unsigned&)(_f.at(k)); }
/**
* @param[in] k the index of a digit of the fraction
* @return a non-const reference to digit number \e k, without generating
* new digits.
* @exception std::out_of_range if the digit hasn't been generated.
**********************************************************************/
unsigned& RawDigit(unsigned k) throw()
{ return (unsigned&)(_f.at(k)); }
/**
* Return to initial state, uniformly distributed in \e n + (0,1). This is
* similar to Init but also returns the memory used by the object to the
* system. Normally Init should be used.
**********************************************************************/
void Clear() {
std::vector<unsigned> z(0);
_n = 0;
_s = 1;
_f.swap(z);
}
/**
* @return the number of digits in fraction
**********************************************************************/
unsigned Size() const throw() { return unsigned(_f.size()); }
/**
* Return the fraction part of the RandomNumber as a floating point number
* of type RealType rounded to the nearest multiple of
* 1/2<sup><i>p</i></sup>, where \e p =
* std::numeric_limits<RealType>::digits, and, if necessary, creating
* additional digits of the number.
*
* @tparam RealType the floating point type to convert to.
* @tparam Random the type of the random generator.
* @param[in,out] r a random generator for generating the necessary digits.
* @return the fraction of the RandomNumber rounded to a RealType.
**********************************************************************/
template<typename RealType, typename Random> RealType Fraction(Random& r) {
STATIC_ASSERT(!std::numeric_limits<RealType>::is_integer,
"RandomNumber::Fraction: invalid real type RealType");
const int d = std::numeric_limits<RealType>::digits;
const int k = (d + bits - 1)/bits;
const int kg = (d + bits)/bits; // For guard bit
RealType y = 0;
if (Digit(r, kg - 1) & (1U << (kg * bits - d - 1)))
// if guard bit is set, round up.
y += std::pow(RealType(2), -d);
const RealType fact = std::pow(RealType(2), -bits);
RealType mult = RealType(1);
for (int i = 0; i < k; ++i) {
mult *= fact;
y += mult * RealType(i < k - 1 ? RawDigit(i) :
RawDigit(i) & (~0U << (k * bits - d)));
}
return y;
}
/**
* Return the value of the RandomNumber rounded to nearest floating point
* number of type RealType and, if necessary, creating additional digits of
* the number.
*
* @tparam RealType the floating point type to convert to.
* @tparam Random the type of the random generator.
* @param[in,out] r a random generator for generating the necessary digits.
* @return the value of the RandomNumber rounded to a RealType.
**********************************************************************/
template<typename RealType, class Random> RealType Value(Random& r) {
// Ignore the possibility of overflow here (OK because int doesn't
// currently overflow any real type). Assume the real type supports
// denormalized numbers. Need to treat rounding explicitly since the
// missing digits always imply rounding up.
STATIC_ASSERT(!std::numeric_limits<RealType>::is_integer,
"RandomNumber::Value: invalid real type RealType");
const int digits = std::numeric_limits<RealType>::digits,
min_exp = std::numeric_limits<RealType>::min_exponent;
RealType y;
int lead; // Position of leading bit (0.5 = position 0)
if (_n) lead = highest_bit_idx(_n);
else {
int i = 0;
while ( Digit(r, i) == 0 && i < (-min_exp)/bits ) ++i;
lead = highest_bit_idx(RawDigit(i)) - (i + 1) * bits;
// To handle denormalized numbers set lead = max(lead, min_exp)
lead = lead > min_exp ? lead : min_exp;
}
int trail = lead - digits; // Position of guard bit (0.5 = position 0)
if (trail > 0) {
y = RealType(_n & (~0U << trail));
if (_n & (1U << (trail - 1)))
y += std::pow(RealType(2), trail);
} else {
y = RealType(_n);
int k = (-trail)/bits; // Byte with guard bit
if (Digit(r, k) & (1U << ((k + 1) * bits + trail - 1)))
// If guard bit is set, round bit (some subsequent bit will be 1).
y += std::pow(RealType(2), trail);
// Byte with trailing bit (can be negative)
k = (-trail - 1 + bits)/bits - 1;
const RealType fact = std::pow(RealType(2), -bits);
RealType mult = RealType(1);
for (int i = 0; i <= k; ++i) {
mult *= fact;
y += mult *
RealType(i < k ? RawDigit(i) :
RawDigit(i) & (~0U << ((k + 1) * bits + trail)));
}
}
if (_s < 0) y *= -1;
return y;
}
/**
* Return the range of possible values for the RandomNumber as pair of
* doubles. This doesn't create any additional digits of the result and
* doesn't try to control roundoff.
*
* @return a pair denoting the range with first being the lower limit and
* second being the upper limit.
**********************************************************************/
std::pair<double, double> Range() const throw() {
double y = _n;
const double fact = std::pow(double(2), -bits);
double mult = double(1);
for (unsigned i = 0; i < Size(); ++i) {
mult *= fact;
y += mult * RawDigit(i);
}
return std::pair<double, double>(_s > 0 ? y : -(y + mult),
_s > 0 ? (y + mult) : -y);
}
/**
* @tparam Random the type of the random generator.
* @param[in,out] r a random generator.
* @return a random digit in [0, 2<sup><i>bits</i></sup>).
**********************************************************************/
template<class Random> static unsigned RandomDigit(Random& r) throw()
{ return unsigned(r.template Integer<bits>()); }
private:
/**
* The integer part
**********************************************************************/
unsigned _n;
/**
* The sign
**********************************************************************/
int _s;
/**
* The fraction part
**********************************************************************/
std::vector<unsigned> _f;
/**
* Fill RandomNumber to \e k digits.
**********************************************************************/
template<class Random> void ExpandTo(Random& r, size_t k) {
size_t l = _f.size();
if (k <= l)
return;
_f.resize(k);
for (size_t i = l; i < k; ++i)
_f[i] = RandomDigit(r);
}
/**
* Return index [0..32] of highest bit set. Return 0 if x = 0, 32 is if x
* = ~0. (From Algorithms for programmers by Joerg Arndt.)
**********************************************************************/
static int highest_bit_idx(unsigned x) throw() {
if (x == 0) return 0;
int r = 1;
if (x & 0xffff0000U) { x >>= 16; r += 16; }
if (x & 0x0000ff00U) { x >>= 8; r += 8; }
if (x & 0x000000f0U) { x >>= 4; r += 4; }
if (x & 0x0000000cU) { x >>= 2; r += 2; }
if (x & 0x00000002U) { r += 1; }
return r;
}
/**
* The number of bits in unsigned.
**********************************************************************/
static const int w = std::numeric_limits<unsigned>::digits;
public:
/**
* A mask for the digits.
**********************************************************************/
static const unsigned mask =
bits == w ? ~0U : ~(~0U << (bits < w ? bits : 0));
};
/**
* \relates RandomNumber
* Print a RandomNumber. Format is n.dddd... where the base for printing is
* 2<sup>max(4,<i>b</i>)</sup>. The ... represents an infinite sequence of
* ungenerated random digits (uniformly distributed). Thus with \e b = 1,
* 0.0... = (0,1/2), 0.00... = (0,1/4), 0.11... = (3/4,1), etc.
**********************************************************************/
template<int bits>
std::ostream& operator<<(std::ostream& os, const RandomNumber<bits>& n) {
const std::ios::fmtflags oldflags = os.flags();
RandomNumber<bits> t = n;
os << (t.Sign() > 0 ? "+" : "-");
unsigned i = t.UInteger();
os << std::hex << std::setfill('0');
if (i == 0)
os << "0";
else {
bool first = true;
const int w = std::numeric_limits<unsigned>::digits;
const unsigned mask = RandomNumber<bits>::mask;
for (int s = ((w + bits - 1)/bits) * bits - bits; s >= 0; s -= bits) {
unsigned d = mask & (i >> s);
if (d || !first) {
if (first) {
os << d;
first = false;
}
else
os << std::setw((bits+3)/4) << d;
}
}
}
os << ".";
unsigned s = t.Size();
for (unsigned i = 0; i < s; ++i)
os << std::setw((bits+3)/4) << t.RawDigit(i);
os << "..." << std::setfill(' ');
os.flags(oldflags);
return os;
}
} // namespace RandomLib
#endif // RANDOMLIB_RANDOMNUMBER_HPP