lmi-commits
[Top][All Lists]
Advanced

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

[lmi-commits] [lmi] master 7d0257a3 2/2: Expose left shift of a negative


From: Greg Chicares
Subject: [lmi-commits] [lmi] master 7d0257a3 2/2: Expose left shift of a negative value in fdlibm
Date: Thu, 16 Jun 2022 16:34:39 -0400 (EDT)

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

    Expose left shift of a negative value in fdlibm
    
    Build this unit test using x86_64-pc-linux-gnu, with "build_type=ubsan",
    and with these variables:
      UBSAN_OPTIONS
      ASAN_OPTIONS
    defined and exported as in 'gwc/.zshrc', to see:
    
    /opt/lmi/src/lmi/fdlibm_expm1.c:242:13: runtime error: left shift of 
negative value -2
        #0 0x5584d6736063 in fdlibm_expm1 /opt/lmi/src/lmi/fdlibm_expm1.c:242
        #1 0x5584d67a05e6 in test_expm1_log1p() 
/opt/lmi/src/lmi/math_functions_test.cpp:660
        #2 0x5584d67b2b6b in test_main(int, char**) 
/opt/lmi/src/lmi/math_functions_test.cpp:848
        #3 0x5584d67b3396 in cpp_main(int, char**) 
/opt/lmi/src/lmi/test_main.cpp:171
    
    Before this change, the UB was detected, but the call stack didn't
    identify which of millions of loop iterations triggered it. Now the
    triggering example is isolated and discussed in some detail.
    
    By default, this unit test wouldn't be built with UBSan because it's
    named in $(excluded_unit_test_targets), but it is possible to run it
    manually as above.
---
 math_functions_test.cpp | 31 +++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/math_functions_test.cpp b/math_functions_test.cpp
index be34b7a1..d67ab439 100644
--- a/math_functions_test.cpp
+++ b/math_functions_test.cpp
@@ -634,6 +634,37 @@ void test_expm1_log1p()
     // 0.025940753546620676 = 3F9A903680771FB0  fdlibm
     // 0.025940753546620673 = 3F9A903680771FAF  glibc
 
+    // Monthly equivalent of a -0.999999 = -99.9999% interest rate.
+    // The transformation is, generally:
+    //   (1+i)^n - 1 <-> expm1(log1p(i) * n)
+    // Substituting i = -0.999999 and n = 1/12 :
+    //   (1-0.999999)^(1/12) - 1 <-> expm1(log1p(-0.999999) / 12.0)
+    // High-precision values:
+    //   https://www.wolframalpha.com/input?i=log1p(-0.999999)/12
+    //   -1.151292546497022842008995727342182103800550744314386488016
+    //   https://www.wolframalpha.com/input?i=expm1(log1p(-0.999999)/12)
+    //   -0.683772233983162066800110645556728146628044486067478317314
+    //   https://www.wolframalpha.com/input?i=(1-0.999999)^(1/12)-1
+    //   -0.683772233983162066800110645556728146628044486067478317314
+    // In this ill-conditioned case, we get something like eleven
+    // digits of precision--an error of about one million ulp.
+    double const i0 = std::log1p(-0.999999) / 12.0;
+    LMI_TEST(materially_equal( -1.1512925464970228, i0, 1.0e-11));
+    double const i1 = std::expm1(-1.1512925464970228);
+    LMI_TEST(materially_equal(-0.68377223398240425, i1, 1.0e-11));
+    // (Optionally, to see the platform-dependent actual values:
+    // i0 = -1.15129254649462642312585 i1 = -0.68377223398316200331237 
MinGW-w32
+    // i0 = -1.15129254649462642312585 i1 = -0.68377223398316200331237 glibc
+    // [which are curiously identical], uncomment the next line.)
+//  std::cout << "i0 = " << i0 << " i1 = " << i1 << std::endl;
+    // Worse, we have UB--which UBSan detects when we build fdlibm
+    // ourselves, though not when we use the same code via glibc:
+    double const i2 = lmi::log1p(-0.999999) / 12.0;
+    LMI_TEST(materially_equal( -1.1512925464970228, i2, 1.0e-11));
+    // fdlibm_expm1.c:242:13: runtime error: left shift of negative value -2
+    double const i3 = lmi::expm1(-1.1512925464970228);
+    LMI_TEST(materially_equal(-0.68377223398240425, i3, 1.0e-11));
+
     constexpr double inf {std::numeric_limits<double>::infinity()};
     constexpr double big {std::numeric_limits<double>::max()};
     // Absolute value of relative error.



reply via email to

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