lmi-commits
[Top][All Lists]
Advanced

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

[lmi-commits] [lmi] master 4fd17aa 2/3: Move signum()


From: Greg Chicares
Subject: [lmi-commits] [lmi] master 4fd17aa 2/3: Move signum()
Date: Tue, 13 Jul 2021 22:10:49 -0400 (EDT)

branch: master
commit 4fd17aacd46be618ccfb9c750051c7e15006b8df
Author: Gregory W. Chicares <gchicares@sbcglobal.net>
Commit: Gregory W. Chicares <gchicares@sbcglobal.net>

    Move signum()
    
    Deduplicated signum(); move it to 'math_functions.hpp' to favor reuse.
---
 math_functions.hpp      | 17 +++++++++++++++--
 math_functions_test.cpp | 47 +++++++++++++++++++++++++++++++++++++++++++++++
 tn_range.tpp            | 14 +-------------
 tn_range_test.cpp       | 24 ------------------------
 zero_test.cpp           | 14 +++-----------
 5 files changed, 66 insertions(+), 50 deletions(-)

diff --git a/math_functions.hpp b/math_functions.hpp
index 668c9eb..7e0f416 100644
--- a/math_functions.hpp
+++ b/math_functions.hpp
@@ -25,11 +25,11 @@
 #include "config.hpp"
 
 #include <algorithm>                    // max(), min(), transform()
-#include <cmath>                        // expm1l(), log1pl()
+#include <cmath>                        // expm1l(), log1pl(), signbit()
 #include <limits>
 #include <numeric>                      // partial_sum()
 #include <stdexcept>
-#include <type_traits>
+#include <type_traits>                  // /is_.*_v/
 #include <vector>
 
 // TODO ?? Write functions here for other refactorable uses of
@@ -112,6 +112,19 @@ inline T outward_quotient(T numerator, T denominator)
     return (0 < numerator == 0 < denominator) ? x + y : x - y;
 }
 
+/// Algebraic sign of argument.
+///
+/// Return value is of same type as argument, as for many members
+/// of std::numeric_limits. Thus, (t * signum(t)) is of type T,
+/// which would not always be the case if an int were returned.
+
+template<typename T>
+T signum(T t)
+{
+    static_assert(std::is_arithmetic_v<T>);
+    return (0 == t) ? 0 : std::signbit(t) ? -1 : 1;
+}
+
 // Actuarial functions.
 //
 // Some inputs are nonsense, like interest rates less than 100%.
diff --git a/math_functions_test.cpp b/math_functions_test.cpp
index 37061bb..dc5c440 100644
--- a/math_functions_test.cpp
+++ b/math_functions_test.cpp
@@ -286,6 +286,45 @@ void assay_speed()
     std::cout << "  10^-9 std        " << TimeAnAliquot(mete5) << '\n';
 }
 
+template<typename T>
+void test_signum(char const* file, int line)
+{
+    T const maxT = std::numeric_limits<T>::max();
+    T const minT = std::numeric_limits<T>::lowest();
+
+    INVOKE_LMI_TEST_EQUAL( 0, signum(T( 0)), file, line);
+    INVOKE_LMI_TEST_EQUAL( 1, signum(T( 1)), file, line);
+
+    INVOKE_LMI_TEST_EQUAL( 1, signum(maxT), file, line);
+
+    if(minT < 0)
+        {
+        // The left-hand side is cast to T to avoid gcc 'bool-compare'
+        // diagnostics. An 'is_bool' conditional wouldn't prevent the
+        // macros from being expanded. See:
+        //   https://lists.nongnu.org/archive/html/lmi/2017-05/msg00029.html
+        INVOKE_LMI_TEST_EQUAL(T(-1), signum(T(-1)), file, line);
+        INVOKE_LMI_TEST_EQUAL(T(-1), signum(minT ), file, line);
+        }
+
+    bool volatile is_iec559 = std::numeric_limits<T>::is_iec559;
+    bool volatile has_infinity = std::numeric_limits<T>::has_infinity;
+    if(is_iec559 && has_infinity)
+        {
+        T const infT = std::numeric_limits<T>::infinity();
+        INVOKE_LMI_TEST_EQUAL(-1, signum(-infT), file, line);
+        INVOKE_LMI_TEST_EQUAL( 1, signum( infT), file, line);
+        }
+
+    bool volatile has_quiet_NaN = std::numeric_limits<T>::has_quiet_NaN;
+    if(is_iec559 && has_quiet_NaN)
+        {
+        T const qnanT = std::numeric_limits<T>::quiet_NaN();
+        INVOKE_LMI_TEST_EQUAL(-1, signum(-qnanT), file, line);
+        INVOKE_LMI_TEST_EQUAL( 1, signum( qnanT), file, line);
+        }
+}
+
 int test_main(int, char*[])
 {
     double      smallnumD = std::numeric_limits<double     >::min();
@@ -364,6 +403,14 @@ int test_main(int, char*[])
 // Appropriately fails to compile due to static assertion:
 //  outward_quotient(1.0, 1.0);
 
+    test_signum<bool         >(__FILE__, __LINE__);
+    test_signum<signed char  >(__FILE__, __LINE__);
+    test_signum<unsigned char>(__FILE__, __LINE__);
+    test_signum<int          >(__FILE__, __LINE__);
+    test_signum<float        >(__FILE__, __LINE__);
+    test_signum<double       >(__FILE__, __LINE__);
+    test_signum<long double  >(__FILE__, __LINE__);
+
     // Actuarial functions.
 
     // Test with 1 == 'n'.
diff --git a/tn_range.tpp b/tn_range.tpp
index 844a111..ffe4fc9 100644
--- a/tn_range.tpp
+++ b/tn_range.tpp
@@ -22,6 +22,7 @@
 #include "tn_range.hpp"
 
 #include "alert.hpp"
+#include "math_functions.hpp"           // signum()
 #include "unwind.hpp"                   // scoped_unwind_toggler
 #include "value_cast.hpp"
 
@@ -104,19 +105,6 @@ namespace
         return strictly_between_extrema_tester<T>()(t);
     }
 
