Why does strtod() with DBL_MIN give ERANGE?

433 Views Asked by At

This program calls strtod() on the first command line argument and prints the returned value:

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

int main(int argc, char **argv)
{
  errno = 0;
  double d = strtod(argv[1], NULL);
  int errno_sav = errno;

  printf("string = %s\n", argv[1]);
  printf("d = %.*e = %a\n", DBL_DIG + 2, d, d);
  printf("errno = %d\n", errno_sav);
  printf("DBL_MIN = %.*e = %a\n", DBL_DIG + 2, DBL_MIN, DBL_MIN);
  return 0;
}

When I run it on SUSE Linux Enterprise Server 11 SP2 or Linux Mint 17 Qiana with an argument of 2.22507385850720138309e-308, corresponding to the smallest representable1 double value (DBL_MIN), it gives the output I expect:

string = 2.22507385850720138309e-308
d = 2.22507385850720138e-308 = 0x1p-1022
errno = 0
DBL_MIN = 2.22507385850720138e-308 = 0x1p-1022

However, on SUSE Linux Enterprise Server 11 SP3 with the same argument2, errno is set to ERANGE:

string = 2.22507385850720138309e-308
d = 2.22507385850720138e-308 = 0x1p-1022
errno = 34
DBL_MIN = 2.22507385850720138e-308 = 0x1p-1022

Is the second behaviour valid, and if so why?


Footnotes:

  1. Since DBL_MIN is representable I think this question is different from "Odd behavior when converting C strings to/from doubles" where the value converted had underflowed.

  2. On SUSE if I run the program with an argument of 2.22507385850720138310e-308 then errno is set to 0. (And if I run the program with an argument of 0x1p-1022 then errno is also set to 0.)

2

There are 2 best solutions below

4
Simon Byrne On

The input value 2.22507385850720138309e-308 is smaller than the exact value of DBL_MIN (2-1022). A larger decimal expansion is:

2.225073858507201383090232717332404064219215980462331830553327416887204434813918...e-308 v.
2.22507385850720138309e-308 

So technically this results in underflow.

The C 1999 (7.20.1.3) states that in this case:

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.

0
chux - Reinstate Monica On

As I read the C spec, this is not correct behavior.

strtod() refers to 7.12.1:

7.12.1 Treatment of error conditions
The result underflows if the magnitude of the mathematical result is so small that the mathematical result cannot be represented, without extraordinary roundoff error, in an object of the specified type. C11 §7.12.1 6

Although, mathematically, input strings are less than DBL_MIN,...

text
2.22507385850720_138e-308       // later post
2.22507385850720_138309e-308    // original post
DBL_MIN
2.22507385850720_13830902...e-308 

... the conversion to (double) DBL_MIN is not with extraordinary roundoff error. Value should convert to DBL_MIN without setting errno.

For reference, other nearby double are shown.

2.22507385850720_08890245...e-308  nextafter(DBL_MIN, 0.0),if sub-normals allowed, else 0.0
2.22507385850720_13830902...e-308  DBL_MIN
2.22507385850720_18771558...e-308  nextafter(DBL_MIN, 1.0)

I suspect the underlying code simple performed the conversion with extend precision like long double and then tested if the long double result was less than DBL_MIN rather than considering the edge conditions.