lmi-commits
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[lmi-commits] [lmi] master 80e62ee 1/3: Calculate bourn_cast limit direc


From: Greg Chicares
Subject: [lmi-commits] [lmi] master 80e62ee 1/3: Calculate bourn_cast limit directly
Date: Wed, 19 Apr 2017 09:53:40 -0400 (EDT)

branch: master
commit 80e62ee0ca810cac927455d7e634a99585f5cebb
Author: Gregory W. Chicares <address@hidden>
Commit: Gregory W. Chicares <address@hidden>

    Calculate bourn_cast limit directly
    
    Using std::ldexp, calculated 2^N where N is the number of non-sign bits
    in the integral type's representation. An integral type's maximum is one
    less than this value, and its minimum is either zero or the negative of
    this value, decreased in magnitude by one if its representation is not
    two's complement.
    
    Replaced the peculiar original limit calculation with this one. Left the
    old one in place temporarily, along with code to validate the new limit
    against the old.
    
    The new calculation would seem to handle sign-and-magnitude and ones'
    complement integers correctly, although that hasn't been tested due to
    lack of hardware.
    
    The new calculation also has the virtue that the result of std::ldexp()
    is specified (by C99 Annex F) to be an infinity if it overflows, whereas
    the behavior of a C++ expression that overflows is undefined according
    to C++11 [5.7/6]. Such an infinite result causes all limit tests to
    pass, so an extra test for an infinite argument is necessary; this, and
    a new test for an unsigned type's zero minimum, changed the exceptions
    caught in the unit test, which was adjusted accordingly.
---
 bourn_cast.hpp      | 42 +++++++++++++++++++++++++++---------------
 bourn_cast_test.cpp |  9 ++++++---
 2 files changed, 33 insertions(+), 18 deletions(-)

diff --git a/bourn_cast.hpp b/bourn_cast.hpp
index 2d5d02f..ec7e70d 100644
--- a/bourn_cast.hpp
+++ b/bourn_cast.hpp
@@ -26,7 +26,7 @@
 
 #include "rtti_lmi.hpp"                 // lmi::TypeInfo [demangling]
 
-#include <cmath>                        // isinf(), isnan(), signbit()
+#include <cmath>                        // isinf(), isnan(), ldexp(), signbit()
 #include <limits>
 #include <sstream>
 #include <stdexcept>
@@ -140,14 +140,17 @@ inline To bourn_cast(From from, std::false_type, 
std::true_type)
 /// long long integers are not generally available, so it is not
 /// possible to test such logic today.
 ///
-/// Precondition: floating type has a two's complement representation.
-/// For ones' complement and sign-and-magnitude representations, the
-/// minimum might be handled in much the same way as the maximum,
-/// throwing if
-///  (argument < 0 && !to_traits::is_signed) ||
-///   argument <= minimum - 1  // 'minimum - 1' == 2^digits exactly
-/// instead of the intractable condition
-///   argument < minimum       // limit may exceed float precision
+/// The result of ldexp() is guaranteed to be representable. If it
+/// overflows, it returns HUGE_VAL[FL] according to C99 [7.12.1/4],
+/// which is a positive infinity [F.9/2] for an implementation that
+/// conforms to IEEE 754. It is okay if one or both of the limits
+/// tested is an infinity: e.g., if the integral type has a maximum
+/// too large for the floating type to represent finitely, then no
+/// finite floating argument is too large to cast to the integral
+/// type. Because radix is asserted upstream to be two for all types,
+/// there is no need to use scalbn() in place of ldexp(); and as long
+/// as the widest integer has less than (sizeof int) digits, there is
+/// no need here for scalbln().
 
 template<typename To, typename From>
 #if 201402L < __cplusplus
@@ -158,19 +161,28 @@ inline To bourn_cast(From from, std::true_type, 
std::false_type)
     using   to_traits = std::numeric_limits<To  >;
     using from_traits = std::numeric_limits<From>;
     static_assert(to_traits::is_integer && !from_traits::is_integer, "");
-    static_assert(to_traits::digits < from_traits::max_exponent, "");
+
+    static constexpr From limit = std::ldexp(From(1), to_traits::digits);
 
     static constexpr bool is_twos_complement(~To(0) == -To(1));
-    static_assert(is_twos_complement, "");
 
-    static From const volatile raw_max = From(to_traits::max());
-    static From const volatile adj_max = raw_max + From(1);
+    if(to_traits::digits < from_traits::max_exponent)
+        {
+        static From const volatile raw_max = From(to_traits::max());
+        static From const volatile adj_max = raw_max + From(1);
+        if(is_twos_complement && limit != adj_max)
+            throw std::runtime_error("Inconsistent limits.");
+        }
 
     if(std::isnan(from))
         throw std::runtime_error("Cannot cast NaN to integral.");
-    if(from < to_traits::lowest())
+    if(std::isinf(from))
+        throw std::runtime_error("Cannot cast infinite to integral.");
+    if(!to_traits::is_signed && from < 0)
+        throw std::runtime_error("Cannot cast negative to unsigned.");
+    if(from < -limit || from == -limit && !is_twos_complement)
         throw std::runtime_error("Cast would transgress lower limit.");
-    if(adj_max <= from)
+    if(limit <= from)
         throw std::runtime_error("Cast would transgress upper limit.");
     To const r = static_cast<To>(from);
     if(r != from)
diff --git a/bourn_cast_test.cpp b/bourn_cast_test.cpp
index 482a985..0c9d290 100644
--- a/bourn_cast_test.cpp
+++ b/bourn_cast_test.cpp
@@ -393,21 +393,24 @@ void test_conv_fpint(char const* file, int line)
     BOOST_TEST_THROW
         (bourn_cast<I>(f_traits::lowest())
         ,std::runtime_error
-        ,"Cast would transgress lower limit."
+        ,   (i_traits::is_signed
+            ? "Cast would transgress lower limit."
+            : "Cannot cast negative to unsigned."
+            )
         );
 
     // From +inf.
     BOOST_TEST_THROW
         (bourn_cast<I>(+f_traits::infinity())
         ,std::runtime_error
-        ,"Cast would transgress upper limit."
+        ,"Cannot cast infinite to integral."
         );
 
     // From -inf.
     BOOST_TEST_THROW
         (bourn_cast<I>(-f_traits::infinity())
         ,std::runtime_error
-        ,"Cast would transgress lower limit."
+        ,"Cannot cast infinite to integral."
         );
 
     // Otherwise disallowed.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]