@@ -41,8 +41,6 @@ inline constexpr struct half_revolution : named_unit<"hrev", mag_pi * radian> {
41
41
} half_revolution;
42
42
inline constexpr auto hrev = half_revolution;
43
43
44
- // constexpr auto revb6 = mag_ratio<1,3> * mag_pi * rad;
45
-
46
44
TEST_CASE (" value_cast should not truncate for valid inputs" , " [value_cast]" )
47
45
{
48
46
SECTION (" num > den > 1, irr = 1" )
@@ -92,8 +90,18 @@ TEST_CASE("value_cast should not truncate for valid inputs", "[value_cast]")
92
90
}
93
91
94
92
93
+ inline constexpr std::int64_t one_64bit = 1 ;
94
+ inline constexpr struct one_in_2to50 : named_unit<" oi2t50" , mag_ratio<(one_64bit << 50 ) + 1 , (one_64bit << 50 )> * one> {
95
+ } one_in_2to50;
96
+ inline constexpr auto oi2t50 = one_in_2to50;
97
+
98
+ inline constexpr struct one_in_2to60 : named_unit<" oi2t60" , mag_ratio<(one_64bit << 60 ) + 1 , (one_64bit << 60 )> * one> {
99
+ } one_in_2to60;
100
+ inline constexpr auto oi2t60 = one_in_2to60;
101
+
102
+
95
103
TEMPLATE_TEST_CASE (" value_cast should not overflow internally for valid inputs" , " [value_cast]" , std::int8_t ,
96
- std::uint8_t , std::int16_t , std::uint16_t , std::int32_t , std::uint32_t )
104
+ std::uint8_t , std::int16_t , std::uint16_t , std::int32_t , std::uint32_t , std:: int64_t , std:: uint64_t )
97
105
{
98
106
// max()/20: small enough so that none of the tested factors are likely to cause overflow, but still be nonzero;
99
107
// the "easy" test to verify the test itself is good.
@@ -103,24 +111,57 @@ TEMPLATE_TEST_CASE("value_cast should not overflow internally for valid inputs",
103
111
test_values.push_back (std::numeric_limits<TestType>::min () + 1 );
104
112
}
105
113
106
- for (TestType tv : test_values) {
107
- SECTION (" grad <-> deg" )
108
- {
109
- auto deg_number = static_cast <TestType>(std::trunc (0.9 * tv));
110
- auto grad_number = static_cast <TestType>(std::round (deg_number / 0.9 ));
114
+ SECTION (" grad <-> deg" )
115
+ {
116
+ for (TestType tv : test_values) {
117
+ // non-overflowing computation for b = 360/400 * a = (10 - 1)/10 * a = (1 - 1/10) * a = 1 - a/10
118
+ auto deg_number = tv - tv / 10 ;
119
+ // non-overflowing computation for b = 400/360 * a = (9 + 1)/9 * a = (1 + 1/9) * a = 1 + a/9
120
+ auto grad_number = deg_number + deg_number / 9 ;
111
121
INFO (MP_UNITS_STD_FMT::format (" {} deg ~ {} grad" , deg_number, grad_number));
112
122
REQUIRE_THAT (value_cast<grad>(deg_number * deg), AlmostEquals (grad_number * grad));
113
123
REQUIRE_THAT (value_cast<deg>(grad_number * grad), AlmostEquals (deg_number * deg));
114
124
}
125
+ }
126
+
127
+ if constexpr (std::numeric_limits<TestType>::digits >= 60 ) {
128
+ // ---- a couple of pathological conversion factors
115
129
130
+ // this one can still be correctly calculated using a double-precision calculation
131
+ SECTION (" one <-> (1 + 2^-50) * one" )
132
+ {
133
+ for (TestType tv : test_values) {
134
+ auto n1 = tv - tv >> 50 ;
135
+ auto n2 = n1 + n1 / ((one_64bit << 50 ) - 1 );
136
+ INFO (MP_UNITS_STD_FMT::format (" {} (1 + 2^-50) * one ~ {} one" , n1, n2));
137
+ REQUIRE_THAT (value_cast<one>(n1 * oi2t50), AlmostEquals (n2 * one));
138
+ REQUIRE_THAT (value_cast<oi2t50>(n2 * one), AlmostEquals (n1 * oi2t50));
139
+ }
140
+ }
116
141
142
+ // this one cannot be correctly calculated in double-precision
143
+ SECTION (" one <-> (1 + 2^-60) * one" )
144
+ {
145
+ for (TestType tv : test_values) {
146
+ auto n1 = tv - tv >> 60 ;
147
+ auto n2 = n1 + n1 / ((one_64bit << 60 ) - 1 );
148
+ INFO (MP_UNITS_STD_FMT::format (" {} (1 + 2^-60) * one ~ {} one" , n1, n2));
149
+ REQUIRE_THAT (value_cast<one>(n1 * oi2t60), AlmostEquals (n2 * one));
150
+ REQUIRE_THAT (value_cast<oi2t60>(n2 * one), AlmostEquals (n1 * oi2t60));
151
+ }
152
+ }
153
+ } else {
154
+ // skipping this oen for the 64 bit types; we don't know how to calculate the expected results to 64 bits
155
+ // precision...
117
156
SECTION (" rad <-> rev" )
118
157
{
119
- auto rev_number = static_cast <TestType>(std::trunc (0.5 * std::numbers::inv_pi * tv));
120
- auto rad_number = static_cast <TestType>(std::round (2 * std::numbers::pi * rev_number));
121
- INFO (MP_UNITS_STD_FMT::format (" {} rev ~ {} rad" , rev_number, rad_number));
122
- REQUIRE_THAT (value_cast<rad>(rev_number * rev), AlmostEquals (rad_number * rad));
123
- REQUIRE_THAT (value_cast<rev>(rad_number * rad), AlmostEquals (rev_number * rev));
158
+ for (TestType tv : test_values) {
159
+ auto rev_number = static_cast <TestType>(std::trunc (0.5 * std::numbers::inv_pi * tv));
160
+ auto rad_number = static_cast <TestType>(std::round (2 * std::numbers::pi * rev_number));
161
+ INFO (MP_UNITS_STD_FMT::format (" {} rev ~ {} rad" , rev_number, rad_number));
162
+ REQUIRE_THAT (value_cast<rad>(rev_number * rev), AlmostEquals (rad_number * rad));
163
+ REQUIRE_THAT (value_cast<rev>(rad_number * rad), AlmostEquals (rev_number * rev));
164
+ }
124
165
}
125
166
}
126
167
}
0 commit comments