2017-02-02 19 views
3

Эта программа вызывает strtod() на первый аргументе командной строки и выводит возвращаемое значение:Почему strtod() с DBL_MIN дает ERANGE?

#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; 
} 

Когда я запускаю его на SUSE Linux Enterprise Server 11 SP2 или Linux Mint 17 Qiana с аргументом 2.22507385850720138309e-308, соответствующей к маленьким представимому двойное значение (DBL_MIN), он дает выход я ожидаю:

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

Однако на SUSE Linux Enterprise Server 11 SP3 с тем же аргументом , эээ нет установлен в ERANGE:

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

ли второе поведение в силе, и если да, то почему?


Сноска:

  1. С DBL_MIN представим Я думаю, этот вопрос отличается от "Odd behavior when converting C strings to/from doubles", где значение конвертируется было underflowed.

  2. На SUSE, если я запустить программу с аргументом 2.22507385850720138310e-308 затем ERRNO устанавливается в 0. (А если я запустить программу с аргументом 0x1p-1022 затем ERRNO также устанавливается в 0.)

+0

Вместо 'printf (" d =% e \ n ", d);', используйте 'printf (" d =%. * E \ n ", DBL_DIG + 2, d);' для отображения 'd' до достаточной точности. Может также хотеть использовать 'printf (" d =% a \ n ", d);' для точного вывода. – chux

+1

Обе системы '2.22507385850720138e-308'. Я изменю программу, чтобы показать их. –

+0

Обратите внимание, что ваш номер входа, конечно, * не * точно представлен, как можно быстро определить. При условии, что правильная доля больше нуля и с конечным двоичным представлением, десятичное представление этой фракции также является конечным, а наименьшая значимая ненулевая цифра его десятичного представления равна 5. Поскольку наименьшая значащая десятичная цифра * вашего * число равно 9 (и это находится во дробной части числа), дробная часть и, следовательно, целое число, не имеет конечного двоичного представления. –

ответ

3

Входное значение 2.22507385850720138309e-308 меньше точного значения DBL_MIN (2 -1022). Более крупное десятичное расширение:

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

Так что технически это приводит к нижнему потоку.

: C 1999 (7.20.1.3) утверждает, что в данном случае:

Если результат (7.12.1 недорасход), функции возвращают значение, величина которого не больше, чем наименьшее нормализованного положительное число в возвращаемом типе; errno приобретает значение ERANGE.

+0

Да, это имеет смысл. Например. подтвердить в 'bc' use' scale = 1000; 2^(- 1022) - 2.22507385850720138309 * 10^(- 308) 'и заметим, что результат больше нуля. Благодаря! –

+0

Почему же они не #define DBL_MIN как кажущееся менее проблематичное значение '2.22507385850720138310e-308'? –

+0

Обратите внимание, что в man-странице strause() пользователя SUSE сказано: «Если правильное значение приведет к переполнению, возвращается нуль и ERANGE сохраняется в errno». Поэтому его поведение соответствует стандарту C, но не его собственной документации! –

3

Поскольку я прочитал спецификацию C, это неправильное поведение.

strtod() относится к 7.12.1:

7.12.1 Лечение состояний ошибки
В результате потери значимости, если величина математического результата настолько мала, что математический результат не может быть представлен, без внеочередной ошибки округления, в объекте указанного типа. C11 §7.12.1 6

Хотя математически входные строки меньше DBL_MIN, ...

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

... превращение в (double) DBL_MIN не с необыкновенной ошибкой округления. Значение должно конвертироваться в DBL_MIN без установки errno.

Для справки, другие около double показаны.

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) 

Я подозреваю, что исходный код простой выполняется преобразование с точностью, как расширить long double и затем тестировали, если long double результат был меньше, чем DBL_MIN, а не с учетом краевых условий.