Strtod underflow, возвращаемое значение!= 0
Здесь мой тестовый код:
errno = 0;
d = strtod("1.8011670033376514e-308", NULL);
С помощью этого кода я получаю d == 1.8011670033376514e-308
и errno == ERANGE
.
Из strtod (3):
Если правильное значение вызовет переполнение, возвращается плюс или минус HUGE_VAL
(HUGE_VALF
, HUGE_VALL
) (в соответствии с знаком значения), а ERANGE
хранится в errno
. Если правильное значение приведет к недопущению, возвращается ноль и ERANGE
сохраняется в errno
.
Итак, мне кажется, что либо errno
должен быть нулевым (без ошибок), либо d
должен быть равен нулю (underflow).
Это ошибка, или я чего-то не хватает? Это происходит для разных версий eglibc и gcc.
Ответы
Ответ 1
В §7.22.1.3 Функции strtod()
, strtof()
и strtold()
, стандарт C11 (ISO/IEC 9899: 2011) гласит:
Функции возвращают преобразованное значение, если оно есть. Если преобразование не может быть выполнено, ноль возвращается. Если правильное значение переполнения и округление по умолчанию действуют (7.12.1), плюс или минус HUGE_VAL
, HUGE_VALF
или HUGE_VALL
(согласно тип возврата и знак значения), а значение макроса ERANGE
сохраняется в errno
. Если результат заканчивается (7.12.1), функции возвращают значение, величина которого равна не более, чем наименьшее нормализованное положительное число в возвращаемом типе; будь то errno
получает значение ERANGE
определяется реализацией.
Стандарт также отмечает в §5.2.4.2.2. Характеристики плавающих типов, которые имеют номера с плавающей запятой IEC 60559 (IEEE 754), имеют предел:
DBL_MIN 2.2250738585072014E-308 // decimal constant
Так как 1.8011670033376514e-308 меньше, чем DBL_MIN
, вы получаете под нормальное число, а ERANGE
вполне уместно (но необязательно).
В Mac OS X 10.9.4 с GCC 4.9.1 следующая программа:
#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;
}
выводит результат:
34: Result too large
1.8011670033376514e-308
0x01 0x00 0x00 0x00 0xA8 0xF3 0x0C 0x00
Сообщение об ошибке иронически неверно - значение слишком мало, но вы не можете иметь все.
Ответ 2
Код ведет себя в соответствии с Спецификация OpenOS POSIX для strtod()
:
Если правильное значение приведет к нижнему потоку, значение, величина которого не превышает наименьшее нормализованное положительное число в возвращаемом типе, возвращается и errno устанавливается на [ERANGE].
Я бы сказал, что то, что вы видите, представляет собой подробную ошибку на странице руководства Linux.
Ответ 3
Если strtod()
возвращено ненулевое значение (это не +/- HUGE_VAL
), вызов преуспел (в соответствии с цитируемой вами страницей man).
Ссылаясь на страницу руководства для errno.h
:
Заголовочный файл <errno.h>
определяет целочисленную переменную errno
, которая устанавливается системными вызовами и некоторыми библиотечными функциями в случае чтобы указать, что пошло не так. Его ценность значительна только когда возвращаемое значение вызова указывает на ошибку (т.е. -1
из большинство системных вызовов; -1
или NULL
из большинства библиотечных функций); а функция, которой преуспевает, может изменить errno
.
Таким образом, вы можете проверить только errno
на ошибку, если возвращаемое значение вашей функции фактически возвращает значение, указывающее на ошибку.
Более полное объяснение errno
(и объяснение его отношения к strtod()
) можно найти на другом StackExchange.