Создайте точную реализацию atof () в c - PullRequest
0 голосов
/ 18 сентября 2018

Я написал реализацию atof () в c.Я сталкиваюсь с ошибками округления в этой реализации.Таким образом, установка значения теста 1236,965 дает результат 1236,964966, но функция библиотеки atof () возвращает 1236,965000.Мой вопрос, как сделать пользовательскую реализацию atof () более «правильной»?

Можно ли где-нибудь найти определение библиотеки atof ()?

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

float str_to_float(char *);
void float_to_str(float,char *);

int main(){
    int max_size;
    float x;
    char *arr;
    printf("Enter max size of string : ");
    scanf("%d",&max_size);
    arr=malloc((max_size+1)*sizeof(char));
    scanf("%s",arr);
    x=str_to_float(arr);
    printf("%f\n%f",x,atof(arr));
    return 0;
}

float str_to_float(char *arr){
    int i,j,flag;
    float val;
    char c;
    i=0;
    j=0;
    val=0;
    flag=0;
    while ((c = *(arr+i))!='\0'){
//      if ((c<'0')||(c>'9')) return 0;
        if (c!='.'){
            val =(val*10)+(c-'0');
            if (flag == 1){
                --j;
            }
        }
        if (c=='.'){ if (flag == 1) return 0; flag=1;}
        ++i;
    }
    val = val*pow(10,j);
    return val;
}

Ответы [ 2 ]

0 голосов
/ 18 сентября 2018

как сделать пользовательскую реализацию atof () более «правильной»?

Легко: 1) никогда не переполнять промежуточные вычисления и 2) только округлять один раз (в конце).

Это сложно , чтобы сделать эти 2 шага.

Примечание: C's atof(), strtof() и т. Д. Также обрабатывают экспоненциальную запись - в десятичной и шестнадцатеричной форме.


Потенциальные округления

val*10
(val*10)+(c-'0');
pow(10,j)
val*pow(10,j)  // This last multiplication is the only tolerable one.

Потенциальное переполнение (даже если окончательный ответ находится в пределах диапазона)

val*10
(val*10)+(c-'0');
pow(10,j)

Использование более широкого типа, например double, можетзначительно уменьшить возникновение таких проблем и добиться более «правильного» ОП. Тем не менее, они все еще существуют.

Это не простая задача , которую нужно решить, чтобы получить лучший (правильный) результат с плавающей запятой из всех строковых входных данных.


Пример подходов к решению.

Избегайте переполнения: вместо pow(10,j):

val = val*pow(5,j);  // rounds, `pow(5,j)` not expected to overflow a finite final result.
val = val*pow(2,j);  // Does not round except at extremes

Код должен сформироваться (ival*10)+(c-'0') с использованием расширенной целочисленной математики в цикле для точности.

Все же это простоРассказывая о многих угловых случаях.


@ Eric Postpischil прокомментировал надежный код C ++, который хорошо обрабатывает неэкспоненциальный ввод строки записи.Это делает начальную математику, используя целые числа и только раунды позже в процессе.Этот связанный код не виден, если ваш представитель не более 10 000+, поскольку вопрос был удален.

0 голосов
/ 18 сентября 2018

Измените все ваши поплавки на двойные.Когда я это проверял, это дало тот же результат, что и библиотечная функция atof для вашего теста.

atof возвращает значение double, а не float.Помните, что это на самом деле double, а не float, который является «нормальным» типом с плавающей точкой в ​​C. Литерал с плавающей точкой, такой как 3.14, имеет тип double, а библиотечные функции, такие как sin, log и (возможно, обманчиво названный) atof работают с двойными числами.

Хотя это все еще не будет "точным".Ближайшее значение, которое вы можете получить к 1236,965, как число с плавающей точкой (точно): 1236,9649658203125 и двойное значение 1236,964999999999918145476840436458587646484375, которое будет округлено до 1236,965000 с помощью printf.Независимо от того, сколько бит у вас есть в двоичном числе с плавающей запятой, 1236,965 не может быть точно представлено, подобно тому, как 1/3 не может быть точно представлено с конечным числом десятичных цифр: 0,3333333333333333 ...

А также, как видно из обсуждения в комментариях, это сложная проблема со многими возможными ловушками, если вам нужен код, который всегда будет давать наиболее близкое значение.

...