Skip to content

Commit 6402189

Browse files
committed
Add testing of all new functions
1 parent a1b6621 commit 6402189

12 files changed

Lines changed: 1369 additions & 0 deletions

test/Jamfile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,17 @@ run test_float_std_format.cpp ;
6262
run test_float_fmt_format.cpp ;
6363
run test_float_limits.cpp ;
6464
run test_float_literals.cpp ;
65+
run test_float_trig.cpp ;
66+
run test_float_hyperbolic.cpp ;
67+
run test_float_exp_log.cpp ;
68+
run test_float_power.cpp ;
69+
run test_float_special.cpp ;
70+
run test_float_rounding.cpp ;
71+
run test_float_manip.cpp ;
72+
run test_float_classification.cpp ;
73+
run test_float_int_rounding.cpp ;
74+
run test_float_charconv.cpp : : : <library>/boost/charconv//boost_charconv ;
75+
run test_hash.cpp ;
6576
compile-fail compile_fail_float_mixed_ops.cpp ;
6677
compile compile_integer_type_properties.cpp ;
6778
run test_unsigned_narrowing_conversions.cpp ;

test/test_float_charconv.cpp

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
// Copyright 2026 Matt Borland
2+
// Distributed under the Boost Software License, Version 1.0.
3+
// https://www.boost.org/LICENSE_1_0.txt
4+
5+
#include <boost/core/lightweight_test.hpp>
6+
7+
#ifdef BOOST_SAFE_NUMBERS_BUILD_MODULE
8+
9+
import boost.safe_numbers;
10+
11+
#else
12+
13+
#include <boost/safe_numbers/floats.hpp>
14+
#include <boost/safe_numbers/charconv.hpp>
15+
#include <random>
16+
#include <limits>
17+
18+
#endif
19+
20+
using namespace boost::safe_numbers;
21+
using boost::charconv::to_chars;
22+
using boost::charconv::from_chars;
23+
using boost::charconv::chars_format;
24+
25+
static std::mt19937_64 rng {42};
26+
27+
// general and scientific produce a round-trippable shortest representation, so the
28+
// recovered value must compare exactly equal to the original. (hex is intentionally
29+
// excluded here: boost::charconv's own hex to_chars/from_chars do not round-trip a
30+
// raw double, which is a charconv-level concern, not a property of this wrapper.)
31+
template <typename T>
32+
void roundtrip(const T value, const chars_format fmt)
33+
{
34+
char buffer[256];
35+
const auto r_to {to_chars(buffer, buffer + sizeof(buffer), value, fmt)};
36+
BOOST_TEST(r_to.ec == std::errc{});
37+
38+
T back {};
39+
const auto r_from {from_chars(buffer, r_to.ptr, back, fmt)};
40+
BOOST_TEST(r_from.ec == std::errc{});
41+
BOOST_TEST(back == value);
42+
}
43+
44+
template <typename T>
45+
void test_roundtrip()
46+
{
47+
using basis_type = typename T::basis_type;
48+
49+
for (const auto fmt : {chars_format::general, chars_format::scientific})
50+
{
51+
// Representative finite values
52+
for (const auto v : {static_cast<basis_type>(0.0), static_cast<basis_type>(1.0),
53+
static_cast<basis_type>(-1.0), static_cast<basis_type>(3.141592653589793),
54+
static_cast<basis_type>(-2.5e10), static_cast<basis_type>(1.25e-7)})
55+
{
56+
roundtrip(T{v}, fmt);
57+
}
58+
59+
roundtrip(T{std::numeric_limits<basis_type>::max()}, fmt);
60+
roundtrip(T{std::numeric_limits<basis_type>::lowest()}, fmt);
61+
roundtrip(T{std::numeric_limits<basis_type>::min()}, fmt);
62+
63+
// Random finite values
64+
std::uniform_real_distribution<basis_type> dist {static_cast<basis_type>(-1.0e6),
65+
static_cast<basis_type>(1.0e6)};
66+
for (int i {}; i < 512; ++i)
67+
{
68+
roundtrip(T{dist(rng)}, fmt);
69+
}
70+
}
71+
}
72+
73+
// The default overloads (general format) must be usable without naming a format.
74+
template <typename T>
75+
void test_default_format()
76+
{
77+
using basis_type = typename T::basis_type;
78+
79+
char buffer[256];
80+
const T value {static_cast<basis_type>(42.5)};
81+
82+
const auto r_to {to_chars(buffer, buffer + sizeof(buffer), value)};
83+
BOOST_TEST(r_to.ec == std::errc{});
84+
85+
T back {};
86+
const auto r_from {from_chars(buffer, r_to.ptr, back)};
87+
BOOST_TEST(r_from.ec == std::errc{});
88+
BOOST_TEST(back == value);
89+
}
90+
91+
// The (format, precision) overload must compile and write without error.
92+
template <typename T>
93+
void test_precision()
94+
{
95+
using basis_type = typename T::basis_type;
96+
97+
char buffer[256];
98+
const T value {static_cast<basis_type>(3.141592653589793)};
99+
100+
const auto r_to {to_chars(buffer, buffer + sizeof(buffer), value, chars_format::fixed, 4)};
101+
BOOST_TEST(r_to.ec == std::errc{});
102+
}
103+
104+
// The hex format path of the wrapper must at least emit without error.
105+
template <typename T>
106+
void test_hex_emit()
107+
{
108+
using basis_type = typename T::basis_type;
109+
110+
char buffer[256];
111+
const auto r {to_chars(buffer, buffer + sizeof(buffer), T{static_cast<basis_type>(1.5)}, chars_format::hex)};
112+
BOOST_TEST(r.ec == std::errc{});
113+
BOOST_TEST(r.ptr != buffer);
114+
}
115+
116+
// Infinity round-trips and compares equal (NAN would not, by definition).
117+
template <typename T>
118+
void test_infinity()
119+
{
120+
using basis_type = typename T::basis_type;
121+
122+
const T pos_inf {std::numeric_limits<basis_type>::infinity()};
123+
roundtrip(pos_inf, chars_format::general);
124+
125+
const T neg_inf {-std::numeric_limits<basis_type>::infinity()};
126+
roundtrip(neg_inf, chars_format::general);
127+
}
128+
129+
int main()
130+
{
131+
test_roundtrip<f32>();
132+
test_roundtrip<f64>();
133+
134+
test_default_format<f32>();
135+
test_default_format<f64>();
136+
137+
test_precision<f32>();
138+
test_precision<f64>();
139+
140+
test_hex_emit<f32>();
141+
test_hex_emit<f64>();
142+
143+
test_infinity<f32>();
144+
test_infinity<f64>();
145+
146+
return boost::report_errors();
147+
}

