浮動點計算中的精確度和精確度
注意
Office 365 專業增強版 即將重新命名為 Microsoft 365 企業版應用程式。 如需關於此變更的詳細資訊,請閱讀此部落格文章。
原始 KB 編號: 125056
摘要
在許多情況下,浮動點計算中的精確度、舍入和精確度可用於產生對程式師令人吃驚的結果。 它們應該遵循四個一般規則:
在包含 single 和 double 精確度的計算中,結果的精確度通常不會是單一精度。 如果需要雙精度,請務必確定計算中的所有字詞(包括常數)都是以 double 精度指定。
永遠不會假設電腦已正確表示簡單數值。 大部分的浮點數值都無法以有限的二進位值完全表示。 例如,
.1
.0001100110011...
在二進位中(它會永遠重複),所以無法使用二進位演算法(包括所有的電腦)在電腦上以完全準確性表示。永遠不會假設結果正確地指向最後的小數位位置。 「True」答案和可用的任何浮點數處理單位的有限精確度,都有一定的細微差異。
永遠不會比較兩個浮點值,以查看它們是否相等或不相等。 這是規則3的 corollary。 在 "應該" 的數位之間,幾乎永遠不會有細微差異。 相反地,請務必檢查號碼是否幾乎相等。 換句話說,請檢查兩者之間的差異是否很小或沒有意義。
其他相關資訊
一般會將上述規則套用至所有語言,包括 C、c + + 和組合語言程式。 下列範例會示範使用 FORTRAN PowerStation 的一些規則。 所有的範例都是以 FORTRAN PowerStation 32 編譯,但不含任何選項,除非最後一個是以 C 撰寫的。
範例 1
第一個範例會示範兩件事:
- 根據預設,FORTRAN 常數預設為單一精度(預設為 C 常數是雙精度)。
- 包含任何單一精確度字詞的計算方式,不會比計算中的所有字詞,都是單一精度高。
在使用1.1 (單一精度常數)初始化後,y 不准確為單一精度變數。
x = 1.100000000000000 y = 1.100000023841858
以精確的雙精度值乘以單一精度值的結果,會接近兩個單一精度值的乘積。 這兩種計算都有數以千計的錯誤的次數乘以兩個雙精度值。
true = 1.320000000000000 (multiplying 2 double precision values)
y = 1.320000052452087 (multiplying a double and a single)
z = 1.320000081062318 (multiplying 2 single precision values)
範例程式碼
C Compile options: none
real*8 x,y,z
x = 1.1D0
y = 1.1
print *, 'x =',x, 'y =', y
y = 1.2 * x
z = 1.2 * 1.1
print *, x, y, z
end
範例 2
範例2使用二個方程式。 它會示範均勻的精確度計算不是理想的,而且計算結果必須先測試,然後才會因小型錯誤可能會產生大量的結果。 範例2中平方根函數的輸入只有略微為負值,但仍然無效。 如果雙精度計算沒有輕微的錯誤,則結果會是:
Root = -1.1500000000
相反地,它會產生下列錯誤:
執行階段錯誤 M6201: MATH
- sqrt:網域錯誤
範例程式碼
C Compile options: none
real*8 a,b,c,x,y
a=1.0D0
b=2.3D0
c=1.322D0
x = b**2
y = 4*a*c
print *,x,y,x-y
print "(' Root =',F16.10)",(-b+dsqrt(x-y))/(2*a)
end
範例3
範例3顯示由於優化未開啟時進行優化,所以值可能會暫時保留比預期更高的精確度,而且會 unwise 來測試兩個浮點值是否相等。
在此範例中,兩個值都相等且不相等。 在第一個 IF 中,Z 的值仍在副處理器的堆疊上,且具有與 Y 相同的精度。因此 X 不等於 Y,第一筆訊息會列印出來。在第二個 IF 時,必須從記憶體載入 Z,因此,其精確度和值與 X 相同,也會列印第二封郵件。
範例程式碼
C Compile options: none
real*8 y
y=27.1024D0
x=27.1024
z=y
if (x.ne.z) then
print *,'X does not equal Z'
end if
if (x.eq.z) then
print *,'X equals Z'
end if
end
範例4
範例程式碼4的第一部分計算兩個接近1.0 的數位可能的最小差異。 這是因為在1.0 的二進位表示中新增了單一位。
x = 1.00000000000000000 (one bit more than 1.0)
y = 1.00000000000000000 (exactly 1.0)
x-y = .00000000000000022 (smallest possible difference)
有些的 FORTRAN 版本會在顯示這些數位時對其進行舍入,使其固有的數值 imprecision 不太明顯。 這就是顯示時 x 和 y 的外觀。
範例程式碼4的第二部分會計算兩個接近10.0 的數位可能的最小差異。 同樣地,它會將單一位新增至10.0 的二進位標記法來執行這項動作。 請注意,接近10的數位之間的差異大於接近1的差值。 這會示範數值的絕對值較大的一般原則,可在指定的位數中儲存的值越少。
x = 10.00000000000000000 (one bit more than 10.0)
y = 10.00000000000000000 (exactly 10.0)
x-y = .00000000000000178
也會顯示這些數位的二進位表示,以顯示它們的差異只是1位。
x = 4024000000000001 Hex
y = 4024000000000000 Hex
範例程式碼4的最後一部分會顯示簡單的非重複十進位值,通常只能使用重複分數以二進位表示。 在此情況下 x = 1.05,這需要重複的因素 CCCCCCCC ...。(十六進位)中的尾數。 在 FORTRAN 中,最後一個數位 "C" 會舍入為 "D",以維持最高的準確性:
x = 3FF0CCCCCCCCCCCD (Hex representation of 1.05D0)
即便是四捨五入後,結果也不會完全準確。 在最低有效的數位後會出現一些錯誤,我們可以移除第一個數位以加以看到。
x-1 = .05000000000000004
範例程式碼
C Compile options: none
IMPLICIT real*8 (A-Z)
integer*4 i(2)
real*8 x,y
equivalence (i(1),x)
x=1.
y=x
i(1)=i(1)+1
print "(1x,'x =',F20.17,' y=',f20.17)", x,y
print "(1x,'x-y=',F20.17)", x-y
print *
x=10.
y=x
i(1)=i(1)+1
print "(1x,'x =',F20.17,' y=',f20.17)", x,y
print "(1x,'x-y=',F20.17)", x-y
print *
print "(1x,'x =',Z16,' Hex y=',Z16,' Hex')", x,y
print *
x=1.05D0
print "(1x,'x =',F20.17)", x
print "(1x,'x =',Z16,' Hex')", x
x=x-1
print "(1x,'x-1=',F20.17)", x
print *
end
範例5
在 C 中,浮動常數預設會加倍。 使用 "f" 表示浮點值,如 "89.95 f"。
/* Compile options needed: none
*/
#include <stdio.h>
void main()
{
float floatvar;
double doublevar;
/* Print double constant. */
printf("89.95 = %f\n", 89.95); // 89.95 = 89.950000
/* Printf float constant */
printf("89.95 = %f\n", 89.95F); // 89.95 = 89.949997
/*** Use double constant. ***/
floatvar = 89.95;
doublevar = 89.95;
printf("89.95 = %f\n", floatvar); // 89.95 = 89.949997
printf("89.95 = %lf\n", doublevar); // 89.95 = 89.950000
/*** Use float constant. ***/
floatvar = 89.95f;
doublevar = 89.95f;
printf("89.95 = %f\n", floatvar); // 89.95 = 89.949997
printf("89.95 = %lf\n", doublevar); // 89.95 = 89.949997
}