Skip to content

Commit c0deda9

Browse files
authored
Merge pull request #851 from cppalliance/saturate
Improve and document explicit conversion saturation
2 parents 0e0a7ec + 6492aa8 commit c0deda9

12 files changed

+58
-54
lines changed

doc/decimal/decimal128.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ template <typename Integral>
5050
constexpr decimal128& operator=(const Integeral& RHS) noexcept;
5151
5252
// 3.2.4.4 Conversion to integral type
53+
// If the value exceeds the range of the integral,
54+
// or is non-finite std::numeric_limits::max() is returned
5355
explicit constexpr operator int() const noexcept;
5456
explicit constexpr operator unsigned() const noexcept;
5557
explicit constexpr operator long() const noexcept;

doc/decimal/decimal128_fast.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ template <typename Integral>
5050
constexpr decimal128_fast& operator=(const Integeral& RHS) noexcept;
5151
5252
// 3.2.4.4 Conversion to integral type
53+
// If the value exceeds the range of the integral,
54+
// or is non-finite std::numeric_limits::max() is returned
5355
explicit constexpr operator int() const noexcept;
5456
explicit constexpr operator unsigned() const noexcept;
5557
explicit constexpr operator long() const noexcept;

doc/decimal/decimal32.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ template <typename Integral>
5050
constexpr decimal32& operator=(const Integeral& RHS) noexcept;
5151
5252
// 3.2.2.4 Conversion to integral type
53+
// If the value exceeds the range of the integral,
54+
// or is non-finite std::numeric_limits::max() is returned
5355
explicit constexpr operator int() const noexcept;
5456
explicit constexpr operator unsigned() const noexcept;
5557
explicit constexpr operator long() const noexcept;

doc/decimal/decimal32_fast.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ template <typename Integral>
5050
constexpr decimal32_fast& operator=(const Integeral& RHS) noexcept;
5151
5252
// 3.2.2.4 Conversion to integral type
53+
// If the value exceeds the range of the integral,
54+
// or is non-finite std::numeric_limits::max() is returned
5355
explicit constexpr operator int() const noexcept;
5456
explicit constexpr operator unsigned() const noexcept;
5557
explicit constexpr operator long() const noexcept;

doc/decimal/decimal64.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ template <typename Integral>
5050
constexpr decimal64& operator=(const Integeral& RHS) noexcept;
5151
5252
// 3.2.3.4 Conversion to integral type
53+
// If the value exceeds the range of the integral,
54+
// or is non-finite std::numeric_limits::max() is returned
5355
explicit constexpr operator int() const noexcept;
5456
explicit constexpr operator unsigned() const noexcept;
5557
explicit constexpr operator long() const noexcept;

doc/decimal/decimal64_fast.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ template <typename Integral>
5050
constexpr decimal64_fast& operator=(const Integeral& RHS) noexcept;
5151
5252
// 3.2.3.4 Conversion to integral type
53+
// If the value exceeds the range of the integral,
54+
// or is non-finite std::numeric_limits::max() is returned
5355
explicit constexpr operator int() const noexcept;
5456
explicit constexpr operator unsigned() const noexcept;
5557
explicit constexpr operator long() const noexcept;

include/boost/decimal/detail/to_integral.hpp

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -41,23 +41,19 @@ constexpr auto to_integral(Decimal val) noexcept
4141
if (isnan(val))
4242
{
4343
errno = EINVAL;
44-
return static_cast<TargetType>(0);
44+
return static_cast<TargetType>(std::numeric_limits<TargetType>::max());
4545
}
4646
if (isinf(val) || val > max_target_type || val < min_target_type)
4747
{
4848
errno = ERANGE;
49-
return static_cast<TargetType>(0);
49+
return static_cast<TargetType>(std::numeric_limits<TargetType>::max());
5050
}
5151

5252
auto result {static_cast<Conversion_Type>(val.full_significand())};
5353
auto expval {val.biased_exponent()};
5454
const auto abs_exp_val {detail::make_positive_unsigned(expval)};
5555

