Odd behavior when converting C strings to/from doubles
Asked Answered
L

2

7

I'm having trouble understanding C's rules for what precision to assume when printing doubles, or when converting strings to doubles. The following program should illustrate my point:

#include <errno.h>
#include <float.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv) {
    double x, y;
    const char *s = "1e-310";

    /* Should print zero */
    x = DBL_MIN/100.;
    printf("DBL_MIN = %e, x = %e\n", DBL_MIN, x);

    /* Trying to read in floating point number smaller than DBL_MIN gives an error */
    y = strtod(s, NULL);
    if(errno != 0)
        printf("  Error converting '%s': %s\n", s, strerror(errno));
    printf("y = %e\n", y);

    return 0;
}

The output I get when I compile and run this program (on a Core 2 Duo with gcc 4.5.2) is:

DBL_MIN = 2.225074e-308, x = 2.225074e-310
  Error converting '1e-310': Numerical result out of range
y = 1.000000e-310

My questions are:

  1. Why is x printed as a nonzero number? I know compilers sometimes promote doubles to higher precision types for the purposes of computation, but shouldn't printf treat x as a 64-bit double?
  2. If the C library is secretly using extended precision floating point numbers, why does strtod set errno when trying to convert these small numbers? And why does it produce the correct result anyway?
  3. Is this behavior just a bug, a result of my particular hardware and development environment? (Unfortunately I'm not able to test on other platforms at the moment.)

Thanks for any help you can give. I will try to clarify the issue as I get feedback.

Littles answered 9/1, 2012 at 20:14 Comment(2)
For one: DBL_MIN = 2.225074e-308 doesn't make much sense as the minimum IEEE DP value is 4.94066e-324. This explains why dividing by 100 still works correctly. But the question is why DBL_MIN is not 4.94066e-324.Pomerleau
Clarified below: DBL_MIN is the smallest normalised value.Littles
C
8
  1. Because of the existence of denormal numbers in the IEEE-754 standard. DBL_MIN is the smallest normalised value.

  2. Because the standard says so (C99 7.20.1.3):

    If the result underflows (7.12.1), the functions return a value whose magnitude is no greater than the smallest normalized positive number in the return type; whether errno acquires the value ERANGE is implementation-defined.

    Returning the "correct" value (i.e. 1e-310) obeys the above constraint.

  3. So not a bug. This is technically platform-dependent, because the C standard(s) place no requirements on the existence or behaviour of denormal numbers (AFAIK).

Coverup answered 9/1, 2012 at 20:23 Comment(1)
Thank you, this is very helpful information. I kind of wish strtod wouldn't flag an error in this case, but I can see the rationale. I'll just work around it.Littles
R
7

Here is what the standard says for strtod underflow (C99, 7.20.1.3p10)

"If the result underflows (7.12.1), the functions return a value whose magnitude is no greater than the smallest normalized positive number in the return type; whether errno acquires the value ERANGE is implementation-defined."

Regarding ERANGE on strtod underflow, here is what glibc says

"When underflow occurs, the underflow exception is raised, and zero (appropriately signed) is returned. errno may be set to ERANGE, but this is not guaranteed."

http://www.gnu.org/savannah-checkouts/gnu/libc/manual/html_node/Math-Error-Reporting.html

(Note that this page is explicitly linked on glibc strtod page "Parsing of Floats": http://www.gnu.org/savannah-checkouts/gnu/libc/manual/html_node/Parsing-of-Floats.html

Rokach answered 9/1, 2012 at 20:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.