Pregunta strtod underflow, return value! = 0


Aquí está mi código de prueba:

errno = 0;
d = strtod("1.8011670033376514e-308", NULL);

Con este código, obtengo d == 1.8011670033376514e-308 y errno == ERANGE.

De strtod (3):

Si el valor correcto causaría desbordamiento, más o menos HUGE_VAL (HUGE_VALF, HUGE_VALL) se devuelve (de acuerdo con el signo del valor), y ERANGE se almacena en errno. Si el valor correcto causaría underflow, se devuelve cero y ERANGE se almacena en errno.

Entonces, me parece que errno debe ser cero (sin error) o d debe ser cero (flujo insuficiente).

¿Es esto un error, o me estoy perdiendo algo? Esto sucede para muchas versiones diferentes de eglibc y gcc.


12
2017-08-26 05:54


origen


Respuestas:


En §7.22.1.3 los strtod(), strtof() y strtold() funciones, el estándar C11 (ISO / IEC 9899: 2011) dice:

Las funciones devuelven el valor convertido, si lo hay. Si no se puede realizar ninguna conversión,   cero es devuelto Si el valor correcto se desborda y el redondeo predeterminado está en vigencia (7.12.1),   más o menos HUGE_VAL, HUGE_VALF, o HUGE_VALL es devuelto (según el   tipo de retorno y signo del valor) y el valor de la macro ERANGE se almacena en    errno. Si el resultado se desborda (7.12.1), las funciones devuelven un valor cuya magnitud es   no mayor que el número positivo normalizado más pequeño en el tipo de devolución; si    errno adquiere el valor ERANGE está definido por la implementación.

El estándar también se señala en §5.2.4.2.2 Características de los tipos flotantes que los números de punto flotante IEC 60559 (IEEE 754) tienen el límite:

DBL_MIN 2.2250738585072014E-308 // decimal constant

Como 1.8011670033376514e-308 es más pequeño que DBL_MIN, obtienes un número subnormal, y ERANGE es bastante apropiado (pero opcional).

En Mac OS X 10.9.4 con GCC 4.9.1, el siguiente programa:

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

produce la salida:

34: Result too large
 1.8011670033376514e-308
0x01 0x00 0x00 0x00 0xA8 0xF3 0x0C 0x00

El mensaje de error es irónicamente incorrecto, el valor es demasiado pequeño, pero no se puede tener todo.


9
2017-08-26 06:28



El código se comporta de acuerdo a La especificación POSIX del Grupo Abierto strtod():

Si el valor correcto causaría un subdesbordamiento, un valor cuya magnitud no sea mayor que el número positivo normalizado más pequeño en el tipo de retorno se devolverá y errno se establecerá en [ERANGE].

Diría que lo que está viendo es un error en detalle en la página de manual de Linux.


3
2017-08-26 06:30



Si strtod() devolvió un valor distinto de cero (que no es +/- HUGE_VAL), la llamada ha tenido éxito (de acuerdo con la página de manual que ha citado).

Refiriéndose a la página del manual para errno.h:

los <errno.h> archivo de cabecera define la variable entera errno, cual          se establece mediante llamadas al sistema y algunas funciones de la biblioteca en el caso de una          error para indicar qué salió mal. Su valor es significativo solo          cuando el valor de retorno de la llamada indica un error (es decir, -1 de          la mayoría de las llamadas al sistema; -1 o NULL de la mayoría de las funciones de la biblioteca); un          función que tiene éxito se le permite cambiar errno.

Por lo tanto, solo puedes verificar errno para un error si el valor de retorno de su función realmente devuelve un valor que indica que se ha producido un error.

Una explicación más completa de errno (y una explicación de su relación con strtod()) puede ser encontrado en otro StackExchange.


2
2017-08-26 06:11