// 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/iostream_state_saver.h" #include #include #include "gtest/gtest.h" namespace { using absl::random_internal::make_istream_state_saver; using absl::random_internal::make_ostream_state_saver; using absl::random_internal::stream_precision_helper; template typename absl::enable_if_t::value, T> // StreamRoundTrip(T t) { std::stringstream ss; { auto saver = make_ostream_state_saver(ss); ss.precision(stream_precision_helper::kPrecision); ss << t; } T result = 0; { auto saver = make_istream_state_saver(ss); ss >> result; } EXPECT_FALSE(ss.fail()) // << ss.str() << " " // << (ss.good() ? "good " : "") // << (ss.bad() ? "bad " : "") // << (ss.eof() ? "eof " : "") // << (ss.fail() ? "fail " : ""); return result; } template typename absl::enable_if_t::value, T> // StreamRoundTrip(T t) { std::stringstream ss; { auto saver = make_ostream_state_saver(ss); ss.precision(stream_precision_helper::kPrecision); ss << t; } T result = 0; { auto saver = make_istream_state_saver(ss); result = absl::random_internal::read_floating_point(ss); } EXPECT_FALSE(ss.fail()) // << ss.str() << " " // << (ss.good() ? "good " : "") // << (ss.bad() ? "bad " : "") // << (ss.eof() ? "eof " : "") // << (ss.fail() ? "fail " : ""); return result; } TEST(IOStreamStateSaver, BasicSaverState) { std::stringstream ss; ss.precision(2); ss.fill('x'); ss.flags(std::ios_base::dec | std::ios_base::right); { auto saver = make_ostream_state_saver(ss); ss.precision(10); EXPECT_NE('x', ss.fill()); EXPECT_EQ(10, ss.precision()); EXPECT_NE(std::ios_base::dec | std::ios_base::right, ss.flags()); ss << 1.23; } EXPECT_EQ('x', ss.fill()); EXPECT_EQ(2, ss.precision()); EXPECT_EQ(std::ios_base::dec | std::ios_base::right, ss.flags()); } TEST(IOStreamStateSaver, RoundTripInts) { const uint64_t kUintValues[] = { 0, 1, static_cast(-1), 2, static_cast(-2), 1 << 7, 1 << 8, 1 << 16, 1ull << 32, 1ull << 50, 1ull << 62, 1ull << 63, (1 << 7) - 1, (1 << 8) - 1, (1 << 16) - 1, (1ull << 32) - 1, (1ull << 50) - 1, (1ull << 62) - 1, (1ull << 63) - 1, static_cast(-(1 << 8)), static_cast(-(1 << 16)), static_cast(-(1ll << 32)), static_cast(-(1ll << 50)), static_cast(-(1ll << 62)), static_cast(-(1 << 8) - 1), static_cast(-(1 << 16) - 1), static_cast(-(1ll << 32) - 1), static_cast(-(1ll << 50) - 1), static_cast(-(1ll << 62) - 1), }; for (const uint64_t u : kUintValues) { EXPECT_EQ(u, StreamRoundTrip(u)); int64_t x = static_cast(u); EXPECT_EQ(x, StreamRoundTrip(x)); double d = static_cast(x); EXPECT_EQ(d, StreamRoundTrip(d)); float f = d; EXPECT_EQ(f, StreamRoundTrip(f)); } } TEST(IOStreamStateSaver, RoundTripFloats) { static_assert( stream_precision_helper::kPrecision >= 9, "stream_precision_helper::kPrecision should be at least 9"); const float kValues[] = { 1, std::nextafter(1.0f, 0.0f), // 1 - epsilon std::nextafter(1.0f, 2.0f), // 1 + epsilon 1.0e+1f, 1.0e-1f, 1.0e+2f, 1.0e-2f, 1.0e+10f, 1.0e-10f, 0.00000051110000111311111111f, -0.00000051110000111211111111f, 1.234678912345678912345e+6f, 1.234678912345678912345e-6f, 1.234678912345678912345e+30f, 1.234678912345678912345e-30f, 1.234678912345678912345e+38f, 1.0234678912345678912345e-38f, // Boundary cases. std::numeric_limits::max(), std::numeric_limits::lowest(), std::numeric_limits::epsilon(), std::nextafter(std::numeric_limits::min(), 1.0f), // min + epsilon std::numeric_limits::min(), // smallest normal // There are some errors dealing with denorms on apple platforms. std::numeric_limits::denorm_min(), // smallest denorm std::numeric_limits::min() / 2, std::nextafter(std::numeric_limits::min(), 0.0f), // denorm_max std::nextafter(std::numeric_limits::denorm_min(), 1.0f), }; for (const float f : kValues) { EXPECT_EQ(f, StreamRoundTrip(f)); EXPECT_EQ(-f, StreamRoundTrip(-f)); double d = f; EXPECT_EQ(d, StreamRoundTrip(d)); EXPECT_EQ(-d, StreamRoundTrip(-d)); // Avoid undefined behavior (overflow/underflow). if (d <= std::numeric_limits::max() && d >= std::numeric_limits::lowest()) { int64_t x = static_cast(f); EXPECT_EQ(x, StreamRoundTrip(x)); } } } TEST(IOStreamStateSaver, RoundTripDoubles) { static_assert( stream_precision_helper::kPrecision >= 17, "stream_precision_helper::kPrecision should be at least 17"); const double kValues[] = { 1, std::nextafter(1.0, 0.0), // 1 - epsilon std::nextafter(1.0, 2.0), // 1 + epsilon 1.0e+1, 1.0e-1, 1.0e+2, 1.0e-2, 1.0e+10, 1.0e-10, 0.00000051110000111311111111, -0.00000051110000111211111111, 1.234678912345678912345e+6, 1.234678912345678912345e-6, 1.234678912345678912345e+30, 1.234678912345678912345e-30, 1.234678912345678912345e+38, 1.0234678912345678912345e-38, 1.0e+100, 1.0e-100, 1.234678912345678912345e+308, 1.0234678912345678912345e-308, 2.22507385850720138e-308, // Boundary cases. std::numeric_limits::max(), std::numeric_limits::lowest(), std::numeric_limits::epsilon(), std::nextafter(std::numeric_limits::min(), 1.0), // min + epsilon std::numeric_limits::min(), // smallest normal // There are some errors dealing with denorms on apple platforms. std::numeric_limits::denorm_min(), // smallest denorm std::numeric_limits::min() / 2, std::nextafter(std::numeric_limits::min(), 0.0), // denorm_max std::nextafter(std::numeric_limits::denorm_min(), 1.0f), }; for (const double d : kValues) { EXPECT_EQ(d, StreamRoundTrip(d)); EXPECT_EQ(-d, StreamRoundTrip(-d)); // Avoid undefined behavior (overflow/underflow). if (d <= std::numeric_limits::max() && d >= std::numeric_limits::lowest()) { float f = static_cast(d); EXPECT_EQ(f, StreamRoundTrip(f)); } // Avoid undefined behavior (overflow/underflow). if (d <= std::numeric_limits::max() && d >= std::numeric_limits::lowest()) { int64_t x = static_cast(d); EXPECT_EQ(x, StreamRoundTrip(x)); } } } TEST(IOStreamStateSaver, RoundTripLongDoubles) { // Technically, C++ only guarantees that long double is at least as large as a // double. Practically it varies from 64-bits to 128-bits. // // So it is best to consider long double a best-effort extended precision // type. static_assert( stream_precision_helper::kPrecision >= 36, "stream_precision_helper::kPrecision should be at least 36"); using real_type = long double; const real_type kValues[] = { 1, std::nextafter(1.0, 0.0), // 1 - epsilon std::nextafter(1.0, 2.0), // 1 + epsilon 1.0e+1, 1.0e-1, 1.0e+2, 1.0e-2, 1.0e+10, 1.0e-10, 0.00000051110000111311111111, -0.00000051110000111211111111, 1.2346789123456789123456789123456789e+6, 1.2346789123456789123456789123456789e-6, 1.2346789123456789123456789123456789e+30, 1.2346789123456789123456789123456789e-30, 1.2346789123456789123456789123456789e+38, 1.2346789123456789123456789123456789e-38, 1.2346789123456789123456789123456789e+308, 1.2346789123456789123456789123456789e-308, 1.0e+100, 1.0e-100, 1.234678912345678912345e+308, 1.0234678912345678912345e-308, // Boundary cases. std::numeric_limits::max(), std::numeric_limits::lowest(), std::numeric_limits::epsilon(), std::nextafter(std::numeric_limits::min(), real_type(1)), // min + epsilon std::numeric_limits::min(), // smallest normal // There are some errors dealing with denorms on apple platforms. std::numeric_limits::denorm_min(), // smallest denorm std::numeric_limits::min() / 2, std::nextafter(std::numeric_limits::min(), 0.0), // denorm_max std::nextafter(std::numeric_limits::denorm_min(), 1.0f), }; int index = -1; for (const long double dd : kValues) { index++; EXPECT_EQ(dd, StreamRoundTrip(dd)) << index; EXPECT_EQ(-dd, StreamRoundTrip(-dd)) << index; // Avoid undefined behavior (overflow/underflow). if (dd <= std::numeric_limits::max() && dd >= std::numeric_limits::lowest()) { double d = static_cast(dd); EXPECT_EQ(d, StreamRoundTrip(d)); } // Avoid undefined behavior (overflow/underflow). if (dd <= std::numeric_limits::max() && dd >= std::numeric_limits::lowest()) { int64_t x = static_cast(dd); EXPECT_EQ(x, StreamRoundTrip(x)); } } } TEST(StrToDTest, DoubleMin) { const char kV[] = "2.22507385850720138e-308"; char* end; double x = std::strtod(kV, &end); EXPECT_EQ(std::numeric_limits::min(), x); // errno may equal ERANGE. } TEST(StrToDTest, DoubleDenormMin) { const char kV[] = "4.94065645841246544e-324"; char* end; double x = std::strtod(kV, &end); EXPECT_EQ(std::numeric_limits::denorm_min(), x); // errno may equal ERANGE. } } // namespace