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 должно быть ноль (недостаточное значение).

Это ошибка, или я что-то упустил? Это происходит для многих разных версий eglibc и gcc.

3 ответа

Решение

В §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 стандарта также отмечается, что для чисел с плавающей запятой в МЭК 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

Сообщение об ошибке по иронии судьбы неверно - значение слишком мало - но вы не можете иметь все.

Если strtod() вернул ненулевое значение (это не +/- HUGE_VAL) вызов успешно завершен (в соответствии с указанной вами страницей руководства).

Ссылаясь на справочную страницу для errno.h:

<errno.h> заголовочный файл определяет целочисленную переменнуюerrno, который устанавливается системными вызовами и некоторыми библиотечными функциями в случае ошибки, чтобы указать, что пошло не так. Его значение имеет значение только тогда, когда возвращаемое значение вызова указывает на ошибку (т. Е. -1 от большинства системных вызовов; -1или же NULL из большинства библиотечных функций); функция, которая завершается успешно, может быть изменена errno,

Таким образом, вы можете только проверить errno для ошибки, если возвращаемое значение вашей функции фактически возвращает значение, указывающее, что произошла ошибка.

Более полное объяснение errno (и объяснение его связи с strtod()) можно найти на другом StackExchange.

Код работает в соответствии со спецификацией Open Group POSIXstrtod():

Если правильное значение вызвало бы недостаточное значение, то значение, величина которого не превышает наименьшего нормализованного положительного числа в типе возвращаемого значения, должно быть возвращено и в качестве значения errno установлено значение [ERANGE].

Я бы сказал, что вы видите ошибку в деталях на странице руководства Linux.

Другие вопросы по тегам