Skip to content

Commit 35a003a

Browse files
authored
Merge pull request #562 from ryanelandt/denorm_min_is_nonzero
`denorm_min` is not zero
2 parents 5f81a78 + 1624080 commit 35a003a

7 files changed

Lines changed: 89 additions & 31 deletions

File tree

include/boost/multiprecision/cpp_bin_float.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2172,7 +2172,7 @@ class numeric_limits<boost::multiprecision::number<boost::multiprecision::cpp_bi
21722172
{
21732173
return number_type(0);
21742174
}
2175-
static constexpr number_type denorm_min() { return number_type(0); }
2175+
static constexpr number_type denorm_min() { return get_min(); }
21762176
static constexpr bool is_iec559 = false;
21772177
static constexpr bool is_bounded = true;
21782178
static constexpr bool is_modulo = false;

include/boost/multiprecision/cpp_dec_float.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3591,7 +3591,7 @@ class numeric_limits<boost::multiprecision::number<boost::multiprecision::cpp_de
35913591
static constexpr boost::multiprecision::number<boost::multiprecision::cpp_dec_float<Digits10, ExponentType, Allocator>, ExpressionTemplates> infinity() { return boost::multiprecision::cpp_dec_float<Digits10, ExponentType, Allocator>::inf(); }
35923592
static constexpr boost::multiprecision::number<boost::multiprecision::cpp_dec_float<Digits10, ExponentType, Allocator>, ExpressionTemplates> quiet_NaN() { return boost::multiprecision::cpp_dec_float<Digits10, ExponentType, Allocator>::nan(); }
35933593
static constexpr boost::multiprecision::number<boost::multiprecision::cpp_dec_float<Digits10, ExponentType, Allocator>, ExpressionTemplates> signaling_NaN() { return boost::multiprecision::cpp_dec_float<Digits10, ExponentType, Allocator>::zero(); }
3594-
static constexpr boost::multiprecision::number<boost::multiprecision::cpp_dec_float<Digits10, ExponentType, Allocator>, ExpressionTemplates> denorm_min() { return boost::multiprecision::cpp_dec_float<Digits10, ExponentType, Allocator>::zero(); }
3594+
static constexpr boost::multiprecision::number<boost::multiprecision::cpp_dec_float<Digits10, ExponentType, Allocator>, ExpressionTemplates> denorm_min() { return (boost::multiprecision::cpp_dec_float<Digits10, ExponentType, Allocator>::min)(); }
35953595
};
35963596

35973597
template <unsigned Digits10, class ExponentType, class Allocator, boost::multiprecision::expression_template_option ExpressionTemplates>

include/boost/multiprecision/gmp.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3595,7 +3595,7 @@ class numeric_limits<boost::multiprecision::number<boost::multiprecision::gmp_fl
35953595
static constexpr number_type infinity() { return number_type(); }
35963596
static constexpr number_type quiet_NaN() { return number_type(); }
35973597
static constexpr number_type signaling_NaN() { return number_type(); }
3598-
static constexpr number_type denorm_min() { return number_type(); }
3598+
static constexpr number_type denorm_min() { return (min)(); }
35993599
static constexpr bool is_iec559 = false;
36003600
static constexpr bool is_bounded = true;
36013601
static constexpr bool is_modulo = false;
@@ -3871,7 +3871,7 @@ class numeric_limits<boost::multiprecision::number<boost::multiprecision::gmp_ra
38713871
static number_type infinity() { return number_type(); }
38723872
static number_type quiet_NaN() { return number_type(); }
38733873
static number_type signaling_NaN() { return number_type(); }
3874-
static number_type denorm_min() { return number_type(); }
3874+
static number_type denorm_min() { return (min)(); }
38753875
static constexpr bool is_iec559 = false;
38763876
static constexpr bool is_bounded = false;
38773877
static constexpr bool is_modulo = false;

include/boost/multiprecision/mpfi.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2388,7 +2388,7 @@ class numeric_limits<boost::multiprecision::number<boost::multiprecision::mpfi_f
23882388
{
23892389
return number_type(0);
23902390
}
2391-
static constexpr number_type denorm_min() { return number_type(0); }
2391+
static constexpr number_type denorm_min() { return (min)(); }
23922392
static constexpr bool is_iec559 = false;
23932393
static constexpr bool is_bounded = true;
23942394
static constexpr bool is_modulo = false;