56-
if (abs_exp_val >= 19)
57-
{
58-
result = 0;
59-
}
60-
else if (expval > 0)
56+
if (expval > 0)
6157
{
6258
result *= detail::pow10<Conversion_Type>(static_cast<Conversion_Type>(expval));
6359
}
@@ -84,23 +80,19 @@ constexpr auto to_integral_128(Decimal val) noexcept
8480
if (isnan(val))
8581
{
8682
errno = EINVAL;
87-
return static_cast<TargetType>(UINT64_C(0));
83+
return static_cast<TargetType>(std::numeric_limits<TargetType>::max());
8884
}
8985
if (isinf(val) || val > max_target_type || val < min_target_type)
9086
{
9187
errno = ERANGE;
92-
return static_cast<TargetType>(UINT64_C(0));
88+
return static_cast<TargetType>(std::numeric_limits<TargetType>::max());
9389
}
9490

9591
auto sig {val.full_significand()};
9692
auto expval {val.biased_exponent()};
9793
const auto abs_exp_val {detail::make_positive_unsigned(expval)};
9894

99-
if (abs_exp_val >= 38)
100-
{
101-
sig = 0;
102-
}
103-
else if (expval > 0)
95+
if (expval > 0)
10496
{
10597
sig *= detail::pow10<detail::uint128>(expval);
10698
}

test/roundtrip_decimal128.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,20 +61,20 @@ void test_conversion_to_integer()
6161
else
6262
{
6363
// Bad conversion so we use zero
64-
BOOST_TEST_EQ(static_cast<T>(-one), static_cast<T>(0)) && BOOST_TEST_EQ(errno, ERANGE);
64+
BOOST_TEST_EQ(static_cast<T>(-one), std::numeric_limits<T>::max()) && BOOST_TEST_EQ(errno, ERANGE);
6565
}
6666

6767
errno = 0;
68-
BOOST_TEST_EQ(static_cast<T>(std::numeric_limits<decimal128>::infinity()), static_cast<T>(0)) && BOOST_TEST_EQ(errno, ERANGE);
68+
BOOST_TEST_EQ(static_cast<T>(std::numeric_limits<decimal128>::infinity()), std::numeric_limits<T>::max()) && BOOST_TEST_EQ(errno, ERANGE);
6969

7070
errno = 0;
71-
BOOST_TEST_EQ(static_cast<T>(-std::numeric_limits<decimal128>::infinity()), static_cast<T>(0)) && BOOST_TEST_EQ(errno, ERANGE);
71+
BOOST_TEST_EQ(static_cast<T>(-std::numeric_limits<decimal128>::infinity()), std::numeric_limits<T>::max()) && BOOST_TEST_EQ(errno, ERANGE);
7272

7373
errno = 0;
74-
BOOST_TEST_EQ(static_cast<T>(std::numeric_limits<decimal128>::quiet_NaN()), static_cast<T>(0)) && BOOST_TEST_EQ(errno, EINVAL);
74+
BOOST_TEST_EQ(static_cast<T>(std::numeric_limits<decimal128>::quiet_NaN()), std::numeric_limits<T>::max()) && BOOST_TEST_EQ(errno, EINVAL);
7575

7676
errno = 0;
77-
BOOST_TEST_EQ(static_cast<T>(std::numeric_limits<decimal128>::signaling_NaN()), static_cast<T>(0)) && BOOST_TEST_EQ(errno, EINVAL);
77+
BOOST_TEST_EQ(static_cast<T>(std::numeric_limits<decimal128>::signaling_NaN()), std::numeric_limits<T>::max()) && BOOST_TEST_EQ(errno, EINVAL);
7878

7979
errno = 0;
8080
BOOST_TEST_EQ(static_cast<T>(half), static_cast<T>(0)) && BOOST_TEST_EQ(errno, 0);
@@ -89,13 +89,13 @@ void test_conversion_to_integer()
8989
std::mt19937_64 rng(42);
9090
std::uniform_int_distribution<int> dist(-100, -20);
9191
errno = 0;
92-
BOOST_TEST_EQ(static_cast<unsigned>(decimal128(dist(rng))), static_cast<unsigned>(0)) && BOOST_TEST_EQ(errno, ERANGE);
92+
BOOST_TEST_EQ(static_cast<unsigned>(decimal128(dist(rng))), std::numeric_limits<unsigned>::max()) && BOOST_TEST_EQ(errno, ERANGE);
9393

9494
errno = 0;
95-
BOOST_TEST_EQ(static_cast<unsigned long>(decimal128(dist(rng))), static_cast<unsigned long>(0)) && BOOST_TEST_EQ(errno, ERANGE);
95+
BOOST_TEST_EQ(static_cast<unsigned long>(decimal128(dist(rng))), std::numeric_limits<unsigned long>::max()) && BOOST_TEST_EQ(errno, ERANGE);
9696