test/test_float_classification.cpp

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
// Copyright 2026 Matt Borland
2+
// Distributed under the Boost Software License, Version 1.0.
3+
// https://www.boost.org/LICENSE_1_0.txt
4+
5+
#include <boost/core/lightweight_test.hpp>
6+
7+
#ifdef BOOST_SAFE_NUMBERS_BUILD_MODULE
8+
9+
import boost.safe_numbers;
10+
11+
#else
12+
13+
#include <boost/safe_numbers/cmath.hpp>
14+
15+
#endif
16+
17+
#include <bit>
18+
#include <cmath>
19+
#include <cstdint>
20+
#include <limits>
21+
#include <type_traits>
22+
23+
using namespace boost::safe_numbers;
24+
25+
// The classification predicates never throw and must agree with the standard
26+
// library for every category of value, including the special ones.
27+
template <typename T>
28+
void test_predicates()
29+
{
30+
using basis_type = typename T::basis_type;
31+
32+
const T zero {static_cast<basis_type>(0.0)};
33+
const T neg_zero {static_cast<basis_type>(-0.0)};
34+
const T one {static_cast<basis_type>(1.0)};
35+
const T neg_one {static_cast<basis_type>(-1.0)};
36+
const T pos_inf {std::numeric_limits<basis_type>::infinity()};
37+
const T neg_inf {-std::numeric_limits<basis_type>::infinity()};
38+
const T qnan {std::numeric_limits<basis_type>::quiet_NaN()};
39+
const T tiny {std::numeric_limits<basis_type>::denorm_min()};
40+
41+
// isnan
42+
BOOST_TEST(isnan(qnan));
43+
BOOST_TEST(!isnan(one));
44+
BOOST_TEST(!isnan(pos_inf));
45+
46+
// isinf
47+
BOOST_TEST(isinf(pos_inf));
48+
BOOST_TEST(isinf(neg_inf));
49+
BOOST_TEST(!isinf(one));
50+
BOOST_TEST(!isinf(qnan));
51+
52+
// isfinite
53+
BOOST_TEST(isfinite(one));
54+
BOOST_TEST(isfinite(zero));
55+
BOOST_TEST(!isfinite(pos_inf));
56+
BOOST_TEST(!isfinite(qnan));
57+
58+
// isnormal
59+
BOOST_TEST(isnormal(one));
60+
BOOST_TEST(!isnormal(zero));
61+
BOOST_TEST(!isnormal(pos_inf));
62+
BOOST_TEST(!isnormal(qnan));
63+
BOOST_TEST(!isnormal(tiny));
64+
65+
// signbit
66+
BOOST_TEST(signbit(neg_zero));
67+
BOOST_TEST(!signbit(zero));
68+
BOOST_TEST(signbit(neg_one));
69+
BOOST_TEST(!signbit(one));
70+
BOOST_TEST(signbit(neg_inf));
71+
BOOST_TEST(!signbit(pos_inf));
72+
73+
// fpclassify
74+
BOOST_TEST(fpclassify(zero) == FP_ZERO);
75+
BOOST_TEST(fpclassify(neg_zero) == FP_ZERO);
76+
BOOST_TEST(fpclassify(one) == FP_NORMAL);
77+
BOOST_TEST(fpclassify(pos_inf) == FP_INFINITE);
78+
BOOST_TEST(fpclassify(qnan) == FP_NAN);
79+
BOOST_TEST(fpclassify(tiny) == FP_SUBNORMAL);
80+
}
81+
82+
// Signaling-NaN bit pattern built via bit_cast, so it reaches the predicate
83+
// without being quieted by a floating-point operation first.
84+
template <typename T>
85+
void test_signaling_nan()
86+
{
87+
using basis_type = typename T::basis_type;
88+
89+
if constexpr (std::numeric_limits<basis_type>::has_signaling_NaN)
90+
{
91+
using bit_type = std::conditional_t<std::is_same_v<basis_type, float>, std::uint32_t, std::uint64_t>;
92+
93+
constexpr auto snan_bits {std::bit_cast<bit_type>(std::numeric_limits<basis_type>::signaling_NaN())};
94+
const T snan {std::bit_cast<basis_type>(snan_bits)};
95+
96+
BOOST_TEST(isnan(snan));
97+
BOOST_TEST(!isinf(snan));
98+
BOOST_TEST(!isfinite(snan));
99+
BOOST_TEST(!isnormal(snan));
100+
BOOST_TEST(fpclassify(snan) == FP_NAN);
101+
}
102+
}
103+
104+
// Non-signaling ordered comparisons: false (not a throw) when an operand is NAN.
105+
template <typename T>
106+
void test_comparisons()
107+
{
108+
using basis_type = typename T::basis_type;
109+
110+
const T one {static_cast<basis_type>(1.0)};
111+
const T two {static_cast<basis_type>(2.0)};
112+
const T qnan {std::numeric_limits<basis_type>::quiet_NaN()};
113+
114+
BOOST_TEST(isgreater(two, one));
115+
BOOST_TEST(!isgreater(one, two));
116+
BOOST_TEST(!isgreater(qnan, one));
117+
BOOST_TEST(!isgreater(one, qnan));
118+
119+
BOOST_TEST(isgreaterequal(two, one));
120+
BOOST_TEST(isgreaterequal(one, one));
121+
BOOST_TEST(!isgreaterequal(qnan, one));
122+
123+
BOOST_TEST(isless(one, two));
124+
BOOST_TEST(!isless(two, one));
125+
BOOST_TEST(!isless(qnan, one));
126+
127+
BOOST_TEST(islessequal(one, two));
128+
BOOST_TEST(islessequal(one, one));
129+
BOOST_TEST(!islessequal(qnan, one));
130+
131+
BOOST_TEST(islessgreater(one, two));
132+
BOOST_TEST(!islessgreater(one, one));
133+
BOOST_TEST(!islessgreater(qnan, one));
134+
135+
BOOST_TEST(isunordered(qnan, one));
136+
BOOST_TEST(isunordered(one, qnan));
137+
BOOST_TEST(!isunordered(one, two));
138+
}
139+
140+
int main()
141+
{
142+
test_predicates<f32>();
143+
test_predicates<f64>();
144+
145+
test_signaling_nan<f32>();
146+
test_signaling_nan<f64>();
147+
148+
test_comparisons<f32>();
149+
test_comparisons<f64>();
150+
151+
return boost::report_errors();
152+
}

0 commit comments

Comments
 (0)