¿Cómo podemos verificar si una cadena de entrada es un doble válido?

Si estoy leyendo números de tipo double de stdin, ¿cómo puedo verificar si los números que se están leyendo son realmente válidos (que los números son de hecho un doble)?

Puedes usar strtod . Compruebe si el resultado es cero y, posteriormente, si endptr == nptr , de acuerdo con la página del manual:

Si no se realiza ninguna conversión, se devuelve cero y el valor de nptr se almacena en la ubicación a la que hace referencia endptr.

Algo como esto:

 char input[50]; char * end; double result = 0; fgets(input, sizeof input, stdin); errno = 0; result = strtod(input, &end); if(result == 0 && (errno != 0 || end == input)){ fprintf(stderr, "Error: input is not a valid double\n"); exit(EXIT_FAILURE); } 

EDITAR parece que hay una pequeña discrepancia entre el estándar y la página de manual. La página del manual dice que endptr == nptr cuando no se realiza una conversión, mientras que el estándar parece implicar que esto no es necesariamente el caso. Peor aún, dice que en caso de que no haya conversión, el errno puede configurarse en EINVAL . Editado el código de ejemplo para comprobar errno también.

Alternativamente, se podría usar sscanf (preferido sobre scanf ), junto con fgets :

 /* just fgetsed input */ if(sscanf(input, "%lf", &result) != 1){ fprintf(stderr, "Error: input is not a valid double\n"); exit(EXIT_FAILURE); } 

Además, no olvide verificar el valor de retorno de fgets para NULL , ¡en caso de que falle!

Ni strtod simple ni sscanf son suficientes para distinguir casos como 1,5 o 1blah de 1.0 deseado. Todos estos resultarán en 1.0 . La razon es que

Las funciones strtod() , strtof() y strtold() convierten la parte inicial de la cadena apuntada por nptr a representación doble doble, flotante y larga, respectivamente.

Para asegurarte de que la cadena completa fuera un literal doble válido, usa strtod así:

 #include  #include  #include  ... char *endptr; errno = 0; double result = strtod(input, &endptr); if (errno != 0 || *endptr != '\0') { fprintf("the value could not be represented as a double exactly\n"); } 

El errno se establecerá si el valor no se puede representar ( ERANGE ). Además, el end apuntará al primer carácter no convertido. Si no se ha establecido la configuración regional, al analizar 1,5 o 1,5 1blah , endptr apuntará al segundo carácter. Si toda la cadena se analizó correctamente como una constante doble, *endptr apuntará a la terminación ‘\ 0’ .

Tenga en cuenta que errno debe establecerse en cero antes de llamar a la función; de lo contrario, conservará el valor de una llamada de función fallida anterior.

¿Cómo podemos verificar si una cadena de entrada es un doble válido?

Empezar con
strtod() para double ,
strtof() para float y
strtold() para long double .

 double strtod(const char * restrict nptr, char ** restrict endptr); 

Las funciones strtod , … convierten la parte inicial de la cadena apuntada por nptr a double ….

Un puntero a la cadena final se almacena en el objeto apuntado por endptr , siempre que endptr no sea un puntero nulo.

C11dr §7.22.1.3 2 y 5

Código simplificado para verificar flojamente la validez. No se queja de desbordamiento / desbordamiento ni texto extra.

 // Return true on valid bool valid_string_to_double(const char *s) { char *end; strtod(s, &end); return s != end; } 

Los desafíos de usar strto*() incluyen: errno == RANGE en el desbordamiento aritmético y quizás en el subdesbordamiento . El valor de retorno en el desbordamiento solo se especifica en el modo de redondeo predeterminado. Ese valor es HUGE_VAL que puede ser un infinito o un gran número. El valor de retorno en el flujo insuficiente es la implementación definida. Se sabe que errno se ha establecido en otros valores distintos de cero en condiciones no especificadas por la especificación C. Se permite el espacio en blanco inicial, no se considera el espacio en blanco al final.


Función de ejemplo que busca 1) conversión, 2) espacio adicional, 3) over / underflow. No solo devuelve una indicación válida, sino que también aborda el valor de la conversión y el estado de errno posteriormente.

 // Return 0 on success // Return non-0 on error, adjust these values as needed - maybe as an `enum`? int convert_string_to_double(double *y, const char *s) { char *end; errno = 0; *y = strtod(s, &end); if (s == end) { return 1; // Failed: No conversion, *y will be 0 } // This may/may not constitute an error - adjust per coding goals // Too great or too small (yet not exactly 0.0) if (errno == ERANGE) { if (fabs(*y) > 1.0) { return 2; // Overflow } // In the case of too small, errno _may_ be set. See §7.22.1.3 10. // For high consistency, return 0.0 and/or clear errno and/or return success. // *y = 0.0; errno = 0; } // What to do if the remainder of the string is not \0? // Since leading whitespace is allowed, // let code be generous and tolerate trailing whitespace too. while (isspace((unsigned char) *end)) { end++; } if (*end) { return 3; // Failed: Extra non-white-space junk at the end. } return 0; // success } 

Si el resultado se desborda (7.12.1), las funciones devuelven un valor cuya magnitud no es mayor que el número positivo normalizado más pequeño en el tipo de retorno; si errno adquiere el valor ERANGE está definido por la implementación. C11dr §7.22.1.3 10


Una consideración incluye el valor de errno después de que se realiza esta función. La especificación C solo especie errno == ERANGE para strtod() , sin embargo, se han conocido varias implementaciones para establecer errno en otros valores por otras razones, incluyendo “sin conversión”. Código podría borrar errno excepto cuando ERANGE para alta consistencia.

Podrías usar la función atof estándar. Devuelve 0 en caso de error, y podría probar si la cadena era 0 de antemano.

http://www.cplusplus.com/reference/cstdlib/atof/