9797
errno = 0;
98-
BOOST_TEST_EQ(static_cast<unsigned long long>(decimal128(dist(rng))), static_cast<unsigned long long>(0)) && BOOST_TEST_EQ(errno, ERANGE);
98+
BOOST_TEST_EQ(static_cast<unsigned long long>(decimal128(dist(rng))), std::numeric_limits<unsigned long long>::max()) && BOOST_TEST_EQ(errno, ERANGE);
9999
}
100100

101101
template <typename T>

test/roundtrip_decimal128_fast.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,20 +61,20 @@ void test_conversion_to_integer()
6161
else
6262
{
6363
// Bad conversion so we use zero
64-
BOOST_TEST_EQ(static_cast<T>(-one), static_cast<T>(0)) && BOOST_TEST_EQ(errno, ERANGE);
64+
BOOST_TEST_EQ(static_cast<T>(-one), std::numeric_limits<T>::max()) && BOOST_TEST_EQ(errno, ERANGE);
6565
}
6666

6767
errno = 0;
68-
BOOST_TEST_EQ(static_cast<T>(std::numeric_limits<decimal128_fast>::infinity()), static_cast<T>(0)) && BOOST_TEST_EQ(errno, ERANGE);
68+
BOOST_TEST_EQ(static_cast<T>(std::numeric_limits<decimal128_fast>::infinity()), std::numeric_limits<T>::max()) && BOOST_TEST_EQ(errno, ERANGE);
6969

7070
errno = 0;
71-
BOOST_TEST_EQ(static_cast<T>(-std::numeric_limits<decimal128_fast>::infinity()), static_cast<T>(0)) && BOOST_TEST_EQ(errno, ERANGE);
71+
BOOST_TEST_EQ(static_cast<T>(-std::numeric_limits<decimal128_fast>::infinity()), std::numeric_limits<T>::max()) && BOOST_TEST_EQ(errno, ERANGE);
7272

7373
errno = 0;
74-
BOOST_TEST_EQ(static_cast<T>(std::numeric_limits<decimal128_fast>::quiet_NaN()), static_cast<T>(0)) && BOOST_TEST_EQ(errno, EINVAL);
74+
BOOST_TEST_EQ(static_cast<T>(std::numeric_limits<decimal128_fast>::quiet_NaN()), std::numeric_limits<T>::max()) && BOOST_TEST_EQ(errno, EINVAL);
7575

7676
errno = 0;
77-
BOOST_TEST_EQ(static_cast<T>(std::numeric_limits<decimal128_fast>::signaling_NaN()), static_cast<T>(0)) && BOOST_TEST_EQ(errno, EINVAL);
77+
BOOST_TEST_EQ(static_cast<T>(std::numeric_limits<decimal128_fast>::signaling_NaN()), std::numeric_limits<T>::max()) && BOOST_TEST_EQ(errno, EINVAL);
7878

7979
errno = 0;
8080
BOOST_TEST_EQ(static_cast<T>(half), static_cast<T>(0)) && BOOST_TEST_EQ(errno, 0);
@@ -89,13 +89,13 @@ void test_conversion_to_integer()
8989
std::mt19937_64 rng(42);
9090
std::uniform_int_distribution<int> dist(-100, -20);
9191
errno = 0;
92-
BOOST_TEST_EQ(static_cast<unsigned>(decimal128_fast(dist(rng))), static_cast<unsigned>(0)) && BOOST_TEST_EQ(errno, ERANGE);
92+
BOOST_TEST_EQ(static_cast<unsigned>(decimal128_fast(dist(rng))), std::numeric_limits<unsigned>::max()) && BOOST_TEST_EQ(errno, ERANGE);
9393

9494
errno = 0;
95-
BOOST_TEST_EQ(static_cast<unsigned long>(decimal128_fast(dist(rng))), static_cast<unsigned long>(0)) && BOOST_TEST_EQ(errno, ERANGE);
95+
BOOST_TEST_EQ(static_cast<unsigned long>(decimal128_fast(dist(rng))), std::numeric_limits<unsigned long>::max()) && BOOST_TEST_EQ(errno, ERANGE);
9696

