[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[lmi-commits] [lmi] master 4ef9a222 4/5: Calculate std::abs(INT_MIN) wit
From: |
Greg Chicares |
Subject: |
[lmi-commits] [lmi] master 4ef9a222 4/5: Calculate std::abs(INT_MIN) without UB |
Date: |
Thu, 2 Jun 2022 20:13:04 -0400 (EDT) |
branch: master
commit 4ef9a22264c69043f7c7228c57aafb8fcd22ad68
Author: Gregory W. Chicares <gchicares@sbcglobal.net>
Commit: Gregory W. Chicares <gchicares@sbcglobal.net>
Calculate std::abs(INT_MIN) without UB
Evidence for this comment:
+/// The return type is specified explicitly because with 'auto'
+/// gcc deduces it incorrectly in the accompanying unit test.
/opt/lmi/src/lmi/math_functions_test.cpp:567:32: \
error: conversion from ‘int’ to ‘uint16_t’ \
{aka ‘short unsigned int’} may change value [-Werror=conversion]
567 | std::uint16_t u = u_abs(j);
| ~~~~~^~~
---
math_functions.hpp | 22 +++++++++++++++++++++-
math_functions_test.cpp | 19 +++++++++++++++++++
2 files changed, 40 insertions(+), 1 deletion(-)
diff --git a/math_functions.hpp b/math_functions.hpp
index dcf6cc96..a2a7c208 100644
--- a/math_functions.hpp
+++ b/math_functions.hpp
@@ -29,7 +29,7 @@
#include <limits>
#include <numeric> // midpoint(), partial_sum()
#include <stdexcept>
-#include <type_traits> // /is_.*/
+#include <type_traits> // is_, has_, make_ ...
#include <vector>
namespace lmi
@@ -129,6 +129,26 @@ T signum(T t)
return (0 == t) ? 0 : std::signbit(t) ? -1 : 1;
}
+/// Unsigned |signed value|. Needed because std::abs(INT_MIN) is UB.
+///
+/// Asserts that both integer types have no padding, to rule out the
+/// UINT_MAX == INT_MAX == -(INT_MIN+1)
+/// case that Daniel Fischer points out somewhere on the web.
+///
+/// The return type is specified explicitly because with 'auto'
+/// gcc deduces it incorrectly in the accompanying unit test.
+
+template<typename T>
+constexpr std::make_unsigned_t<T> u_abs(T t)
+{
+ static_assert(std::is_integral_v<T>);
+ static_assert(std::is_signed_v<T>);
+ using U = std::make_unsigned_t<T>;
+ static_assert(std::has_unique_object_representations_v<T>);
+ static_assert(std::has_unique_object_representations_v<U>);
+ return (t < 0) ? -static_cast<U>(t) : static_cast<U>(t);
+}
+
// 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 f291fd8b..08eed487 100644
--- a/math_functions_test.cpp
+++ b/math_functions_test.cpp
@@ -33,6 +33,7 @@
#include <algorithm> // min()
#include <cfloat> // DBL_EPSILON
#include <cmath> // fabs(), isnan(), pow()
+#include <cstdint>
#include <iomanip>
#include <limits>
#include <type_traits>
@@ -559,6 +560,22 @@ void test_signum(char const* file, int line)
}
}
+void test_u_abs()
+{
+ LMI_TEST_EQUAL(9223372036854775808ULL, u_abs(INT64_MIN));
+
+ LMI_TEST_EQUAL(128, u_abs(INT8_MIN));
+
+ for(std::int16_t j = INT8_MIN; j < INT8_MAX; ++j)
+ {
+ std::uint16_t u = u_abs(j);
+ if(0 <= j)
+ {LMI_TEST_EQUAL(u, j);}
+ if(j <= 0)
+ {LMI_TEST_EQUAL(u, -j);}
+ }
+}
+
/// Test fdlibm expm1() and log1p().
///
/// Testing for exact floating-point equality seems to be a patent
@@ -781,6 +798,8 @@ int test_main(int, char*[])
test_signum<double >(__FILE__, __LINE__);
test_signum<long double >(__FILE__, __LINE__);
+ test_u_abs();
+
test_expm1_log1p();
sample_results();