1

With GCC 9.1, when calling std::numeric_limits's functions with floating-point types, they return 0 in most cases.

This happens in a project I'm working on, and there is no issue with MSVC, GCC 8.3 or Clang 8.0. <double>::epsilon() sometimes has a correct value, but when called from other files it may evaluate to 0 as well.

// Commented values at the end of the lines are the values given by the debugger
// Making the variable constexpr doesn't change their values

auto intMax = std::numeric_limits<int>::max(); // {int} 2147483647

auto floatMax     = std::numeric_limits<float>::max();     // {float} 0
auto floatEpsilon = std::numeric_limits<float>::epsilon(); // {float} 0
auto floatMin     = std::numeric_limits<float>::min();     // {float} 0
auto floatLowest  = std::numeric_limits<float>::lowest();  // {float} -0

auto doubleMax     = std::numeric_limits<double>::max();     // {double} 0
auto doubleEpsilon = std::numeric_limits<double>::epsilon(); // {double} 2.2204460492503131e-16
auto doubleMin     = std::numeric_limits<double>::min();     // {double} 0
auto doubleLowest  = std::numeric_limits<double>::lowest();  // {double} -0

std::cout << std::setprecision(10) << std::fixed
          << "Max int        = " << std::numeric_limits<int>::max()
          << "\n"
          << "\nMax float      = " << std::numeric_limits<float>::max()
          << "\nEpsilon float  = " << std::numeric_limits<float>::epsilon()
          << "\nMin float      = " << std::numeric_limits<float>::min()
          << "\nLowest float   = " << std::numeric_limits<float>::lowest()
          << "\n"
          << "\nMax double     = " << std::numeric_limits<double>::max()
          << "\nEpsilon double = " << std::numeric_limits<double>::epsilon()
          << "\nMin double     = " << std::numeric_limits<double>::min()
          << "\nLowest double  = " << std::numeric_limits<double>::lowest() << std::endl;

(The <int>::max() is left as a reference here)

Result in an independent file (correct values):

Max int        = 2147483647

Max float      = 3.40282e+38
Epsilon float  = 1.19209e-07
Min float      = 1.17549e-38
Lowest float   = -3.40282e+38

Max double     = 1.79769e+308
Epsilon double = 2.22045e-16
Min double     = 2.22507e-308
Lowest double  = -1.79769e+308

Result in the project:

Max int        = 2147483647

Max float      = 0
Epsilon float  = 0
Min float      = 0
Lowest float   = -0

Max double     = 0
Epsilon double = 2.22045e-16
Min double     = 0
Lowest double  = -0

When compiling a dedicated file indepentenly, the values are correct: the issue is then not from GCC (as I expected anyway), but most likely from the project's configuration.

EDIT: compiling a project's file (on which this issue occurs at the moment) independently gives correct results as well. With gcc -dM -E, the __DBL_MAX__ is defined to double(1.79769313486231570814527423731704357e+308L).

The __DBL_MAX__ value is defined, code surrounded by an ifdef is executed:

#ifdef __DBL_MAX__
#pragma message "__DBL_MAX__ defined"
#endif

/*
note: #pragma message: __DBL_MAX__ defined
   40 | #pragma message "__DBL_MAX__ defined"
      |                 ^~~~~~~~~~~~~~~~~~~~~
*/

GDB gives the same exact value, so no problem on the output part. Outputting XXX_YYY or __XXX_YYY__ gives the same results, since numeric_limits' functions call them anyway.

To be 100% clear: std::numeric_limits<double>::max() == 0 returns true, so there is no problem on the output part. It is just left here as a reference.

What could be the reason(s) GCC produces such behavior? Aren't __XXX_YYY__ built-in values anyway? How can they possibly hold 0?