-    /// Algebraic sign of argument.
-    ///
-    /// Return value is of same type as argument, as for many members
-    /// of std::numeric_limits. Thus, (t * signum(t)) is of type T,
-    /// which would not always be the case if an int were returned.
-
-    template<typename T>
-    T signum(T t)
-    {
-        static_assert(std::is_arithmetic_v<T>);
-        return (0 == t) ? 0 : std::signbit(t) ? -1 : 1;
-    }
-
     /// Exact-integer determination for floating types.
     ///
     /// Motivation: Ascertaining whether a floating-point value lies
diff --git a/tn_range_test.cpp b/tn_range_test.cpp
index c12b9f1..1967694 100644
--- a/tn_range_test.cpp
+++ b/tn_range_test.cpp
@@ -143,11 +143,6 @@ void tn_range_test::test_auxiliary_functions(char const* 
file, int line)
         INVOKE_LMI_TEST( is_strictly_between_extrema<T>(1), file, line);
         }
 
-    INVOKE_LMI_TEST_EQUAL( 0, signum(T( 0)), file, line);
-    INVOKE_LMI_TEST_EQUAL( 1, signum(T( 1)), file, line);
-
-    INVOKE_LMI_TEST_EQUAL( 1, signum(maxT), file, line);
-
     INVOKE_LMI_TEST_EQUAL(true , is_exact_integer(T( 0)), file, line);
     INVOKE_LMI_TEST_EQUAL(true , is_exact_integer(T( 1)), file, line);
 
@@ -157,8 +152,6 @@ void tn_range_test::test_auxiliary_functions(char const* 
file, int line)
         // diagnostics. An 'is_bool' conditional wouldn't prevent the
         // macros from being expanded. See:
         //   https://lists.nongnu.org/archive/html/lmi/2017-05/msg00029.html
-        INVOKE_LMI_TEST_EQUAL(T(-1), signum(T(-1)), file, line);
-        INVOKE_LMI_TEST_EQUAL(T(-1), signum(minT ), file, line);
         INVOKE_LMI_TEST_EQUAL(true , is_exact_integer(T(-1)), file, line);
         }
 
@@ -170,23 +163,6 @@ void tn_range_test::test_auxiliary_functions(char const* 
file, int line)
         INVOKE_LMI_TEST_EQUAL(false, is_exact_integer(T( 0.5)), file, line);
         INVOKE_LMI_TEST_EQUAL(false, is_exact_integer(T(1.07)), file, line);
         }
-
-    bool volatile is_iec559 = std::numeric_limits<T>::is_iec559;
-    bool volatile has_infinity = std::numeric_limits<T>::has_infinity;
-    if(is_iec559 && has_infinity)
-        {
-        T const infT = std::numeric_limits<T>::infinity();
-        INVOKE_LMI_TEST_EQUAL(-1, signum(-infT), file, line);
-        INVOKE_LMI_TEST_EQUAL( 1, signum( infT), file, line);
-        }
-
-    bool volatile has_quiet_NaN = std::numeric_limits<T>::has_quiet_NaN;
-    if(is_iec559 && has_quiet_NaN)
-        {
-        T const qnanT = std::numeric_limits<T>::quiet_NaN();
-        INVOKE_LMI_TEST_EQUAL(-1, signum(-qnanT), file, line);
-        INVOKE_LMI_TEST_EQUAL( 1, signum( qnanT), file, line);
-        }
 }
 
 template<typename T>
diff --git a/zero_test.cpp b/zero_test.cpp
index 5401898..4569ea7 100644
--- a/zero_test.cpp
+++ b/zero_test.cpp
@@ -24,14 +24,14 @@
 #include "zero.hpp"
 
 #include "materially_equal.hpp"
+#include "math_functions.hpp"           // signum()
 #include "miscellany.hpp"               // stifle_warning_for_unused_variable()
 #include "test_tools.hpp"
 
 #include <cfloat>                       // DECIMAL_DIG
-#include <cmath>                        // exp(), fabs(), log(), pow(), 
signbit()
+#include <cmath>                        // exp(), fabs(), log(), pow()
 #include <limits>
 #include <sstream>
-#include <type_traits>                  // is_arithmetic_v
 
 namespace
 {
@@ -286,8 +286,7 @@ double eq_2_1(double x)
 
 double signum_offset(double d)
 {
-    double z = d + 1.0 / 3.0;
-    return (0.0 == z) ? 0.0 : std::signbit(z) ? -1.0 : 1.0;
+    return signum(d + 1.0 / 3.0);
 }
 
 // This problem once arose in a unit test for irr calculations.
@@ -340,13 +339,6 @@ void test_fundamentals()
     LMI_TEST(root_not_bracketed == r.validity);
 }
 
-template<typename T>
-inline T signum(T t)
-{
-    static_assert(std::is_arithmetic_v<T>);
-    return (0 == t) ? 0 : std::signbit(t) ? -1 : 1;
-}
-
 /// A function whose value almost everywhere in (-1.0e100, 1.0e100)
 /// is a "signed" NaN. It's dubious to think of NaNs as possessing
 /// signedness, yet they do have a sign bit.



reply via email to

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