C Implementazione di atof
Sono un principiante in C. Attualmente sto implementando atof per costruire un raytracer, tuttavia sto ancora imparando a scrivere programmi in modo efficiente.
Assegnazione
Istruzioni
Il programma prende un file di descrizione della scena come argomento per generare oggetti. Alcuni di questi parametri sono float. Esempio del file.

Sto analizzando il file. Dato che sono limitato in termini di linee consentite per funzione e attualmente sto imparando come funzionano i doppi puntatori, sto usando un puntatore a doppio carattere. Esempio di una di queste funzioni che utilizza 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);
}
Codice attuale da rivedere:
#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);
}
Le uniche modifiche all'attuale atof che ho apportato sono avere un puntatore a doppio carattere come argomento e restituire -1 in caso di errori.
Ogni input molto apprezzato.
Risposte
Portabilità
Non vi è alcuna garanzia che questo codice utilizzerà ASCII, quindi sarebbe meglio utilizzare '0'
piuttosto che 48
che è qualcosa di un numero magico. L'utilizzo '0'
lo rende più leggibile e più facile da capire.
lc_atof
Non gestisce correttamente la terminazione della stringa o la fine della riga
Questo codice non gestisce una stringa con terminazione NULL o un carattere di fine riga. La funzione isspace()
ritorna true
per la fine della riga in modo che il codice la oltrepassi.
while (isspace(**str))
(*str)++;
if (**str == '+' || **str == '-')
{
if (**str == '-')
sign = -1.0;
(*str)++;
}
if (!isdigit(**str))
{
errno = EIO;
return (-1);
}
Complessità
Mi rendo conto che non hai chiesto di rivedere questo, ma la complessità di ogni if
affermazione nella funzione di chiamata di esempio è eccessiva e mi ha causato un errore nella mia recensione in precedenza:
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);
}
Riscriverei il codice come:
#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);
}
che mostra veramente la complessità della funzione.
La scelta
EIO
per la segnalazione degli errori è molto dubbia.lc_atof
non esegue alcun input o output; perché dovrebbe segnalare un errore IO? Se il tipo restituito non può rappresentare il risultato (ad esempiod_nbr > FLT_MAX
), una scelta logica èERANGE
oEOVERFLOW
. Se la conversione non può essere completata a causa dell'argomento errato (ad esempio!isdigit(**str)
), la scelta logica potrebbe essereEINVAL
.Detto questo, non approvo l'impostazione
errno
nella funzione libreria. Una tradizione di lunga data è quella di impostareerrno
solo le chiamate di sistema. So che questa tradizione è sempre più violata in questi giorni, ma ancora. Se disponi di altri mezzi di segnalazione degli errori, mantienili.L'uso del parametro inout (
str
nel tuo caso) non è consigliabile. Complica inutilmente il codice, sia dal lato del chiamante che da quello del chiamato. Il chiamato è costretto a usare troppi riferimenti indiretti extra ea preoccuparsi delle parentesi(**str)++
. A sua volta, il chiamante perde traccia di dove è iniziato il parsable (ad esempio, deve registrare il numero errato). Guarda comestrtof
gestisce questo:float strtof(const char *restrict nptr, char **restrict endptr);
Ecco
nptr
un in-only edendptr
è out-only.Sono sorpreso che tu abbia deciso di limitare l'utilità della funzione gestendo solo una cifra dopo il punto decimale. Non è un grande sforzo gestirli tutti e i vantaggi sono molto maggiori.
Non è necessario mettere tra parentesi il valore restituito.
return
è un operatore, non una funzione.