9797
errno = 0;
98-
BOOST_TEST_EQ(static_cast<unsigned long long>(decimal128_fast(dist(rng))), static_cast<unsigned long long>(0)) && BOOST_TEST_EQ(errno, ERANGE);
98+
BOOST_TEST_EQ(static_cast<unsigned long long>(decimal128_fast(dist(rng))), std::numeric_limits<unsigned long long>::max()) && BOOST_TEST_EQ(errno, ERANGE);
9999
}
100100

101101
template <typename T>

test/roundtrip_decimal32.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -54,20 +54,20 @@ void test_conversion_to_integer()
5454
else
5555
{
5656
// Bad conversion so we use zero
57-
BOOST_TEST_EQ(static_cast<T>(-one), static_cast<T>(0)) && BOOST_TEST_EQ(errno, ERANGE);
57+
BOOST_TEST_EQ(static_cast<T>(-one), std::numeric_limits<T>::max()) && BOOST_TEST_EQ(errno, ERANGE);
5858
}
5959

6060
errno = 0;
61-
BOOST_TEST_EQ(static_cast<T>(std::numeric_limits<decimal32>::infinity()), static_cast<T>(0)) && BOOST_TEST_EQ(errno, ERANGE);
61+
BOOST_TEST_EQ(static_cast<T>(std::numeric_limits<decimal32>::infinity()), std::numeric_limits<T>::max()) && BOOST_TEST_EQ(errno, ERANGE);
6262

6363
errno = 0;
64-
BOOST_TEST_EQ(static_cast<T>(-std::numeric_limits<decimal32>::infinity()), static_cast<T>(0)) && BOOST_TEST_EQ(errno, ERANGE);
64+
BOOST_TEST_EQ(static_cast<T>(-std::numeric_limits<decimal32>::infinity()), std::numeric_limits<T>::max()) && BOOST_TEST_EQ(errno, ERANGE);
6565

6666
errno = 0;
67-
BOOST_TEST_EQ(static_cast<T>(std::numeric_limits<decimal32>::quiet_NaN()), static_cast<T>(0)) && BOOST_TEST_EQ(errno, EINVAL);
67+
BOOST_TEST_EQ(static_cast<T>(std::numeric_limits<decimal32>::quiet_NaN()), std::numeric_limits<T>::max()) && BOOST_TEST_EQ(errno, EINVAL);
6868

6969
errno = 0;
70-
BOOST_TEST_EQ(static_cast<T>(std::numeric_limits<decimal32>::signaling_NaN()), static_cast<T>(0)) && BOOST_TEST_EQ(errno, EINVAL);
70+
BOOST_TEST_EQ(static_cast<T>(std::numeric_limits<decimal32>::signaling_NaN()), std::numeric_limits<T>::max()) && BOOST_TEST_EQ(errno, EINVAL);
7171

7272
errno = 0;
7373
BOOST_TEST_EQ(static_cast<T>(half), static_cast<T>(0)) && BOOST_TEST_EQ(errno, 0);
@@ -82,13 +82,13 @@ void test_conversion_to_integer()
8282
std::mt19937_64 rng(42);
8383
std::uniform_int_distribution<int> dist(-100, -20);
8484
errno = 0;
85-
BOOST_TEST_EQ(static_cast<unsigned>(decimal32(dist(rng))), static_cast<unsigned>(0)) && BOOST_TEST_EQ(errno, ERANGE);
85+
BOOST_TEST_EQ(static_cast<unsigned>(decimal32(dist(rng))), std::numeric_limits<unsigned>::max()) && BOOST_TEST_EQ(errno, ERANGE);
8686

8787
errno = 0;
88-
BOOST_TEST_EQ(static_cast<unsigned long>(decimal32(dist(rng))), static_cast<unsigned long>(0)) && BOOST_TEST_EQ(errno, ERANGE);
88+
BOOST_TEST_EQ(static_cast<unsigned long>(decimal32(dist(rng))), std::numeric_limits<unsigned long>::max()) && BOOST_TEST_EQ(errno, ERANGE);
8989

9090
errno = 0;
91-
BOOST_TEST_EQ(static_cast<unsigned long long>(decimal32(dist(rng))), static_cast<unsigned long long>(0)) && BOOST_TEST_EQ(errno, ERANGE);
91+
BOOST_TEST_EQ(static_cast<unsigned long long>(decimal32(dist(rng))), std::numeric_limits<unsigned long long>::max()) && BOOST_TEST_EQ(errno, ERANGE);
9292
}
9393

9494
template <typename T>

0 commit comments

Comments
 (0)