Skip to content

Commit 79a3361

Browse files
committed
Disable folding
1 parent fd78a3c commit 79a3361

5 files changed

Lines changed: 104 additions & 26 deletions

File tree

test/test_float_exp_log.cpp

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,22 +32,36 @@ import boost.safe_numbers;
3232

3333
using namespace boost::safe_numbers;
3434

35+
// Route the argument through a volatile so the reference std:: call is evaluated by the
36+
// runtime libm rather than constant-folded by the compiler. On 32-bit x86 (x87) some
37+
// libm transcendentals are not correctly rounded and differ from the compiler's folded
38+
// value by an ULP; forcing both the wrapper and the reference onto the same runtime call
39+
// keeps the bit-exact comparison valid.
40+
template <typename U>
41+
auto opaque(const U value) noexcept -> U
42+
{
43+
volatile U sink {value};
44+
return sink;
45+
}
46+
3547
template <typename T>
3648
void test_finite()
3749
{
3850
using basis_type = typename T::basis_type;
3951

40-
for (const auto x : {static_cast<basis_type>(0.0), static_cast<basis_type>(1.0),
41-
static_cast<basis_type>(2.5), static_cast<basis_type>(-1.0)})
52+
for (const auto raw : {static_cast<basis_type>(0.0), static_cast<basis_type>(1.0),
53+
static_cast<basis_type>(2.5), static_cast<basis_type>(-1.0)})
4254
{
55+
const auto x {opaque(raw)};
4356
BOOST_TEST(exp(T{x}) == T{std::exp(x)});
4457
BOOST_TEST(exp2(T{x}) == T{std::exp2(x)});
4558
BOOST_TEST(expm1(T{x}) == T{std::expm1(x)});
4659
}
4760

48-
for (const auto x : {static_cast<basis_type>(0.5), static_cast<basis_type>(1.0),
49-
static_cast<basis_type>(2.0), static_cast<basis_type>(100.0)})
61+
for (const auto raw : {static_cast<basis_type>(0.5), static_cast<basis_type>(1.0),
62+
static_cast<basis_type>(2.0), static_cast<basis_type>(100.0)})
5063
{
64+
const auto x {opaque(raw)};
5165
BOOST_TEST(log(T{x}) == T{std::log(x)});
5266
BOOST_TEST(log2(T{x}) == T{std::log2(x)});
5367
BOOST_TEST(log10(T{x}) == T{std::log10(x)});

test/test_float_hyperbolic.cpp

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,30 +32,45 @@ import boost.safe_numbers;
3232

3333
using namespace boost::safe_numbers;
3434

35+
// Route the argument through a volatile so the reference std:: call is evaluated by the
36+
// runtime libm rather than constant-folded by the compiler. On 32-bit x86 (x87) some
37+
// libm transcendentals are not correctly rounded and differ from the compiler's folded
38+
// value by an ULP; forcing both the wrapper and the reference onto the same runtime call
39+
// keeps the bit-exact comparison valid.
40+
template <typename U>
41+
auto opaque(const U value) noexcept -> U
42+
{
43+
volatile U sink {value};
44+
return sink;
45+
}
46+
3547
template <typename T>
3648
void test_finite()
3749
{
3850
using basis_type = typename T::basis_type;
3951

40-
for (const auto x : {static_cast<basis_type>(0.0), static_cast<basis_type>(0.5),
41-
static_cast<basis_type>(-0.5), static_cast<basis_type>(1.0)})
52+
for (const auto raw : {static_cast<basis_type>(0.0), static_cast<basis_type>(0.5),
53+
static_cast<basis_type>(-0.5), static_cast<basis_type>(1.0)})
4254
{
55+
const auto x {opaque(raw)};
4356
BOOST_TEST(sinh(T{x}) == T{std::sinh(x)});
4457
BOOST_TEST(cosh(T{x}) == T{std::cosh(x)});
4558
BOOST_TEST(tanh(T{x}) == T{std::tanh(x)});
4659
BOOST_TEST(asinh(T{x}) == T{std::asinh(x)});
4760
}
4861

4962
// atanh domain is the open interval (-1, 1)
50-
for (const auto x : {static_cast<basis_type>(0.0), static_cast<basis_type>(0.5),
51-
static_cast<basis_type>(-0.5), static_cast<basis_type>(0.9)})
63+
for (const auto raw : {static_cast<basis_type>(0.0), static_cast<basis_type>(0.5),
64+
static_cast<basis_type>(-0.5), static_cast<basis_type>(0.9)})
5265
{
66+
const auto x {opaque(raw)};
5367
BOOST_TEST(atanh(T{x}) == T{std::atanh(x)});
5468
}
5569

5670
// acosh domain is [1, inf)
57-
for (const auto x : {static_cast<basis_type>(1.0), static_cast<basis_type>(2.0)})
71+
for (const auto raw : {static_cast<basis_type>(1.0), static_cast<basis_type>(2.0)})
5872
{
73+
const auto x {opaque(raw)};
5974
BOOST_TEST(acosh(T{x}) == T{std::acosh(x)});
6075
}
6176
}

test/test_float_power.cpp

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,22 +32,41 @@ import boost.safe_numbers;
3232

3333
using namespace boost::safe_numbers;
3434

35+
// Route the argument through a volatile so the reference std:: call is evaluated by the
36+
// runtime libm rather than constant-folded by the compiler. On 32-bit x86 (x87) some
37+
// libm transcendentals are not correctly rounded and differ from the compiler's folded
38+
// value by an ULP; forcing both the wrapper and the reference onto the same runtime call
39+
// keeps the bit-exact comparison valid.
40+
template <typename U>
41+
auto opaque(const U value) noexcept -> U
42+
{
43+
volatile U sink {value};
44+
return sink;
45+
}
46+
3547
template <typename T>
3648
void test_finite()
3749
{
3850
using basis_type = typename T::basis_type;
3951

52+
// sqrt is correctly rounded everywhere, so an exact integral result is portable
4053
BOOST_TEST(sqrt(T{static_cast<basis_type>(4.0)}) == T{static_cast<basis_type>(2.0)});
4154
BOOST_TEST(sqrt(T{static_cast<basis_type>(0.0)}) == T{static_cast<basis_type>(0.0)});
42-
BOOST_TEST(cbrt(T{static_cast<basis_type>(27.0)}) == T{std::cbrt(static_cast<basis_type>(27.0))});
55+
56+
const auto twenty_seven {opaque(static_cast<basis_type>(27.0))};
57+
BOOST_TEST(cbrt(T{twenty_seven}) == T{std::cbrt(twenty_seven)});
4358
// cbrt of a negative value is well defined
44-
BOOST_TEST(cbrt(T{static_cast<basis_type>(-8.0)}) == T{std::cbrt(static_cast<basis_type>(-8.0))});
59+
const auto neg_eight {opaque(static_cast<basis_type>(-8.0))};
60+
BOOST_TEST(cbrt(T{neg_eight}) == T{std::cbrt(neg_eight)});
4561

46-
BOOST_TEST(pow(T{static_cast<basis_type>(2.0)}, T{static_cast<basis_type>(10.0)})
47-
== T{std::pow(static_cast<basis_type>(2.0), static_cast<basis_type>(10.0))});
48-
BOOST_TEST(pow(T{static_cast<basis_type>(9.0)}, T{static_cast<basis_type>(0.5)})
49-
== T{std::pow(static_cast<basis_type>(9.0), static_cast<basis_type>(0.5))});
62+
const auto two {opaque(static_cast<basis_type>(2.0))};
63+
const auto ten {opaque(static_cast<basis_type>(10.0))};
64+
BOOST_TEST(pow(T{two}, T{ten}) == T{std::pow(two, ten)});
65+
const auto nine {opaque(static_cast<basis_type>(9.0))};
66+
const auto half {opaque(static_cast<basis_type>(0.5))};
67+
BOOST_TEST(pow(T{nine}, T{half}) == T{std::pow(nine, half)});
5068

69+
// hypot(3, 4) == 5 exactly
5170
BOOST_TEST(hypot(T{static_cast<basis_type>(3.0)}, T{static_cast<basis_type>(4.0)})
5271
== T{static_cast<basis_type>(5.0)});
5372
}

test/test_float_special.cpp

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,22 +33,37 @@ import boost.safe_numbers;
3333

3434
using namespace boost::safe_numbers;
3535

36+
// Route the argument through a volatile so the reference std:: call is evaluated by the
37+
// runtime libm rather than constant-folded by the compiler. On 32-bit x86 (x87) some
38+
// libm transcendentals are not correctly rounded and differ from the compiler's folded
39+
// value by an ULP; forcing both the wrapper and the reference onto the same runtime call
40+
// keeps the bit-exact comparison valid.
41+
template <typename U>
42+
auto opaque(const U value) noexcept -> U
43+
{
44+
volatile U sink {value};
45+
return sink;
46+
}
47+
3648
template <typename T>
3749
void test_finite()
3850
{
3951
using basis_type = typename T::basis_type;
4052

41-
for (const auto x : {static_cast<basis_type>(0.0), static_cast<basis_type>(0.5),
42-
static_cast<basis_type>(-0.5), static_cast<basis_type>(1.0)})
53+
for (const auto raw : {static_cast<basis_type>(0.0), static_cast<basis_type>(0.5),
54+
static_cast<basis_type>(-0.5), static_cast<basis_type>(1.0)})
4355
{
56+
const auto x {opaque(raw)};
4457
BOOST_TEST(erf(T{x}) == T{std::erf(x)});
4558
BOOST_TEST(erfc(T{x}) == T{std::erfc(x)});
4659
}
4760

4861
// tgamma(5) = 4! = 24, lgamma of a positive value is finite
49-
BOOST_TEST(tgamma(T{static_cast<basis_type>(5.0)}) == T{std::tgamma(static_cast<basis_type>(5.0))});
50-
BOOST_TEST(lgamma(T{static_cast<basis_type>(5.0)}) == T{std::lgamma(static_cast<basis_type>(5.0))});
51-
BOOST_TEST(tgamma(T{static_cast<basis_type>(0.5)}) == T{std::tgamma(static_cast<basis_type>(0.5))});
62+
const auto five {opaque(static_cast<basis_type>(5.0))};
63+
BOOST_TEST(tgamma(T{five}) == T{std::tgamma(five)});
64+
BOOST_TEST(lgamma(T{five}) == T{std::lgamma(five)});
65+
const auto half {opaque(static_cast<basis_type>(0.5))};
66+
BOOST_TEST(tgamma(T{half}) == T{std::tgamma(half)});
5267
}
5368

5469
// tgamma at a negative integer is a pole. Most standard libraries report it as a

test/test_float_trig.cpp

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,29 @@ import boost.safe_numbers;
3232

3333
using namespace boost::safe_numbers;
3434

35+
// Route the argument through a volatile so the reference std:: call is evaluated by the
36+
// runtime libm rather than constant-folded by the compiler. On 32-bit x86 (x87) some
37+
// libm transcendentals are not correctly rounded and differ from the compiler's folded
38+
// value by an ULP; forcing both the wrapper and the reference onto the same runtime call
39+
// keeps the bit-exact comparison valid.
40+
template <typename U>
41+
auto opaque(const U value) noexcept -> U
42+
{
43+
volatile U sink {value};
44+
return sink;
45+
}
46+
3547
// Finite inputs: the wrapper delegates to the same std function on the same basis
3648
// value, so the result is bit-identical and can be compared with exact equality.
3749
template <typename T>
3850
void test_finite()
3951
{
4052
using basis_type = typename T::basis_type;
4153

42-
for (const auto x : {static_cast<basis_type>(0.0), static_cast<basis_type>(0.5),
43-
static_cast<basis_type>(-0.5), static_cast<basis_type>(1.0)})
54+
for (const auto raw : {static_cast<basis_type>(0.0), static_cast<basis_type>(0.5),
55+
static_cast<basis_type>(-0.5), static_cast<basis_type>(1.0)})
4456
{
57+
const auto x {opaque(raw)};
4558
BOOST_TEST(sin(T{x}) == T{std::sin(x)});
4659
BOOST_TEST(cos(T{x}) == T{std::cos(x)});
4760
BOOST_TEST(tan(T{x}) == T{std::tan(x)});
@@ -50,10 +63,12 @@ void test_finite()
5063
BOOST_TEST(acos(T{x}) == T{std::acos(x)});
5164
}
5265

53-
BOOST_TEST(atan2(T{static_cast<basis_type>(1.0)}, T{static_cast<basis_type>(1.0)})
54-
== T{std::atan2(static_cast<basis_type>(1.0), static_cast<basis_type>(1.0))});
55-
BOOST_TEST(atan2(T{static_cast<basis_type>(-1.0)}, T{static_cast<basis_type>(2.0)})
56-
== T{std::atan2(static_cast<basis_type>(-1.0), static_cast<basis_type>(2.0))});
66+
const auto a {opaque(static_cast<basis_type>(1.0))};
67+
const auto b {opaque(static_cast<basis_type>(1.0))};
68+
BOOST_TEST(atan2(T{a}, T{b}) == T{std::atan2(a, b)});
69+
const auto c {opaque(static_cast<basis_type>(-1.0))};
70+
const auto d {opaque(static_cast<basis_type>(2.0))};
71+
BOOST_TEST(atan2(T{c}, T{d}) == T{std::atan2(c, d)});
5772
}
5873

5974
// asin/acos outside [-1, 1] produce NAN -> domain_error

0 commit comments

Comments
 (0)