-- 790f9061df340cd900e8da70e66c363f7af3c2eb by Abseil Team <absl-team@google.com>: Add support for rvalue reference to function types. PiperOrigin-RevId: 324508531 -- 51fe201dbb41a3ebc3d49ff65250b5f464279d43 by Abseil Team <absl-team@google.com>: Cleaning up function comment style; no substantive change. PiperOrigin-RevId: 324497401 -- da8595d5266577d0c170528d12f6de17b8affcc2 by Abseil Team <absl-team@google.com>: Add support for demangling GNU vector types. PiperOrigin-RevId: 324494559 -- 0cb0acf88c1750f6963c9cb85249f9b4f0bd5104 by Abseil Team <absl-team@google.com>: Add support for thread-local types. PiperOrigin-RevId: 324491183 -- c676bc8380560599cd26f7f231e04e6be532e904 by Abseil Team <absl-team@google.com>: Add support for demangling "Du" (char8_t). PiperOrigin-RevId: 324441607 -- b218bf6467bc62b327214782c881e8224ad91509 by Abseil Team <absl-team@google.com>: Update doc comments in header of `any.h` to reflect that `absl::variant` has been released. PiperOrigin-RevId: 324431690 -- e5b579f3f1aa598c1f62e71dba7103b98811de59 by Laramie Leavitt <lar@google.com>: Bugfix: Fix bounds in absl::Uniform where one of the bounds is min/max. When absl::Uniform(rng, tag, a, b) is called, the tag is used in conjunction with the type to determine whether or not to manipulate the bounds to make them inclusive or exclusive through the uniform_*_bound functions. Unfortunately, at limits of the interval the function was not well behaved. The previous implementation used wrapping arithmetic. This causes incorrect bounds computation at the extremes (numeric_limits::min / numeric_limits::max) the bound would wrap. Improve this situation by: 1/ Changing the uniform_*_bound functions to use saturating arithmetic instead of wrapping, thus in the unsigned case, the upper_bound of IntervalOpenOpen for 0 is now 0, rather than numeric_limits::max, likewise for the lower bound. 2/ Adjusting the hi/lo checks in the distributions. When the interval is empty, such as for absl::Uniform(absl::IntervalOpenOpen, gen, 1, 0), the return value is somewhat nonsensical. Now absl::Uniform more consistently returns the low input rather than any adjusted input. In the above case, that means that 1 is returned rather than 2. NOTE: Calls to absl::Uniform where the resolved upper bound is < the lower bound are still ill-formed and should be avoided. 3/ Adding better tests. The underlying uniform_*_distribution classes are not affected. PiperOrigin-RevId: 324240873 GitOrigin-RevId: 790f9061df340cd900e8da70e66c363f7af3c2eb Change-Id: I2a2208650ea3135c575e200b868ce1d275069fc8pull/754/head
parent
184cf25241
commit
1995c6a3c2
9 changed files with 430 additions and 114 deletions
@ -0,0 +1,279 @@ |
||||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/random/internal/uniform_helper.h" |
||||
|
||||
#include <cmath> |
||||
#include <cstdint> |
||||
#include <random> |
||||
|
||||
#include "gtest/gtest.h" |
||||
|
||||
namespace { |
||||
|
||||
using absl::IntervalClosedClosedTag; |
||||
using absl::IntervalClosedOpenTag; |
||||
using absl::IntervalOpenClosedTag; |
||||
using absl::IntervalOpenOpenTag; |
||||
using absl::random_internal::uniform_inferred_return_t; |
||||
using absl::random_internal::uniform_lower_bound; |
||||
using absl::random_internal::uniform_upper_bound; |
||||
|
||||
class UniformHelperTest : public testing::Test {}; |
||||
|
||||
TEST_F(UniformHelperTest, UniformBoundFunctionsGeneral) { |
||||
constexpr IntervalClosedClosedTag IntervalClosedClosed; |
||||
constexpr IntervalClosedOpenTag IntervalClosedOpen; |
||||
constexpr IntervalOpenClosedTag IntervalOpenClosed; |
||||
constexpr IntervalOpenOpenTag IntervalOpenOpen; |
||||
|
||||
// absl::uniform_int_distribution natively assumes IntervalClosedClosed
|
||||
// absl::uniform_real_distribution natively assumes IntervalClosedOpen
|
||||
|
||||
EXPECT_EQ(uniform_lower_bound(IntervalOpenClosed, 0, 100), 1); |
||||
EXPECT_EQ(uniform_lower_bound(IntervalOpenOpen, 0, 100), 1); |
||||
EXPECT_GT(uniform_lower_bound<float>(IntervalOpenClosed, 0, 1.0), 0); |
||||
EXPECT_GT(uniform_lower_bound<float>(IntervalOpenOpen, 0, 1.0), 0); |
||||
EXPECT_GT(uniform_lower_bound<double>(IntervalOpenClosed, 0, 1.0), 0); |
||||
EXPECT_GT(uniform_lower_bound<double>(IntervalOpenOpen, 0, 1.0), 0); |
||||
|
||||
EXPECT_EQ(uniform_lower_bound(IntervalClosedClosed, 0, 100), 0); |
||||
EXPECT_EQ(uniform_lower_bound(IntervalClosedOpen, 0, 100), 0); |
||||
EXPECT_EQ(uniform_lower_bound<float>(IntervalClosedClosed, 0, 1.0), 0); |
||||
EXPECT_EQ(uniform_lower_bound<float>(IntervalClosedOpen, 0, 1.0), 0); |
||||
EXPECT_EQ(uniform_lower_bound<double>(IntervalClosedClosed, 0, 1.0), 0); |
||||
EXPECT_EQ(uniform_lower_bound<double>(IntervalClosedOpen, 0, 1.0), 0); |
||||
|
||||
EXPECT_EQ(uniform_upper_bound(IntervalOpenOpen, 0, 100), 99); |
||||
EXPECT_EQ(uniform_upper_bound(IntervalClosedOpen, 0, 100), 99); |
||||
EXPECT_EQ(uniform_upper_bound<float>(IntervalOpenOpen, 0, 1.0), 1.0); |
||||
EXPECT_EQ(uniform_upper_bound<float>(IntervalClosedOpen, 0, 1.0), 1.0); |
||||
EXPECT_EQ(uniform_upper_bound<double>(IntervalOpenOpen, 0, 1.0), 1.0); |
||||
EXPECT_EQ(uniform_upper_bound<double>(IntervalClosedOpen, 0, 1.0), 1.0); |
||||
|
||||
EXPECT_EQ(uniform_upper_bound(IntervalOpenClosed, 0, 100), 100); |
||||
EXPECT_EQ(uniform_upper_bound(IntervalClosedClosed, 0, 100), 100); |
||||
EXPECT_GT(uniform_upper_bound<float>(IntervalOpenClosed, 0, 1.0), 1.0); |
||||
EXPECT_GT(uniform_upper_bound<float>(IntervalClosedClosed, 0, 1.0), 1.0); |
||||
EXPECT_GT(uniform_upper_bound<double>(IntervalOpenClosed, 0, 1.0), 1.0); |
||||
EXPECT_GT(uniform_upper_bound<double>(IntervalClosedClosed, 0, 1.0), 1.0); |
||||
|
||||
// Negative value tests
|
||||
EXPECT_EQ(uniform_lower_bound(IntervalOpenClosed, -100, -1), -99); |
||||
EXPECT_EQ(uniform_lower_bound(IntervalOpenOpen, -100, -1), -99); |
||||
EXPECT_GT(uniform_lower_bound<float>(IntervalOpenClosed, -2.0, -1.0), -2.0); |
||||
EXPECT_GT(uniform_lower_bound<float>(IntervalOpenOpen, -2.0, -1.0), -2.0); |
||||
EXPECT_GT(uniform_lower_bound<double>(IntervalOpenClosed, -2.0, -1.0), -2.0); |
||||
EXPECT_GT(uniform_lower_bound<double>(IntervalOpenOpen, -2.0, -1.0), -2.0); |
||||
|
||||
EXPECT_EQ(uniform_lower_bound(IntervalClosedClosed, -100, -1), -100); |
||||
EXPECT_EQ(uniform_lower_bound(IntervalClosedOpen, -100, -1), -100); |
||||
EXPECT_EQ(uniform_lower_bound<float>(IntervalClosedClosed, -2.0, -1.0), -2.0); |
||||
EXPECT_EQ(uniform_lower_bound<float>(IntervalClosedOpen, -2.0, -1.0), -2.0); |
||||
EXPECT_EQ(uniform_lower_bound<double>(IntervalClosedClosed, -2.0, -1.0), |
||||
-2.0); |
||||
EXPECT_EQ(uniform_lower_bound<double>(IntervalClosedOpen, -2.0, -1.0), -2.0); |
||||
|
||||
EXPECT_EQ(uniform_upper_bound(IntervalOpenOpen, -100, -1), -2); |
||||
EXPECT_EQ(uniform_upper_bound(IntervalClosedOpen, -100, -1), -2); |
||||
EXPECT_EQ(uniform_upper_bound<float>(IntervalOpenOpen, -2.0, -1.0), -1.0); |
||||
EXPECT_EQ(uniform_upper_bound<float>(IntervalClosedOpen, -2.0, -1.0), -1.0); |
||||
EXPECT_EQ(uniform_upper_bound<double>(IntervalOpenOpen, -2.0, -1.0), -1.0); |
||||
EXPECT_EQ(uniform_upper_bound<double>(IntervalClosedOpen, -2.0, -1.0), -1.0); |
||||
|
||||
EXPECT_EQ(uniform_upper_bound(IntervalOpenClosed, -100, -1), -1); |
||||
EXPECT_EQ(uniform_upper_bound(IntervalClosedClosed, -100, -1), -1); |
||||
EXPECT_GT(uniform_upper_bound<float>(IntervalOpenClosed, -2.0, -1.0), -1.0); |
||||
EXPECT_GT(uniform_upper_bound<float>(IntervalClosedClosed, -2.0, -1.0), -1.0); |
||||
EXPECT_GT(uniform_upper_bound<double>(IntervalOpenClosed, -2.0, -1.0), -1.0); |
||||
EXPECT_GT(uniform_upper_bound<double>(IntervalClosedClosed, -2.0, -1.0), |
||||
-1.0); |
||||
|
||||
EXPECT_GT(uniform_lower_bound(IntervalOpenClosed, 1.0, 2.0), 1.0); |
||||
EXPECT_LT(uniform_lower_bound(IntervalOpenClosed, 1.0, +0.0), 1.0); |
||||
EXPECT_LT(uniform_lower_bound(IntervalOpenClosed, 1.0, -0.0), 1.0); |
||||
EXPECT_LT(uniform_lower_bound(IntervalOpenClosed, 1.0, -1.0), 1.0); |
||||
} |
||||
|
||||
TEST_F(UniformHelperTest, UniformBoundFunctionsIntBounds) { |
||||
// Verifies the saturating nature of uniform_lower_bound and
|
||||
// uniform_upper_bound
|
||||
constexpr IntervalOpenOpenTag IntervalOpenOpen; |
||||
|
||||
// uint max.
|
||||
constexpr auto m = (std::numeric_limits<uint64_t>::max)(); |
||||
|
||||
EXPECT_EQ(1, uniform_lower_bound(IntervalOpenOpen, 0u, 0u)); |
||||
EXPECT_EQ(m, uniform_lower_bound(IntervalOpenOpen, m, m)); |
||||
EXPECT_EQ(m, uniform_lower_bound(IntervalOpenOpen, m - 1, m - 1)); |
||||
EXPECT_EQ(0, uniform_upper_bound(IntervalOpenOpen, 0u, 0u)); |
||||
EXPECT_EQ(m - 1, uniform_upper_bound(IntervalOpenOpen, m, m)); |
||||
|
||||
// int min/max
|
||||
constexpr auto l = (std::numeric_limits<int64_t>::min)(); |
||||
constexpr auto r = (std::numeric_limits<int64_t>::max)(); |
||||
EXPECT_EQ(1, uniform_lower_bound(IntervalOpenOpen, 0, 0)); |
||||
EXPECT_EQ(l + 1, uniform_lower_bound(IntervalOpenOpen, l, l)); |
||||
EXPECT_EQ(r, uniform_lower_bound(IntervalOpenOpen, r - 1, r - 1)); |
||||
EXPECT_EQ(r, uniform_lower_bound(IntervalOpenOpen, r, r)); |
||||
EXPECT_EQ(-1, uniform_upper_bound(IntervalOpenOpen, 0, 0)); |
||||
EXPECT_EQ(l, uniform_upper_bound(IntervalOpenOpen, l, l)); |
||||
EXPECT_EQ(r - 1, uniform_upper_bound(IntervalOpenOpen, r, r)); |
||||
} |
||||
|
||||
TEST_F(UniformHelperTest, UniformBoundFunctionsRealBounds) { |
||||
// absl::uniform_real_distribution natively assumes IntervalClosedOpen;
|
||||
// use the inverse here so each bound has to change.
|
||||
constexpr IntervalOpenClosedTag IntervalOpenClosed; |
||||
|
||||
// Edge cases: the next value toward itself is itself.
|
||||
EXPECT_EQ(1.0, uniform_lower_bound(IntervalOpenClosed, 1.0, 1.0)); |
||||
EXPECT_EQ(1.0f, uniform_lower_bound(IntervalOpenClosed, 1.0f, 1.0f)); |
||||
|
||||
// rightmost and leftmost finite values.
|
||||
constexpr auto r = (std::numeric_limits<double>::max)(); |
||||
const auto re = std::nexttoward(r, 0.0); |
||||
constexpr auto l = -r; |
||||
const auto le = std::nexttoward(l, 0.0); |
||||
|
||||
EXPECT_EQ(l, uniform_lower_bound(IntervalOpenClosed, l, l)); // (l,l)
|
||||
EXPECT_EQ(r, uniform_lower_bound(IntervalOpenClosed, r, r)); // (r,r)
|
||||
EXPECT_EQ(le, uniform_lower_bound(IntervalOpenClosed, l, r)); // (l,r)
|
||||
EXPECT_EQ(le, uniform_lower_bound(IntervalOpenClosed, l, 0.0)); // (l, 0)
|
||||
EXPECT_EQ(le, uniform_lower_bound(IntervalOpenClosed, l, le)); // (l, le)
|
||||
EXPECT_EQ(r, uniform_lower_bound(IntervalOpenClosed, re, r)); // (re, r)
|
||||
|
||||
EXPECT_EQ(le, uniform_upper_bound(IntervalOpenClosed, l, l)); // (l,l)
|
||||
EXPECT_EQ(r, uniform_upper_bound(IntervalOpenClosed, r, r)); // (r,r)
|
||||
EXPECT_EQ(r, uniform_upper_bound(IntervalOpenClosed, l, r)); // (l,r)
|
||||
EXPECT_EQ(r, uniform_upper_bound(IntervalOpenClosed, l, re)); // (l,re)
|
||||
EXPECT_EQ(r, uniform_upper_bound(IntervalOpenClosed, 0.0, r)); // (0, r)
|
||||
EXPECT_EQ(r, uniform_upper_bound(IntervalOpenClosed, re, r)); // (re, r)
|
||||
EXPECT_EQ(r, uniform_upper_bound(IntervalOpenClosed, le, re)); // (le, re)
|
||||
|
||||
const double e = std::nextafter(1.0, 2.0); // 1 + epsilon
|
||||
const double f = std::nextafter(1.0, 0.0); // 1 - epsilon
|
||||
|
||||
// (1.0, 1.0 + epsilon)
|
||||
EXPECT_EQ(e, uniform_lower_bound(IntervalOpenClosed, 1.0, e)); |
||||
EXPECT_EQ(std::nextafter(e, 2.0), |
||||
uniform_upper_bound(IntervalOpenClosed, 1.0, e)); |
||||
|
||||
// (1.0-epsilon, 1.0)
|
||||
EXPECT_EQ(1.0, uniform_lower_bound(IntervalOpenClosed, f, 1.0)); |
||||
EXPECT_EQ(e, uniform_upper_bound(IntervalOpenClosed, f, 1.0)); |
||||
|
||||
// denorm cases.
|
||||
const double g = std::numeric_limits<double>::denorm_min(); |
||||
const double h = std::nextafter(g, 1.0); |
||||
|
||||
// (0, denorm_min)
|
||||
EXPECT_EQ(g, uniform_lower_bound(IntervalOpenClosed, 0.0, g)); |
||||
EXPECT_EQ(h, uniform_upper_bound(IntervalOpenClosed, 0.0, g)); |
||||
|
||||
// (denorm_min, 1.0)
|
||||
EXPECT_EQ(h, uniform_lower_bound(IntervalOpenClosed, g, 1.0)); |
||||
EXPECT_EQ(e, uniform_upper_bound(IntervalOpenClosed, g, 1.0)); |
||||
|
||||
// Edge cases: invalid bounds.
|
||||
EXPECT_EQ(f, uniform_lower_bound(IntervalOpenClosed, 1.0, -1.0)); |
||||
} |
||||
|
||||
struct Invalid {}; |
||||
|
||||
template <typename A, typename B> |
||||
auto InferredUniformReturnT(int) -> uniform_inferred_return_t<A, B>; |
||||
|
||||
template <typename, typename> |
||||
Invalid InferredUniformReturnT(...); |
||||
|
||||
// Given types <A, B, Expect>, CheckArgsInferType() verifies that
|
||||
//
|
||||
// uniform_inferred_return_t<A, B> and
|
||||
// uniform_inferred_return_t<B, A>
|
||||
//
|
||||
// returns the type "Expect".
|
||||
//
|
||||
// This interface can also be used to assert that a given inferred return types
|
||||
// are invalid. Writing:
|
||||
//
|
||||
// CheckArgsInferType<float, int, Invalid>()
|
||||
//
|
||||
// will assert that this overload does not exist.
|
||||
template <typename A, typename B, typename Expect> |
||||
void CheckArgsInferType() { |
||||
static_assert( |
||||
absl::conjunction< |
||||
std::is_same<Expect, decltype(InferredUniformReturnT<A, B>(0))>, |
||||
std::is_same<Expect, |
||||
decltype(InferredUniformReturnT<B, A>(0))>>::value, |
||||
""); |
||||
} |
||||
|
||||
TEST_F(UniformHelperTest, UniformTypeInference) { |
||||
// Infers common types.
|
||||
CheckArgsInferType<uint16_t, uint16_t, uint16_t>(); |
||||
CheckArgsInferType<uint32_t, uint32_t, uint32_t>(); |
||||
CheckArgsInferType<uint64_t, uint64_t, uint64_t>(); |
||||
CheckArgsInferType<int16_t, int16_t, int16_t>(); |
||||
CheckArgsInferType<int32_t, int32_t, int32_t>(); |
||||
CheckArgsInferType<int64_t, int64_t, int64_t>(); |
||||
CheckArgsInferType<float, float, float>(); |
||||
CheckArgsInferType<double, double, double>(); |
||||
|
||||
// Properly promotes uint16_t.
|
||||
CheckArgsInferType<uint16_t, uint32_t, uint32_t>(); |
||||
CheckArgsInferType<uint16_t, uint64_t, uint64_t>(); |
||||
CheckArgsInferType<uint16_t, int32_t, int32_t>(); |
||||
CheckArgsInferType<uint16_t, int64_t, int64_t>(); |
||||
CheckArgsInferType<uint16_t, float, float>(); |
||||
CheckArgsInferType<uint16_t, double, double>(); |
||||
|
||||
// Properly promotes int16_t.
|
||||
CheckArgsInferType<int16_t, int32_t, int32_t>(); |
||||
CheckArgsInferType<int16_t, int64_t, int64_t>(); |
||||
CheckArgsInferType<int16_t, float, float>(); |
||||
CheckArgsInferType<int16_t, double, double>(); |
||||
|
||||
// Invalid (u)int16_t-pairings do not compile.
|
||||
// See "CheckArgsInferType" comments above, for how this is achieved.
|
||||
CheckArgsInferType<uint16_t, int16_t, Invalid>(); |
||||
CheckArgsInferType<int16_t, uint32_t, Invalid>(); |
||||
CheckArgsInferType<int16_t, uint64_t, Invalid>(); |
||||
|
||||
// Properly promotes uint32_t.
|
||||
CheckArgsInferType<uint32_t, uint64_t, uint64_t>(); |
||||
CheckArgsInferType<uint32_t, int64_t, int64_t>(); |
||||
CheckArgsInferType<uint32_t, double, double>(); |
||||
|
||||
// Properly promotes int32_t.
|
||||
CheckArgsInferType<int32_t, int64_t, int64_t>(); |
||||
CheckArgsInferType<int32_t, double, double>(); |
||||
|
||||
// Invalid (u)int32_t-pairings do not compile.
|
||||
CheckArgsInferType<uint32_t, int32_t, Invalid>(); |
||||
CheckArgsInferType<int32_t, uint64_t, Invalid>(); |
||||
CheckArgsInferType<int32_t, float, Invalid>(); |
||||
CheckArgsInferType<uint32_t, float, Invalid>(); |
||||
|
||||
// Invalid (u)int64_t-pairings do not compile.
|
||||
CheckArgsInferType<uint64_t, int64_t, Invalid>(); |
||||
CheckArgsInferType<int64_t, float, Invalid>(); |
||||
CheckArgsInferType<int64_t, double, Invalid>(); |
||||
|
||||
// Properly promotes float.
|
||||
CheckArgsInferType<float, double, double>(); |
||||
} |
||||
|
||||
} // namespace
|
Loading…
Reference in new issue