[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.