簡體   English   中英

在 c 中創建一個精確的 atof() 實現

[英]Create a precise atof() implementation in c

我已經用 c 編寫了一個 atof() 實現。 我在這個實現中面臨四舍五入的錯誤。 因此,輸入 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;
}

將所有花車改為雙打。 當我測試它時,它給出了與測試用例的庫函數atof相同的結果。

atof返回雙倍,而不是浮點數。 請記住,它實際上是 double 而不是 C 中“正常”浮點類型的 float。浮點文字,例如3.14 ,屬於 double 類型,而庫函數例如sinlog和(可能具有欺騙性命名) atof與雙打一起工作。

盡管如此,它仍然不會是“精確的”。 作為浮點數,最接近 1236.965 的是(正好)1236.9649658203125,作為雙精度數 1236.9649999999999181454768404364585873046484375,將 6 printf 舍入到 6 printf 到 9。 無論二進制浮點數有多少位,1236.965 都無法精確表示,類似於 1/3 無法用有限數量的十進制數字精確表示:0.3333333333333333...

而且,正如在評論中的討論中所見,這是一個難題,如果您希望代碼始終提供最接近的值,則可能存在許多陷阱。

如何使用戶定義的 atof() 實現更“正確”?

簡單:1) 從不溢出中間計算和 2) 只循環一次(最后)。

很難做到這兩個步驟。

注意:C 的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這樣更寬的類型可以大大減少此類問題的發生,並實現 OP 的“更'正確'”。 然而它們仍然存在。

要從所有字符串輸入中獲得最佳(正確)浮點結果,這不是一個容易解決的問題


解決的示例方法。

避免溢出:而不是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,因為問題已被刪除,否則此鏈接代碼是不可見的。

我用你的代碼作為靈感來寫我自己的。 其他評論者和答案沒有認識到問題的最初原因是嵌入的情況。 在我的情況下,庫“atof”引入了一些做“printf”的東西,它引入了我沒有的“系統調用”。

所以....在這里我提出了一個簡單的(不實現指數表示法)atof 實現,它適用於浮點數,適用於嵌入。

我的實現使用更少的變量。

float ratof(char *arr)
{
  float val = 0;
  int afterdot=0;
  float scale=1;
  int neg = 0; 

  if (*arr == '-') {
    arr++;
    neg = 1;
  }
  while (*arr) {
    if (afterdot) {
      scale = scale/10;
      val = val + (*arr-'0')*scale;
    } else {
      if (*arr == '.') 
    afterdot++;
      else
    val = val * 10.0 + (*arr - '0');
    }
    arr++;
  }
  if(neg) return -val;
  else    return  val;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM