• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 The Abseil Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "absl/random/uniform_real_distribution.h"
16 
17 #include <cmath>
18 #include <cstdint>
19 #include <iterator>
20 #include <random>
21 #include <sstream>
22 #include <string>
23 #include <vector>
24 
25 #include "gmock/gmock.h"
26 #include "gtest/gtest.h"
27 #include "absl/base/internal/raw_logging.h"
28 #include "absl/random/internal/chi_square.h"
29 #include "absl/random/internal/distribution_test_util.h"
30 #include "absl/random/internal/sequence_urbg.h"
31 #include "absl/random/random.h"
32 #include "absl/strings/str_cat.h"
33 
34 // NOTES:
35 // * Some documentation on generating random real values suggests that
36 //   it is possible to use std::nextafter(b, DBL_MAX) to generate a value on
37 //   the closed range [a, b]. Unfortunately, that technique is not universally
38 //   reliable due to floating point quantization.
39 //
40 // * absl::uniform_real_distribution<float> generates between 2^28 and 2^29
41 //   distinct floating point values in the range [0, 1).
42 //
43 // * absl::uniform_real_distribution<float> generates at least 2^23 distinct
44 //   floating point values in the range [1, 2). This should be the same as
45 //   any other range covered by a single exponent in IEEE 754.
46 //
47 // * absl::uniform_real_distribution<double> generates more than 2^52 distinct
48 //   values in the range [0, 1), and should generate at least 2^52 distinct
49 //   values in the range of [1, 2).
50 //
51 
52 namespace {
53 
54 template <typename RealType>
55 class UniformRealDistributionTest : public ::testing::Test {};
56 
57 #if defined(__EMSCRIPTEN__)
58 using RealTypes = ::testing::Types<float, double>;
59 #else
60 using RealTypes = ::testing::Types<float, double, long double>;
61 #endif  // defined(__EMSCRIPTEN__)
62 
63 TYPED_TEST_SUITE(UniformRealDistributionTest, RealTypes);
64 
TYPED_TEST(UniformRealDistributionTest,ParamSerializeTest)65 TYPED_TEST(UniformRealDistributionTest, ParamSerializeTest) {
66   using param_type =
67       typename absl::uniform_real_distribution<TypeParam>::param_type;
68 
69   constexpr const TypeParam a{1152921504606846976};
70 
71   constexpr int kCount = 1000;
72   absl::InsecureBitGen gen;
73   for (const auto& param : {
74            param_type(),
75            param_type(TypeParam(2.0), TypeParam(2.0)),  // Same
76            param_type(TypeParam(-0.1), TypeParam(0.1)),
77            param_type(TypeParam(0.05), TypeParam(0.12)),
78            param_type(TypeParam(-0.05), TypeParam(0.13)),
79            param_type(TypeParam(-0.05), TypeParam(-0.02)),
80            // double range = 0
81            // 2^60 , 2^60 + 2^6
82            param_type(a, TypeParam(1152921504606847040)),
83            // 2^60 , 2^60 + 2^7
84            param_type(a, TypeParam(1152921504606847104)),
85            // double range = 2^8
86            // 2^60 , 2^60 + 2^8
87            param_type(a, TypeParam(1152921504606847232)),
88            // float range = 0
89            // 2^60 , 2^60 + 2^36
90            param_type(a, TypeParam(1152921573326323712)),
91            // 2^60 , 2^60 + 2^37
92            param_type(a, TypeParam(1152921642045800448)),
93            // float range = 2^38
94            // 2^60 , 2^60 + 2^38
95            param_type(a, TypeParam(1152921779484753920)),
96            // Limits
97            param_type(0, std::numeric_limits<TypeParam>::max()),
98            param_type(std::numeric_limits<TypeParam>::lowest(), 0),
99            param_type(0, std::numeric_limits<TypeParam>::epsilon()),
100            param_type(-std::numeric_limits<TypeParam>::epsilon(),
101                       std::numeric_limits<TypeParam>::epsilon()),
102            param_type(std::numeric_limits<TypeParam>::epsilon(),
103                       2 * std::numeric_limits<TypeParam>::epsilon()),
104        }) {
105     // Validate parameters.
106     const auto a = param.a();
107     const auto b = param.b();
108     absl::uniform_real_distribution<TypeParam> before(a, b);
109     EXPECT_EQ(before.a(), param.a());
110     EXPECT_EQ(before.b(), param.b());
111 
112     {
113       absl::uniform_real_distribution<TypeParam> via_param(param);
114       EXPECT_EQ(via_param, before);
115     }
116 
117     std::stringstream ss;
118     ss << before;
119     absl::uniform_real_distribution<TypeParam> after(TypeParam(1.0),
120                                                      TypeParam(3.1));
121 
122     EXPECT_NE(before.a(), after.a());
123     EXPECT_NE(before.b(), after.b());
124     EXPECT_NE(before.param(), after.param());
125     EXPECT_NE(before, after);
126 
127     ss >> after;
128 
129     EXPECT_EQ(before.a(), after.a());
130     EXPECT_EQ(before.b(), after.b());
131     EXPECT_EQ(before.param(), after.param());
132     EXPECT_EQ(before, after);
133 
134     // Smoke test.
135     auto sample_min = after.max();
136     auto sample_max = after.min();
137     for (int i = 0; i < kCount; i++) {
138       auto sample = after(gen);
139       // Failure here indicates a bug in uniform_real_distribution::operator(),
140       // or bad parameters--range too large, etc.
141       if (after.min() == after.max()) {
142         EXPECT_EQ(sample, after.min());
143       } else {
144         EXPECT_GE(sample, after.min());
145         EXPECT_LT(sample, after.max());
146       }
147       if (sample > sample_max) {
148         sample_max = sample;
149       }
150       if (sample < sample_min) {
151         sample_min = sample;
152       }
153     }
154 
155     if (!std::is_same<TypeParam, long double>::value) {
156       // static_cast<double>(long double) can overflow.
157       std::string msg = absl::StrCat("Range: ", static_cast<double>(sample_min),
158                                      ", ", static_cast<double>(sample_max));
159       ABSL_RAW_LOG(INFO, "%s", msg.c_str());
160     }
161   }
162 }
163 
164 #ifdef _MSC_VER
165 #pragma warning(push)
166 #pragma warning(disable:4756)  // Constant arithmetic overflow.
167 #endif
TYPED_TEST(UniformRealDistributionTest,ViolatesPreconditionsDeathTest)168 TYPED_TEST(UniformRealDistributionTest, ViolatesPreconditionsDeathTest) {
169 #if GTEST_HAS_DEATH_TEST
170   // Hi < Lo
171   EXPECT_DEBUG_DEATH(
172       { absl::uniform_real_distribution<TypeParam> dist(10.0, 1.0); }, "");
173 
174   // Hi - Lo > numeric_limits<>::max()
175   EXPECT_DEBUG_DEATH(
176       {
177         absl::uniform_real_distribution<TypeParam> dist(
178             std::numeric_limits<TypeParam>::lowest(),
179             std::numeric_limits<TypeParam>::max());
180       },
181       "");
182 #endif  // GTEST_HAS_DEATH_TEST
183 #if defined(NDEBUG)
184   // opt-mode, for invalid parameters, will generate a garbage value,
185   // but should not enter an infinite loop.
186   absl::InsecureBitGen gen;
187   {
188     absl::uniform_real_distribution<TypeParam> dist(10.0, 1.0);
189     auto x = dist(gen);
190     EXPECT_FALSE(std::isnan(x)) << x;
191   }
192   {
193     absl::uniform_real_distribution<TypeParam> dist(
194         std::numeric_limits<TypeParam>::lowest(),
195         std::numeric_limits<TypeParam>::max());
196     auto x = dist(gen);
197     // Infinite result.
198     EXPECT_FALSE(std::isfinite(x)) << x;
199   }
200 #endif  // NDEBUG
201 }
202 #ifdef _MSC_VER
203 #pragma warning(pop)  // warning(disable:4756)
204 #endif
205 
TYPED_TEST(UniformRealDistributionTest,TestMoments)206 TYPED_TEST(UniformRealDistributionTest, TestMoments) {
207   constexpr int kSize = 1000000;
208   std::vector<double> values(kSize);
209 
210   absl::InsecureBitGen rng;
211   absl::uniform_real_distribution<TypeParam> dist;
212   for (int i = 0; i < kSize; i++) {
213     values[i] = dist(rng);
214   }
215 
216   const auto moments =
217       absl::random_internal::ComputeDistributionMoments(values);
218   EXPECT_NEAR(0.5, moments.mean, 0.01);
219   EXPECT_NEAR(1 / 12.0, moments.variance, 0.015);
220   EXPECT_NEAR(0.0, moments.skewness, 0.02);
221   EXPECT_NEAR(9 / 5.0, moments.kurtosis, 0.015);
222 }
223 
TYPED_TEST(UniformRealDistributionTest,ChiSquaredTest50)224 TYPED_TEST(UniformRealDistributionTest, ChiSquaredTest50) {
225   using absl::random_internal::kChiSquared;
226   using param_type =
227       typename absl::uniform_real_distribution<TypeParam>::param_type;
228 
229   constexpr size_t kTrials = 100000;
230   constexpr int kBuckets = 50;
231   constexpr double kExpected =
232       static_cast<double>(kTrials) / static_cast<double>(kBuckets);
233 
234   // 1-in-100000 threshold, but remember, there are about 8 tests
235   // in this file. And the test could fail for other reasons.
236   // Empirically validated with --runs_per_test=10000.
237   const int kThreshold =
238       absl::random_internal::ChiSquareValue(kBuckets - 1, 0.999999);
239 
240   absl::InsecureBitGen rng;
241   for (const auto& param : {param_type(0, 1), param_type(5, 12),
242                             param_type(-5, 13), param_type(-5, -2)}) {
243     const double min_val = param.a();
244     const double max_val = param.b();
245     const double factor = kBuckets / (max_val - min_val);
246 
247     std::vector<int32_t> counts(kBuckets, 0);
248     absl::uniform_real_distribution<TypeParam> dist(param);
249     for (size_t i = 0; i < kTrials; i++) {
250       auto x = dist(rng);
251       auto bucket = static_cast<size_t>((x - min_val) * factor);
252       counts[bucket]++;
253     }
254 
255     double chi_square = absl::random_internal::ChiSquareWithExpected(
256         std::begin(counts), std::end(counts), kExpected);
257     if (chi_square > kThreshold) {
258       double p_value =
259           absl::random_internal::ChiSquarePValue(chi_square, kBuckets);
260 
261       // Chi-squared test failed. Output does not appear to be uniform.
262       std::string msg;
263       for (const auto& a : counts) {
264         absl::StrAppend(&msg, a, "\n");
265       }
266       absl::StrAppend(&msg, kChiSquared, " p-value ", p_value, "\n");
267       absl::StrAppend(&msg, "High ", kChiSquared, " value: ", chi_square, " > ",
268                       kThreshold);
269       ABSL_RAW_LOG(INFO, "%s", msg.c_str());
270       FAIL() << msg;
271     }
272   }
273 }
274 
TYPED_TEST(UniformRealDistributionTest,StabilityTest)275 TYPED_TEST(UniformRealDistributionTest, StabilityTest) {
276   // absl::uniform_real_distribution stability relies only on
277   // random_internal::RandU64ToDouble and random_internal::RandU64ToFloat.
278   absl::random_internal::sequence_urbg urbg(
279       {0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull, 0xC332DDEFBE6C5AA5ull,
280        0x6558218568AB9702ull, 0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull,
281        0xECDD4775619F1510ull, 0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull,
282        0xB5735C904C70A239ull, 0xD59E9E0BCBAADE14ull, 0xEECC86BC60622CA7ull});
283 
284   std::vector<int> output(12);
285 
286   absl::uniform_real_distribution<TypeParam> dist;
287   std::generate(std::begin(output), std::end(output), [&] {
288     return static_cast<int>(TypeParam(1000000) * dist(urbg));
289   });
290 
291   EXPECT_THAT(
292       output,  //
293       testing::ElementsAre(59, 999246, 762494, 395876, 167716, 82545, 925251,
294                            77341, 12527, 708791, 834451, 932808));
295 }
296 
TEST(UniformRealDistributionTest,AlgorithmBounds)297 TEST(UniformRealDistributionTest, AlgorithmBounds) {
298   absl::uniform_real_distribution<double> dist;
299 
300   {
301     // This returns the smallest value >0 from absl::uniform_real_distribution.
302     absl::random_internal::sequence_urbg urbg({0x0000000000000001ull});
303     double a = dist(urbg);
304     EXPECT_EQ(a, 5.42101086242752217004e-20);
305   }
306 
307   {
308     // This returns a value very near 0.5 from absl::uniform_real_distribution.
309     absl::random_internal::sequence_urbg urbg({0x7fffffffffffffefull});
310     double a = dist(urbg);
311     EXPECT_EQ(a, 0.499999999999999944489);
312   }
313   {
314     // This returns a value very near 0.5 from absl::uniform_real_distribution.
315     absl::random_internal::sequence_urbg urbg({0x8000000000000000ull});
316     double a = dist(urbg);
317     EXPECT_EQ(a, 0.5);
318   }
319 
320   {
321     // This returns the largest value <1 from absl::uniform_real_distribution.
322     absl::random_internal::sequence_urbg urbg({0xFFFFFFFFFFFFFFEFull});
323     double a = dist(urbg);
324     EXPECT_EQ(a, 0.999999999999999888978);
325   }
326   {
327     // This *ALSO* returns the largest value <1.
328     absl::random_internal::sequence_urbg urbg({0xFFFFFFFFFFFFFFFFull});
329     double a = dist(urbg);
330     EXPECT_EQ(a, 0.999999999999999888978);
331   }
332 }
333 
334 }  // namespace
335