Here's my test code:
errno = 0;
d = strtod("1.8011670033376514e-308", NULL);
With this code, I get d == 1.8011670033376514e-308
and errno == ERANGE
.
From strtod(3):
If the correct value would cause overflow, plus or minus
HUGE_VAL
(HUGE_VALF
,HUGE_VALL
) is returned (according to the sign of the value), andERANGE
is stored inerrno
. If the correct value would cause underflow, zero is returned andERANGE
is stored inerrno
.
So, it seems to me that either errno
should be zero (no error) or d
should be zero (underflow).
Is this a bug, or am I missing something? This happens for many different versions of eglibc and gcc.
In §7.22.1.3 The strtod()
, strtof()
and strtold()
functions, the C11 standard (ISO/IEC 9899:2011) says:
The functions return the converted value, if any. If no conversion could be performed, zero is returned. If the correct value overflows and default rounding is in effect (7.12.1), plus or minus
HUGE_VAL
,HUGE_VALF
, orHUGE_VALL
is returned (according to the return type and sign of the value), and the value of the macroERANGE
is stored inerrno
. 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; whethererrno
acquires the valueERANGE
is implementation-defined.
The standard also notes in §5.2.4.2.2 Characteristics of floating types that IEC 60559 (IEEE 754) floating point numbers have the limit:
DBL_MIN 2.2250738585072014E-308 // decimal constant
Since 1.8011670033376514e-308 is smaller than DBL_MIN
, you get a sub-normal number, and ERANGE
is quite appropriate (but optional).
On Mac OS X 10.9.4 with GCC 4.9.1, the following program:
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char *end;
errno = 0;
double d = strtod("1.8011670033376514e-308", &end);
if (errno != 0)
{
int errnum = errno;
printf("%d: %s\n", errnum, strerror(errnum));
}
printf("%24.16e\n", d);
unsigned char *p = (unsigned char *)&d;
const char *pad = "";
for (size_t i = 0; i < sizeof(double); i++)
{
printf("%s0x%.2X", pad, *p++);
pad = " ";
}
putchar('\n');
return 0;
}
produces the output:
34: Result too large
1.8011670033376514e-308
0x01 0x00 0x00 0x00 0xA8 0xF3 0x0C 0x00
The error message is ironically wrong — the value is too small — but you can't have everything.