Razakhel
  • 732
  • 13
  • 34
  • Are you compiling your project with compiler flags that break the floating point math? – Giovanni Cerretani Jul 12 '19 at 14:22
  • None in particular, just `-O3 -DNDEBUG -Wall -Wextra -Werror -Wno-unknown-pragmas` I doubt this would have been different with 8.3, except if 9.1 changed some default behaviors. I can try with some enabled though – Razakhel Jul 12 '19 at 14:38
  • No difference whatsoever with `-ffloat-store -ffast-math` – Razakhel Jul 12 '19 at 14:50
  • You may have a look at the output of `gcc -dM -E - < /dev/null` – Giovanni Cerretani Jul 12 '19 at 15:23
  • `__DBL_MAX__` is well defined with this command (`#define __DBL_MAX__ ((double)1.79769313486231570814527423731704357e+308L)`). It will be harder to have check in the project (which uses CMake) since it doesn't output anything when I pass -E, except many `c++: warning: file.cpp.o: linker input file unused because linking not done` lines – Razakhel Jul 15 '19 at 07:29
  • I've tried running GCC with `-save-temps` but no substring "`DBL_MAX`" is found in any of the .ii files produced - (As a side note, code surrounded with `#ifdef __DBL_MAX__` is executed, so there's not much doubt about having it redefined somewhere) – Razakhel Jul 15 '19 at 09:09
  • did you `std::setprecision`? Can you post full MCVE? Can you try with `std::fixed`? – KamilCuk Jul 15 '19 at 10:10
  • Gives the same result with `std::setprecision(10)`, which is logical since the debugger gives me the same values as said in my post. This is the full MCVE, the problem happens with this code only, simply most likely in a particular setup so I don't expect it to be reproducible. The `std::fixed` doesn't change anything, the values are plain `0.0000000000` – Razakhel Jul 15 '19 at 10:23
  • Well, then, we need to delve into that particular setup and environment. Maybe, what if you plain did `std::cout << ((double)1.79769313486231570814527423731704357e+308L)` ? You could try with `+307`, `+306`, ... maybe it has some rounding limit? Does it print floates and doubles correctly at all? Do you use some strange standard C++ implementation? What is your architecture/platform? What are your compiler options? Can you post the full compilation line? Can you print the C macros? `std::cout << __FLT_MAX__` ? `printf("%f", __FLT_MAX__)` ? It's strange debugger gives you zeros. – KamilCuk Jul 15 '19 at 11:10
  • do a verbose make, capture the actual compilation command, see what it does. – n. m. could be an AI Jul 15 '19 at 11:22
  • @KamilCuk The direct output of the value works well with `+308`. The output is perfectly correct with other values as you can see with the epsilon, the problem is clearly on the `__DBL_MAX__` and such values. All the code is standard C++ (17), compiling on both Windows & Linux, with MSVC/GCC/Clang. My platform is Arch Linux, 64 bits, running through WSL. The C macros give the same result, as I said in the last part of my post. The original problem was that `0 == std::numeric_limits::max()` returned true, so really no problem on the output part – Razakhel Jul 15 '19 at 12:06
  • So your guess is that `__DBL_MAX__` macro is overwritten? You can print it's value at compile time, with simple `#define STRING(x) #x` `#define XSTRING(x) STRING(x)` and `#pragma message "__DBL_MAX__=" XSTRING(__DBL_MAX__)`. What will it output? Maybe inspect other constants in that way? [godbolt](https://godbolt.org/z/xkgYQ9). Maybe just try with `#ifdef __DBL_MAX` `#error` ? If `__DBL_MAX__` value is missing, maybe you are not using `gcc` as your compiler? `#ifdef __GNUC__` `#error` ? – KamilCuk Jul 15 '19 at 12:09
  • I can't think of anything else at the moment, I thought there could have been a mix between stdlib headers but I've checked that there's no such thing. I will investigate the command line's options as you & n.m. suggested, but I'm clearly out of ideas. EDIT (again): the compiler tells `#pragma message: __DBL_MAX__=double(0.0L)`, but the linter (CLion) gives me the correct value. 2D EDIT: `__DBL_MAX__` is defined, I told it in a previous comment. I'll add it in the original post – Razakhel Jul 15 '19 at 12:11
  • Alright, so it comes from our precompiled headers. Removing all PCHs works, I've tried from a single file by removing the PCH-related part from the command line options (thanks for pointing this out!), then globally deactivated them. There's nothing related to limits in the included files, so there may be a bug on cotire's part... I've found another thread which seemingly shows problems with cotire and limits, though not the same as mine: https://stackoverflow.com/questions/46734274/c-cmake-build-with-cotire-and-gtest-error-with-float-h that may be related in some way – Razakhel Jul 15 '19 at 14:37

1 Answers1

1

So the error came from (at least indirectly) the precompiled headers, which we use with cotire for CMake.

Every file included by those headers, manually included individually in the "problematic" files, did not reproduce the issue.

Although a bit weird, I don't see any viable explanation other than cotire for now. It has apparently already been reported to have problems with limits, as per this thread: C++ Cmake build with cotire and gtest - error with float.h

The temporary solution will then be to disable precompiled headers with GCC 9.1+. If anyone has had the same kind of issue, feel free to add comments or answers if you do know why and how this happens.

Thanks to Giovanni, Kamil and n.m in the comments who led me in the right direction!

Razakhel
  • 732
  • 13
  • 34