[英]Should I combine multiplication and division steps when working with floating point values?
我知道浮點數和雙打的精度問題,這就是為什么我這樣問:
如果我有一個公式,如: (a/PI)*180.0
(其中PI是常數)
我應該結合除法和乘法,所以我只能使用一個除法: a/0.017453292519943295769236
,以避免精度損失?
當計算結果的步驟較少時,這是否會使其更精確?
是的,通常應該將盡可能多的乘法和除法組合成一個操作。 它(通常(*))同時更快,更准確。
π和π/ 180及其反轉都不能完全表示為浮點。 因此,計算將涉及至少一個近似常數(除了所涉及的每個操作的近似值之外)。
因為兩個操作分別引入一個近似,所以可以期望在一個操作中進行整個計算更准確。
除此之外,“幸運”的問題是,π/ 180在浮點格式中表示的相對精度是好於還是差於180 /π。
我的編譯器提供了long double
類型的附加精度,所以我可以使用它作為參考來回答double
這個問題:
~ $ cat t.c
#define PIL 3.141592653589793238462643383279502884197L
#include <stdio.h>
int main() {
long double heop = 180.L / PIL;
long double pohe = PIL / 180.L;
printf("relative acc. of π/180: %Le\n", (pohe - (double) pohe) / pohe);
printf("relative acc. of 180/π: %Le\n", (heop - (double) heop) / heop);
}
~ $ gcc t.c && ./a.out
relative acc. of π/180: 1.688893e-17
relative acc. of 180/π: -3.469703e-17
在通常的編程實踐中,人們不會打擾並簡單地乘以180 /π的(浮點表示),因為乘法比除法快得多。 事實證明,在binary64浮點類型double
幾乎總是映射到的情況下,π/ 180可以用比180 /π更好的相對精度來表示,所以π/ 180是用來優化精度的常數: a / ((double) (π / 180))
。 使用這個公式,總相對誤差大約是常數的相對誤差(1.688893e-17)和除法的相對誤差之和(取決於a
的值,但絕不會超過2) - 53 )。
需要注意的是分工是如此昂貴,你可以得到一個更准確的結果通過使用一個乘法和一個FMA更快:讓heop1
是最好的double
180 /π近似,並且heop2
最好的double
180 /π逼近- heop1
。 然后,結果的最佳值可以計算為:
double r = fma(a, heop1, a * heop2);
事實上,上述是對實數計算的絕對最佳可能的double
近似是一個定理(事實上,它是一個例外的定理。細節可以在“浮點運算手冊”中找到)。 但是,即使為了得到double
結果而想要乘以double
精度的實常數是該定理的例外之一,上述計算仍然非常准確,並且僅與一些特殊值的最佳double
近似不同a
。
如果像我的一樣,你的編譯器為long double
提供了比double
更高的精度,你也可以使用一個long double
乘法:
// this is more accurate than double division:
double r = (double)((long double) a * 57.295779513082320876798L)
這不是基於FMA解決好,但它是不夠好,對於大多數值a
,它產生最佳的double
逼近實際計算。
(*)對於大多數常數而言,對組常數更好的說法在統計上是正確的。
如果你碰巧要乘a
,比方說,真正的恆0.0000001 * DBL_MIN
,你會好起來的第一乘以0.0000001
,然后通過DBL_MIN
,而最終的結果(可以是一個標准化的數字,如果a
是超過100萬大或者等等)比你乘以0.0000001 * DBL_MIN
的最佳double
表示更准確。 這是因為將0.0000001 * DBL_MIN
表示為單個double
精度值時的相對精度遠低於表示0.0000001的精度。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.