Bug 414870

Summary: std::frexp(long double) broken under valgrind.
Product: [Developer tools] valgrind Reporter: John Maddock <john>
Component: memcheckAssignee: Julian Seward <jseward>
Status: RESOLVED NOT A BUG    
Severity: normal CC: tom
Priority: NOR    
Version First Reported In: 3.15 SVN   
Target Milestone: ---   
Platform: Other   
OS: Linux   
Latest Commit: Version Fixed/Implemented In:
Sentry Crash Report:

Description John Maddock 2019-12-05 18:16:17 UTC
SUMMARY

The invariants of std::frexp(long double) are broken when the argument is > DOUBLE_MAX on Ubuntu 19.10, but not on Ubuntu 18.04.


STEPS TO REPRODUCE

Compile the following program with g++ -g:

#include <cmath>
#include <limits>

int main()
{
  long double f = std::numeric_limits<double>::max();
  int e;
  assert(std::frexp(f, &e) < 1);  // passes
  f *= 2;
  if(!std::isinf(f))              // passes, f is still finite
    assert(std::frexp(f, &e) < 1);  // fails
  return 0;
}

Then run valgrind ./a.out

OBSERVED RESULT

==3249== Memcheck, a memory error detector
==3249== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==3249== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==3249== Command: ./a.out
==3249== 
a.out: boost/t.cpp:12: int main(): Assertion `std::frexp(f, &e) < 1' failed.
==3249== 
==3249== Process terminating with default action of signal 6 (SIGABRT)
==3249==    at 0x4BDD3EB: raise (raise.c:51)
==3249==    by 0x4BBC898: abort (abort.c:79)
==3249==    by 0x4BBC768: __assert_fail_base.cold (assert.c:92)
==3249==    by 0x4BCE005: __assert_fail (assert.c:101)
==3249==    by 0x109284: main (t.cpp:12)
==3249== 
==3249== HEAP SUMMARY:
==3249==     in use at exit: 0 bytes in 0 blocks
==3249==   total heap usage: 3 allocs, 3 frees, 72,882 bytes allocated
==3249== 
==3249== All heap blocks were freed -- no leaks are possible
==3249== 
==3249== For lists of detected and suppressed errors, rerun with: -s
==3249== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Aborted (core dumped)

EXPECTED RESULT

There is no assert when running outside valgrind, or on ubuntu-18 (not sure which valgrind version is installed there).

This is a reduced test case from a Boost.Multiprecision bug report: https://github.com/boostorg/multiprecision/issues/174

SOFTWARE/OS VERSIONS
Comment 1 Tom Hughes 2019-12-05 19:10:53 UTC
Please see http://valgrind.org/docs/manual/manual-core.html#manual-core.limits for details of limitations on valgrind's floating point support.

In particular please note that there is no support for x86-32 80 bit floating point.
Comment 2 John Maddock 2019-12-06 11:10:18 UTC
I appreciate long double is unsupported - however this did actually work.  The critical change appears to be GCC-9 vc GCC-8.
Comment 3 Tom Hughes 2019-12-06 11:15:30 UTC
Well sure gcc may have generated different code.

The point is that once you use long double on x86-32 there is no guarantee you will get the same results under valgrind as when running natively. You might, but you might not.

Actually that can be true even with double because gcc by default generates code that can keep values on the x87 stack between operations, so 80 bit precision is used until gcc needs to move the value back to memory.

Using -ffloat-store can reduce that by forcing it to move stuff back to memory more often, or just use -mfpmath=sse to make it use SSE for floating point where you will get standard IEEE semantics.
Comment 4 Julian Seward 2019-12-28 19:18:26 UTC
To echo Tom .. unfortunately, we just don't have the resources to 
support 80 bit floating point properly, compared to other problems
that need fixing.  And to be honest, code that relies on it is pretty
non-standard -- and rare, and unportable.  Your least-worst option
is to find a way to make your world work just using standard IEEE754
64-bit double precision.  Sorry.