C Implementación de atof
Soy un principiante en C. Actualmente estoy implementando atof para construir un trazador de rayos, sin embargo, todavía estoy aprendiendo cómo escribir programas de manera eficiente.
asignación
Instrucciones
El programa toma un archivo de descripción de escena como argumento para generar objetos. Algunos de estos parámetros son flotantes. Ejemplo del archivo.
Estoy analizando el archivo. Como estoy restringido en términos de líneas permitidas por función y actualmente estoy aprendiendo cómo funcionan los punteros dobles, estoy usando un puntero de doble carácter. Ejemplo de una de esas funciones usando lc_atof.
int a_parsing(char *str, t_pars *data)
{
if (*(str++) == 'A')
{
if (((data->a_ratio = lc_atof(&str)) >= 0.0) && data->a_ratio <= 1.0 && errno == 0)
//
if (((data->a_R = lc_atoi(&str)) >= 0) && data->a_R <= 255 && errno == 0)
if (*(str++) = ',' && ((data->a_G = lc_atoi(&str)) >= 0) && data->a_G <= 255 && errno == 0)
if (*(str++) = ',' && ((data->a_B = lc_atoi(&str)) >= 0) && data->a_B <= 255 && errno == 0)
return (skip_space(&str));
}
return (0);
}
Código actual para revisar:
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
#include <float.h>
static float conversion(char **str)
{
double d_nbr;
double power;
d_nbr = 0.0;
power = 10.0;
while (isdigit(**str))
{
d_nbr = d_nbr * 10.0 + (**str - 48);
if (d_nbr > FLT_MAX)
{
errno = EIO;
return (-1);
}
(*str)++;
}
if (**str == '.')
{
(*str)++;
if (isdigit(**str))
{
d_nbr = d_nbr * 10.0 + (**str - 48);
if (d_nbr > FLT_MAX)
{
errno = EIO;
return (-1);
}
(*str)++;
return ((float)(d_nbr / power));
}
}
errno = EIO;
return (-1);
}
float lc_atof(char **str)
{
float n;
int sign;
n = 0.0;
sign = 1.0;
if (!str || !*str)
{
errno = EIO;
return (-1);
}
while (isspace(**str))
(*str)++;
if (**str == '+' || **str == '-')
{
if (**str == '-')
sign = -1.0;
(*str)++;
}
if (!isdigit(**str))
{
errno = EIO;
return (-1);
}
if ((n = conversion(str)) == 0 && errno != 0)
return (-1);
return (sign * n);
}
Los únicos ajustes al atof real que he hecho es tener un puntero de doble carácter como argumento y devolver -1 en caso de errores.
Cada entrada muy apreciada.
Respuestas
Portabilidad
No hay garantía de que este código use ASCII, por lo que sería mejor usarlo '0'en lugar de 48que sea algo así como un número mágico. El uso '0'lo hace más legible y más fácil de entender.
lc_atofNo maneja la terminación de cadena o el final de línea correctamente
Este código no maneja una cadena terminada en NULL o un carácter de fin de línea. La función isspace()regresa truepara el final de la línea, por lo que el código pasará directamente.
while (isspace(**str))
(*str)++;
if (**str == '+' || **str == '-')
{
if (**str == '-')
sign = -1.0;
(*str)++;
}
if (!isdigit(**str))
{
errno = EIO;
return (-1);
}
Complejidad
Me doy cuenta de que no pidió que se revisara esto, pero la complejidad de cada ifdeclaración en la función de llamada de ejemplo es demasiado y me hizo cometer un error en mi revisión anterior:
int a_parsing(char* str, t_pars* data)
{
if (*(str++) == 'A')
{
if (((data->a_ratio = lc_atof(&str)) >= 0.0) && data->a_ratio <= 1.0 && errno == 0)
//
if (((data->a_R = lc_atoi(&str)) >= 0) && data->a_R <= 255 && errno == 0)
if (*(str++) = ',' && ((data->a_G = lc_atoi(&str)) >= 0) && data->a_G <= 255 && errno == 0)
if (*(str++) = ',' && ((data->a_B = lc_atoi(&str)) >= 0) && data->a_B <= 255 && errno == 0)
return (skip_space(&str));
}
return (0);
}
Yo reescribiría el código como:
#define MAX_COLOR 0xFF
int a_parsing_prime(char* str, t_pars* data)
{
if (*(str++) == 'A')
{
data->a_ratio = lc_atof(&str);
if (!errno && data->a_R <= MAX_COLOR)
{
if (*(str++) = ',')
{
data->a_G = lc_atoi(&str);
if (!errno && data->a_G <= MAX_COLOR)
{
if (*(str++) = ',')
{
data->a_B = lc_atoi(&str);
if (!errno && data->a_B <= MAX_COLOR)
{
return (skip_space(&str));
}
}
}
}
}
}
return (0);
}
lo que muestra fielmente la complejidad de la función.
Elegir
EIOel informe de errores es muy dudoso.lc_atofno hace ninguna entrada o salida; ¿Por qué debería informar un error de IO? Si el tipo de devolución no puede representar el resultado (por ejemplod_nbr > FLT_MAX, ), una opción lógica esERANGEoEOVERFLOW. Si la conversión no puede completarse debido a un argumento mal formado (p. ej!isdigit(**str).), la opción lógica quizás seaEINVAL.Dicho esto, no apruebo la configuración
errnoen la función de biblioteca. Una larga tradición es configurarerrnosolo llamadas al sistema. Sé que esta tradición se viola cada vez más en estos días, pero aún así. Si tiene otros medios para informar errores, quédese con ellos.No es aconsejable usar el parámetro inout (
stren su caso). Complica innecesariamente el código, tanto en el lado de la persona que llama como en el lado del destinatario. El receptor de la llamada se ve obligado a usar el direccionamiento indirecto adicional demasiadas veces y a preocuparse por los paréntesis(**str)++. A su vez, la persona que llama pierde la noción de dónde comenzó el análisis (por ejemplo, necesita registrar el número con formato incorrecto). Mira comostrtofse maneja esto:float strtof(const char *restrict nptr, char **restrict endptr);Aquí
nptrhay solo entrada y soloendptrsalida.Me sorprende que haya decidido limitar la utilidad de la función manejando solo un dígito después del punto decimal. No es un gran esfuerzo manejarlos todos, y los beneficios son mucho mayores.
No es necesario poner entre paréntesis el valor devuelto.
returnes un operador, no una función.