精度處理的問題

2021-06-22 20:04:50 字數 4353 閱讀 9723

1.

浮點數為啥會有精度問題:

浮點數(以

c/c++為準)

,一般用的較多的是

float,double。

佔位元組數

數值範圍

十進位制精度位數

float

4-3.4e-38

~3.4e38

6~7double

8-1.7e-308

~1.7e308

14~15

如果記憶體不是很緊張或者精度要求不是很低,一般選用

double。14

位的精度

(是有效數字位,不是小數點後的位數

)通常夠用了。注意,問題來了,資料精度位數達到了

14位,但有些浮點運算的結果精度並達不到這麼高,可能準確的結果只有

10~12

位左右。那低幾位呢?自然就是不可預料的數字了。這給我們帶來這樣的問題:即使是理論上相同的值,由於是經過不同的運算過程得到的,他們在低幾位有可能

(一般來說都是

)是不同的。

這種現象看似沒太大的影響,卻會一種運算產生致命的影響

: ==

。恩,就是判斷相等。注意,

c/c++

中浮點數的

==需要完全一樣才能返回

true

。來看下面這個例子:

#include

#include

int main()

輸出:

a = 3.14159265358979360000

b = 3.14159265358979310000

a - b = 0.00000000000000044409

a == b = 0

我們解決的辦法是引進

eps,來輔助判斷浮點數的相等。

2.eps

eps縮寫自epsilon

,表示乙個小量,但這個小量又要確保遠大於浮點運算結果的不確定量。

eps最常見的取值是

1e-8

左右。引入

eps後,我們判斷兩浮點數a、

b相等的方式如下:

定義三出口函式如下

: intsgn(double a)

則各種判斷大小的運算都應做如下修正:

傳統意義

修正寫法

a == b

fabs(a – b) < eps

a != b

fabs(a – b) > eps

a < b

a – b < -eps

a <= b

a – b < eps

a > b

a – b > eps

a >= b

a – b > -eps

這樣,我們才能把相差非常近的浮點數判為相等

;同時把確實相差較大

(差值大於

eps)

的數判為不相等。

ps:養成好習慣,盡量不要再對浮點數做

==判斷。例如,我的修正寫法裡就沒有出現==。

3.eps

帶來的函式越界如果

sqrt(a),asin(a), acos(a)中的a

是你自己算出來並傳進來的,那就得小心了。 如果

a本來應該是

0的,由於浮點誤差,可能實際是乙個絕對值很小的負數(比如

1e-12),

這樣sqrt(a)應得0

的,直接因

a不在定義域而出錯。

類似地,如果

a本來應該是

±1,則

asin(a)

、acos(a)

也有可能出錯。

因此,對於此種函式,必需事先對

a進行校正。

4.

輸出陷阱

i

這一節和下一節一樣,都是因為題目要求輸出浮點數,導致的問題。而且都和四捨五入有關。 ,

據我所知有三種常見的方法:

1. printf(「%.3lf」, a); //保留a

的三位小數,按照第四位四捨五入。

2. (int)a; //將a

靠進0取整

3. ceil(a); floor(a); //

顧名思義,向上取證、向下取整。

需要注意的是,這兩個函式都返回

double

,而非int

其中第一種很常見於輸出

(nonsense…)。

現在考慮一種情況

,題目要求輸出保留兩位小數。有個

case

的正確答案的精確值是

0.005,

按理應該輸出

0.01,

但你的結果可能是

0.005000000001(恭喜)

,也有可能是

0.004999999999(

悲劇),

如果按照

printf(「%.2lf」,a)

輸出,那你的遭遇將和括號裡的字相同。

解決辦法是,如果

a為正,則輸出

a+eps,

否則輸出

a-eps

典型案例

: poj2826

5.

輸出陷阱

ii

icpc

題目輸出有個不成文的規定

(有時也成文

),不要輸出

: -0.000

那我們首先要弄清,什麼時候按

printf(「%.3lf\n」,a)

輸出會出現這個結果。

直接給出結果好了:a∈

(-0.000499999……,-0.000……1)

所以,如果你發現

a落在這個範圍內,請直接輸出

0.000

。更保險的做法是用

sprintf

直接判斷輸出結果是不是

-0.000

再予處理。

典型案例

:uva746

6.

範圍越界

這個嚴格來說不屬於精度範疇了,不過湊數還是可以的。請注意,雖然

double

可以表示的數的範圍很大,卻不是不窮大,上面說過最大是

1e308

。所以有些時候你得小心了,比如做連乘的時候,必要的時候要換成對數的和。

典型案例

:hdu3558

7.

關於set

有時候我們可能會有這種需求,對浮點數進行

插入、查詢是否插入過

的操作。手寫

hash

表是乙個方法

(hash

函式一樣要小心設計),但

set不是更方便嗎。但

set好像是按

==來判重的呀?貌似行不通呢。經觀察

,set

不是通過

==來判斷相等的,是通過

<

來進行的,具體說來,只要將小於定義成

: bool operator < (const dat dat)const

就可以解決問題了。

(基本型別不能過載運算子,所以封裝了下)

8.

輸入值波動過大

這種情況不常見,不過可以幫助你更熟悉

eps。假如一道題輸入說,給乙個浮點數

a, 1e-20 < a < 1e20

。那你還敢用

1e-8

做eps

麼?合理的做法是把

eps按照輸入規模縮放到合適大小。

典型案例

: hustoj1361

9.

一些建議

容易產生較大浮點誤差的函式有

asin

、acos

。歡迎盡量使用

atan2。

另外,如果資料明確說明是整數,而且範圍不大的話,使用

int或者

long long

代替double

都是極佳選擇,因為就不存在浮點誤差了。

注:atan2函式

atan2

(y,x)

是表示x-y

平面上所對應的

(x,y)

座標的角度,它的值域範圍是

(-pi,pi)  

用數學表示就是:

atan2

(y,x)=arg(y/x)-pi  

當y<0

時,其值為負,

當y>0

時,其值為正

.  

js處理精度問題( )

加法函式 function accadd arg1,arg2 catch e try catch e m math.pow 10,math.max r1,r2 return arg1 m arg2 m m 給number型別增加乙個add方法,呼叫起來更加方便。number.prototype.ad...

php計算 處理丟失精度問題 保留小數

解決方法 使用php的內庫libbcmath 自 php 4.0.4,libbcmath 隨同 php 一起發布。該擴充套件不需要任何外部的庫 a bcdiv 100,100,2 除法 那麼 a 1.00 bcadd 2個任意精度數字的加法計算 bccomp 比較兩個任意精度的數字 bcdiv 2個...

PHP處理數學精度

用程式語言做計算,很多時候浮點數精度都是困擾過我的問題,即便是剛學php的新手也會在群裡問為什麼我的計算結果明顯不對,而我們總是老態龍鍾的丟出一句浮點數計算都存在精度問題,並沒有提出過什麼實質性的改善。比如下面的計算0.57 100 zhgxun pro zhgxun php a interacti...