include/boost/multiprecision/mpfr.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3434,7 +3434,7 @@ class numeric_limits<boost::multiprecision::number<boost::multiprecision::mpfr_f
34343434
{
34353435
return number_type(0);
34363436
}
3437-
static constexpr number_type denorm_min() { return number_type(0); }
3437+
static constexpr number_type denorm_min() { return (min)(); }
34383438
static constexpr bool is_iec559 = false;
34393439
static constexpr bool is_bounded = true;
34403440
static constexpr bool is_modulo = false;
@@ -3550,7 +3550,7 @@ class numeric_limits<boost::multiprecision::number<boost::multiprecision::mpfr_f
35503550
return value;
35513551
}
35523552
static number_type signaling_NaN() { return number_type(0); }
3553-
static number_type denorm_min() { return number_type(0); }
3553+
static number_type denorm_min() { return (min)(); }
35543554
static constexpr bool is_iec559 = false;
35553555
static constexpr bool is_bounded = true;
35563556
static constexpr bool is_modulo = false;

test/test_cpp_bin_float.cpp

Lines changed: 61 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -50,25 +50,31 @@ typedef number<cpp_bin_float<std::numeric_limits<good_type>::digits, digit_base_
5050
void test_special_cases()
5151
{
5252
#if !defined(BOOST_CI_ASAN_BUILD) && !defined(BOOST_CI_USAN_BUID)
53-
test_type max_val = (std::numeric_limits<test_type>::max)();
54-
test_type min_val = (std::numeric_limits<test_type>::min)();
55-
test_type eps = std::numeric_limits<test_type>::epsilon();
56-
test_type inf_val = (std::numeric_limits<test_type>::infinity)();
57-
test_type nan_val = (std::numeric_limits<test_type>::quiet_NaN)();
58-
test_type half = 0.5;
59-
test_type one_point_5 = 1.5;
53+
test_type max_val = (std::numeric_limits<test_type>::max)();
54+
test_type denorm_min_val = std::numeric_limits<test_type>::denorm_min();
55+
test_type min_val = (std::numeric_limits<test_type>::min)();
56+
test_type eps = std::numeric_limits<test_type>::epsilon();
57+
test_type inf_val = (std::numeric_limits<test_type>::infinity)();
58+
test_type nan_val = (std::numeric_limits<test_type>::quiet_NaN)();
59+
test_type half = 0.5;
60+
test_type one_point_5 = 1.5;
6061

6162
BOOST_CHECK((boost::math::isnormal)(max_val));
6263
BOOST_CHECK((boost::math::isnormal)(-max_val));
6364
BOOST_CHECK((boost::math::isnormal)(min_val));
6465
BOOST_CHECK((boost::math::isnormal)(-min_val));
66+
BOOST_CHECK((boost::math::isnormal)(denorm_min_val));
67+
BOOST_CHECK((boost::math::isnormal)(-denorm_min_val));
6568
BOOST_CHECK((boost::math::isinf)(inf_val));
6669
BOOST_CHECK((boost::math::isinf)(-inf_val));
6770
BOOST_CHECK((boost::math::isnan)(nan_val));
6871
BOOST_CHECK((boost::math::isnan)(-nan_val));
6972

70-
if (std::numeric_limits<test_type>::has_denorm)
71-
min_val = std::numeric_limits<test_type>::denorm_min();
73+
if (!std::numeric_limits<test_type>::has_denorm) {
74+
BOOST_CHECK_EQUAL(denorm_min_val, min_val);
75+
} else {
76+
BOOST_CHECK_LE(denorm_min_val, min_val);
77+
}
7278

7379
// Adding epsilon will increment 1.0:
7480
BOOST_CHECK(test_type(1) + eps != test_type(1));
@@ -91,23 +97,39 @@ void test_special_cases()
9197
BOOST_CHECK_EQUAL(max_val / -half, -inf_val);
9298
BOOST_CHECK_EQUAL(max_val / min_val, inf_val);
9399
BOOST_CHECK_EQUAL(max_val / -min_val, -inf_val);
100+
BOOST_CHECK_EQUAL(max_val / denorm_min_val, inf_val);
101+
BOOST_CHECK_EQUAL(max_val / -denorm_min_val, -inf_val);
94102
// Underflow:
95103
BOOST_CHECK_EQUAL(min_val * 2 - one_point_5 * min_val, 0);
96104
BOOST_CHECK_EQUAL(-min_val * 2 + one_point_5 * min_val, 0);
97105
BOOST_CHECK_EQUAL(min_val / 2, 0);
98106
BOOST_CHECK_EQUAL(min_val / max_val, 0);
99107
BOOST_CHECK_EQUAL(min_val * half, 0);
100108
BOOST_CHECK_EQUAL(min_val - min_val, 0);
101-
BOOST_CHECK_EQUAL(max_val - max_val, 0);
102109
BOOST_CHECK_EQUAL(-min_val + min_val, 0);
103-
BOOST_CHECK_EQUAL(-max_val + max_val, 0);
110+
111+
BOOST_CHECK_EQUAL(denorm_min_val * 2 - one_point_5 * denorm_min_val, 0);
112+
BOOST_CHECK_EQUAL(-denorm_min_val * 2 + one_point_5 * denorm_min_val, 0);
113+
BOOST_CHECK_EQUAL(denorm_min_val / 2, 0);
114+
BOOST_CHECK_EQUAL(denorm_min_val / max_val, 0);
115+
BOOST_CHECK_EQUAL(denorm_min_val * half, 0);
116+
BOOST_CHECK_EQUAL(denorm_min_val - denorm_min_val, 0);
117+
BOOST_CHECK_EQUAL(-denorm_min_val + denorm_min_val, 0);
104118
// Things which should not over/underflow:
105119
BOOST_CHECK_EQUAL((min_val * 2) / 2, min_val);
106-
BOOST_CHECK_EQUAL((max_val / 2) * 2, max_val);
107120
BOOST_CHECK_GE((min_val * 2.0000001) / 1.9999999999999999, min_val);
108-
BOOST_CHECK_LE((max_val / 2.0000001) * 1.9999999999999999, max_val);
109121
BOOST_CHECK_EQUAL(min_val * 2 - min_val, min_val);
122+
123+
BOOST_CHECK_EQUAL((denorm_min_val * 2) / 2, denorm_min_val);
124+
BOOST_CHECK_GE((denorm_min_val * 2.0000001) / 1.9999999999999999, denorm_min_val);
125+
BOOST_CHECK_EQUAL(denorm_min_val * 2 - denorm_min_val, denorm_min_val);
126+
127+
BOOST_CHECK_EQUAL(-max_val + max_val, 0);
128+
BOOST_CHECK_EQUAL(max_val - max_val, 0);
129+
BOOST_CHECK_EQUAL((max_val / 2) * 2, max_val);
130+
BOOST_CHECK_LE((max_val / 2.0000001) * 1.9999999999999999, max_val);
110131
BOOST_CHECK_EQUAL(max_val / 2 + max_val / 2, max_val);
132+
111133
// Things involving zero:
112134
BOOST_CHECK_EQUAL(max_val + 0, max_val);
113135
BOOST_CHECK_EQUAL(max_val - 0, max_val);
@@ -210,12 +232,20 @@ void test_special_cases()
210232
// Corner cases:
211233
BOOST_CHECK_EQUAL((max_val * half) / half, max_val);
212234
BOOST_CHECK_EQUAL((max_val / 2) * 2, max_val);
235+
213236
BOOST_CHECK_EQUAL((min_val / half) * half, min_val);
214237
BOOST_CHECK_EQUAL((min_val * 2) / 2, min_val);
215238
BOOST_CHECK_EQUAL(max_val + min_val, max_val);
216239
BOOST_CHECK_EQUAL(min_val + max_val, max_val);
217240
BOOST_CHECK_EQUAL(max_val - min_val, max_val);
218241
BOOST_CHECK_EQUAL(min_val - max_val, -max_val);
242+
243+
BOOST_CHECK_EQUAL((denorm_min_val / half) * half, denorm_min_val);
244+
BOOST_CHECK_EQUAL((denorm_min_val * 2) / 2, denorm_min_val);
245+
BOOST_CHECK_EQUAL(max_val + denorm_min_val, max_val);
246+
BOOST_CHECK_EQUAL(denorm_min_val + max_val, max_val);
247+
BOOST_CHECK_EQUAL(max_val - denorm_min_val, max_val);
248+
BOOST_CHECK_EQUAL(denorm_min_val - max_val, -max_val);
219249
// Signed zeros:
220250
BOOST_CHECK(boost::math::signbit(min_val * -min_val));
221251
BOOST_CHECK(boost::math::signbit(min_val * min_val) == 0);
@@ -230,6 +260,24 @@ void test_special_cases()
230260
BOOST_CHECK(boost::math::signbit(-min_val / -2) == 0);
231261
BOOST_CHECK(boost::math::signbit(-min_val / 2));
232262
test_type neg_zero = min_val * -min_val;
263+
264+
BOOST_CHECK(boost::math::signbit(denorm_min_val * -denorm_min_val));
265+
BOOST_CHECK(boost::math::signbit(denorm_min_val * denorm_min_val) == 0);
266+
BOOST_CHECK(boost::math::signbit(-denorm_min_val * -denorm_min_val) == 0);
267+
BOOST_CHECK(boost::math::signbit(-denorm_min_val * denorm_min_val));
268+
BOOST_CHECK(boost::math::signbit(denorm_min_val / max_val) == 0);
269+
BOOST_CHECK(boost::math::signbit(denorm_min_val / -max_val));
270+
BOOST_CHECK(boost::math::signbit(-denorm_min_val / -max_val) == 0);
271+
BOOST_CHECK(boost::math::signbit(-denorm_min_val / max_val));
272+
BOOST_CHECK(boost::math::signbit(denorm_min_val / 2) == 0);
273+
BOOST_CHECK(boost::math::signbit(denorm_min_val / -2));
274+
BOOST_CHECK(boost::math::signbit(-denorm_min_val / -2) == 0);
275+
BOOST_CHECK(boost::math::signbit(-denorm_min_val / 2));
276+
277+
test_type neg_denorm_zero = denorm_min_val * -denorm_min_val;
278+
279+
BOOST_CHECK_EQUAL(neg_zero, neg_denorm_zero);
280+
233281
test_type zero = 0;
234282
// Arithmetic involving signed zero:
235283
BOOST_CHECK_EQUAL(-neg_zero, 0);

test/test_numeric_limits.cpp

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -83,33 +83,33 @@ void test_specific(const std::integral_constant<int, boost::multiprecision::numb
8383
BOOST_CHECK((boost::math::isnormal)(sqrt(minv)));
8484
BOOST_CHECK((boost::math::isnormal)(sqrt(maxv)));
8585

86-
if (std::numeric_limits<Number>::is_specialized)
86+
BOOST_IF_CONSTEXPR(std::numeric_limits<Number>::is_specialized)
8787
{
88-
if (std::numeric_limits<Number>::has_quiet_NaN)
88+
BOOST_IF_CONSTEXPR(std::numeric_limits<Number>::has_quiet_NaN)
8989
{
9090
BOOST_TEST((boost::math::isnan)(std::numeric_limits<Number>::quiet_NaN()));
9191
BOOST_TEST(FP_NAN == (boost::math::fpclassify)(std::numeric_limits<Number>::quiet_NaN()));
9292
BOOST_TEST(!(boost::math::isfinite)(std::numeric_limits<Number>::quiet_NaN()));
9393
BOOST_TEST(!(boost::math::isnormal)(std::numeric_limits<Number>::quiet_NaN()));
9494
BOOST_TEST(!(boost::math::isinf)(std::numeric_limits<Number>::quiet_NaN()));
9595
}
96-
if (std::numeric_limits<Number>::has_signaling_NaN)
96+
BOOST_IF_CONSTEXPR(std::numeric_limits<Number>::has_signaling_NaN)
9797
{
9898
BOOST_TEST((boost::math::isnan)(std::numeric_limits<Number>::signaling_NaN()));
9999
BOOST_TEST(FP_NAN == (boost::math::fpclassify)(std::numeric_limits<Number>::signaling_NaN()));
100100
BOOST_TEST(!(boost::math::isfinite)(std::numeric_limits<Number>::signaling_NaN()));
101101
BOOST_TEST(!(boost::math::isnormal)(std::numeric_limits<Number>::signaling_NaN()));
102102
BOOST_TEST(!(boost::math::isinf)(std::numeric_limits<Number>::signaling_NaN()));
103103
}
104-
if (std::numeric_limits<Number>::has_infinity)
104+
BOOST_IF_CONSTEXPR(std::numeric_limits<Number>::has_infinity)
105105
{
106106
BOOST_TEST((boost::math::isinf)(std::numeric_limits<Number>::infinity()));
107107
BOOST_TEST(FP_INFINITE == (boost::math::fpclassify)(std::numeric_limits<Number>::infinity()));
108108
BOOST_TEST(!(boost::math::isfinite)(std::numeric_limits<Number>::infinity()));
109109
BOOST_TEST(!(boost::math::isnormal)(std::numeric_limits<Number>::infinity()));
110110
BOOST_TEST(!(boost::math::isnan)(std::numeric_limits<Number>::infinity()));
111111
}
112-
if (std::numeric_limits<Number>::has_denorm == std::denorm_present)
112+
BOOST_IF_CONSTEXPR(std::numeric_limits<Number>::has_denorm == std::denorm_present)
113113
{
114114
BOOST_TEST(FP_SUBNORMAL == (boost::math::fpclassify)(std::numeric_limits<Number>::denorm_min()));
115115
BOOST_TEST(FP_SUBNORMAL == (boost::math::fpclassify)((std::numeric_limits<Number>::min)() / 2));
@@ -121,6 +121,16 @@ void test_specific(const std::integral_constant<int, boost::multiprecision::numb
121121
BOOST_TEST(0 != (std::numeric_limits<Number>::min)() / 2);
122122
BOOST_TEST(0 != std::numeric_limits<Number>::denorm_min());
123123
}
124+
BOOST_IF_CONSTEXPR(std::numeric_limits<Number>::has_denorm == std::denorm_absent)
125+
{
126+
BOOST_TEST(std::numeric_limits<Number>::denorm_min() > 0);
127+
BOOST_TEST(!(std::numeric_limits<Number>::denorm_min() > (std::numeric_limits<Number>::min)()));
128+
BOOST_TEST(!(std::numeric_limits<Number>::denorm_min() < (std::numeric_limits<Number>::min)()));
129+
BOOST_TEST(std::numeric_limits<Number>::denorm_min() == (std::numeric_limits<Number>::min)());
130+
BOOST_TEST(FP_NORMAL == (boost::math::fpclassify)(std::numeric_limits<Number>::denorm_min()));
131+
BOOST_TEST(FP_NORMAL == (boost::math::fpclassify)(-std::numeric_limits<Number>::denorm_min()));
132+
BOOST_TEST(FP_ZERO != (boost::math::fpclassify)(std::numeric_limits<Number>::denorm_min()));
133+
}
124134
}
125135
Number n = 0;
126136
BOOST_TEST((boost::math::fpclassify)(n) == FP_ZERO);
@@ -135,11 +145,11 @@ void test_specific(const std::integral_constant<int, boost::multiprecision::numb
135145
BOOST_TEST(!(boost::math::isinf)(n));
136146
BOOST_TEST(!(boost::math::isnan)(n));
137147

138-
if (std::numeric_limits<Number>::round_style == std::round_to_nearest)
148+
BOOST_IF_CONSTEXPR(std::numeric_limits<Number>::round_style == std::round_to_nearest)
139149
{
140150
BOOST_CHECK_EQUAL(std::numeric_limits<Number>::round_error(), 0.5);
141151
}
142-
else if (std::numeric_limits<Number>::round_style != std::round_indeterminate)
152+
else BOOST_IF_CONSTEXPR(std::numeric_limits<Number>::round_style != std::round_indeterminate)
143153
{
144154
// Round error is 1.0:
145155
BOOST_CHECK_EQUAL(std::numeric_limits<Number>::round_error(), 1);
@@ -154,9 +164,9 @@ void test_specific(const std::integral_constant<int, boost::multiprecision::numb
154164
template <class Number>
155165
void test_specific(const std::integral_constant<int, boost::multiprecision::number_kind_integer>&)
156166
{
157-
if (std::numeric_limits<Number>::is_modulo)
167+
BOOST_IF_CONSTEXPR(std::numeric_limits<Number>::is_modulo)
158168
{
159-
if (!std::numeric_limits<Number>::is_signed)
169+
BOOST_IF_CONSTEXPR(!std::numeric_limits<Number>::is_signed)
160170
{
161171
BOOST_TEST(1 + (std::numeric_limits<Number>::max)() == 0);
162172
BOOST_TEST(--Number(0) == (std::numeric_limits<Number>::max)());
@@ -186,13 +196,13 @@ void test()
186196
std::cout << "numeric_limits values for type " << typeid(Number).name() << std::endl;
187197

188198
PRINT(is_specialized);
189-
if (std::numeric_limits<Number>::is_integer)
199+
BOOST_IF_CONSTEXPR(std::numeric_limits<Number>::is_integer)
190200
{
191201
std::cout << std::hex << std::showbase;
192202
}
193203
std::cout << "max()"
194204
<< " = " << (std::numeric_limits<Number>::max)() << std::endl;
195-
if (std::numeric_limits<Number>::is_integer)
205+
BOOST_IF_CONSTEXPR(std::numeric_limits<Number>::is_integer)
196206
{
197207
std::cout << std::dec;
198208
}

0 commit comments

Comments
 (0)