4

I've recently come across the problem that visual-c++ does not seem to be IEEE 754 compliant, but instead uses subnormal representation. That is, double precision floats in it do not have the usual representation of 1 sign bit, 11 exponent bits and 52 explicitly stored significant decimal bits, see below.

As gcc and clang are however compliant and consistent cross-platform behaviour is highly desired I would like to know whether it is possible to force visual-c++ to use the normal representation. Alternatively making gcc and clang use the subnormal representation would of course also solve the problem.

The issue of the different double representations can be reproduced in visual-c++, gcc and clang using the following code:

#include <iostream>
#include <string>

int main()
{
    try {
        std::stod("8.0975711886543594e-324");
        std::cout << "Subnormal representation.";
    } catch (std::exception& e) {
        std::cout << "Normal representation.";
    }
    return 0;
}

Is a representation specification to produce consitent behaviour in all three cases at all possible?

Edit: As geza pointed out, this appears a problem in the different implementations of std::stod, which would then make the question if there is any way to make std::stod behave consistently without having to implement a seperate wrapper for it.

abcalphabet
  • 1,158
  • 3
  • 16
  • 31
  • 1
    Here, the difference lies in `std::stod`, doesn't it? GCC and clang throw out_of_range for subnormal numbers (which is weird, if you ask me), while msvc doesn't. I even don't understand, why they throw out_of_range for a small number which is truncated to zero. It's definitely not out_of_range. There should be a separate exception for this case. – geza Nov 19 '18 at 13:39
  • Relevant: https://stackoverflow.com/questions/48086830/stdstod-throws-out-of-range-error-for-a-string-that-should-be-valid – geza Nov 19 '18 at 13:45
  • 1
    `std::stod` is badly designed. `strtod` can report whether the ERANGE means overflow or underflow. `std::stod` cannot. – geza Nov 19 '18 at 13:48
  • @geza Thanks, edited the post to reflect that its `std::stod`, maybe there's some standard way around the design – abcalphabet Nov 19 '18 at 13:49
  • Yes, use `strtod`, which doesn't have this limitation. It behaves not as documented for subnormal numbers: it will return the subnormal value (so, contrary to the doc, it returns a non-zero value, yet sets errno=ERANGE). Which is good for us, because you can get the number you want. – geza Nov 19 '18 at 13:53
  • @geza Yup, did the trick perfectly, all I needed was `std::strtod(cstr, nullptr)`, thanks! If you want to make it into an answer I'd be happy to accept it – abcalphabet Nov 19 '18 at 14:46

1 Answers1

3

Unfortunately, std::stod is badly designed, because it is not possible to determine what caused the std::out_of_range exception.

I'd suggest you to use strtod instead. While is not specified in the standard what this function should do for subnormal numbers, it behaves well for subnormal numbers usually (it means that it returns subnormal numbers). The benefit of this function is that it returns a meaningful result for out-of-range situations, so it is possible to determine the cause of out-of-range.

If you want to handle out of range situations, you'll need to check errno for ERANGE. Note, that if a subnormal/zero number is a result, then maybe errno will be set to ERANGE, which you should ignore (you can check this out with fpclassify).

So the logic is something like this:

double r = strtod(string, &end);
// here, check for end to know about invalid strings

if (errno==ERANGE) { // out-of-range (overflow, underflow)
    int c = fpclassify(r);
    if (c!=FP_SUBNORMAL&&c!=FP_ZERO) { // let's filter out underflow cases
        // "real" out of range handling here, just overflow
    }
}
geza
  • 28,403
  • 6
  • 61
  • 135