簡體   English   中英

c中的浮點錯誤

[英]floating point inaccuracies in c

我知道浮點值受到可以准確表達的數字的限制,而且我發現了許多描述這種情況發生的站點。 但我還沒有找到任何有關如何有效處理此問題的信息。 但是我確信NASA不能接受0.2 / 0.1 = 0.199999。 例:

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

int main(void)
{
    float number = 4.20;
    float denominator = 0.25;

    printf("number = %f\n", number);
    printf("denominator = %f\n", denominator);
    printf("quotient as a float = %f should be 16.8\n", number/denominator);
    printf("the remainder of 4.20 / 0.25 = %f\n", number - ((int) number/denominator)*denominator);
    printf("now if i divide 0.20000 by 0.1 i get %f not 2\n", ( number - ((int) number/denominator)*denominator)/0.1);
}

輸出:

number = 4.200000
denominator = 0.250000
quotient as a float = 16.799999 should be 16.8
the remainder of 4.20 / 0.25 = 0.200000
now if i divide 0.20000 by 0.1 i get 1.999998 not 2

因此,我該如何使用浮點數(或小數或雙精度數)進行算術運算並獲得准確的結果。 希望我不僅錯過了一些非常明顯的事情。 任何幫助都是極好的! 謝謝。

解決方案是不要對不能接受舍入錯誤的應用程序使用浮點數。 使用擴展精度庫(又稱任意精度庫),例如GNU MP Bignum 有關任意精度庫的詳細列表,請參見此Wikipedia頁面 另請參閱有關合理數據類型此線程的Wikipedia文章,以了解更多信息。

如果要使用浮點表示形式( floatdouble等),則使用可接受的方法處理舍入錯誤(例如,避免== )編寫代碼。 有關如何執行此操作的在線文獻很多,並且方法會根據所涉及的應用程序和算法而變化很大。

大多數時候,浮點數還不錯。 這是我要牢記的關鍵事項:

  • floatdouble之間確實有很大的區別。 在大多數情況下, double可以為大多數事情提供足夠的精度; 出人意料的float常常給你帶來的不足。 除非您知道自己在做什么,並且有充分的理由,否則請始終使用double

  • 有些事情浮點不好。 盡管C本機不支持它,但定點通常是一個很好的選擇。 如果您以美分而不是美元來進行財務計算,那么實際上就是在使用定點,也就是說,如果您使用的是int或美分的long int ,請記住在時間到時將小數點放在右邊的兩位以美元打印。

  • 您使用的算法確實很重要。 幼稚或“顯而易見”的算法可以輕易地放大舍入誤差的影響,而更復雜的算法則將其最小化。 一個簡單的例子是,您將浮點數相加的順序可能很重要。

  • 不用擔心16.8和16.799999。 這類事情總是會發生,但這不是問題,除非您將其視為問題。 如果要在小數點后一位,請使用%.1f打印,而printf會為您取整。 (也不要嘗試比較浮點數是否完全相等,但我認為您現在已經聽說過。)

  • 與上述相關,請記住,0.1不能精確地用二進制表示(就像1/3不能精確地用十進制表示)。 這只是您總是會看到微小的舍入“錯誤”的眾多原因之一,即使它們是完全正常的並且不需要引起問題也是如此。

  • 有時,您需要一個多精度(MP或“ bignum”)庫,該庫可以將數字表示為任意精度,但是使用這些函數(相對)較慢且(相對)麻煩,而且幸運的是,您通常不需要它們。 但是,很高興知道它們的存在,如果您是一個數學家,那么使用它們會很有趣。

  • 有時,用於表示有理數的庫很有用。 例如,這樣的庫將數字1/3表示為一對數字(1、3),因此在嘗試將該數字表示為0.333333333時,它沒有固有的准確性。

其他人推薦了每篇計算機科學家應該知道的有關浮點算術的文章 ,盡管它是篇幅相當長且相當技術性的文章,但它還是很好參考書, 也是標准參考書。 我推薦的一個更簡單,簡短的閱讀方法是我以前教過的一課的講義: https : //www.eskimo.com/~scs/cclass/handouts/sciprog.html#precision 到目前為止,這還有些過時,但是應該可以幫助您入門。

沒有一個好的答案,這通常是一個問題。

如果數據是整數,例如,錢的數量,則將其存儲為整數,這意味着雙精度數必須限制為容納整數的美分而不是有理數的美元。 但這僅在少數情況下有用。

通常,當您嘗試除以接近零的數字時會出現誤差。 因此,您只需要編寫算法來避免或禁止此類操作。 關於“數值穩定”算法和“不穩定”算法的討論很多,這里有一個太大的話題不能公正對待。 然后,通常最好將浮點數視為具有很小的隨機誤差。 如果它們最終代表了現實世界中模擬值的度量,則無論如何它們都必須具有一定的容忍度或不准確性。

如果您是在做數學而不是在處理數據,請不要使用C或C ++。 使用諸如Maple之類的符號代數包,該包將諸如sqrt(2)之類的值存儲為表達式而不是浮點數,因此sqrt(2)* sqrt(2)將始終給出正好2,而不是非常接近的數字至2。

暫無
暫無

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

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