欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
浮點(diǎn)數精確運算的分析和解決辦法
浮點(diǎn)數精確運算的分析和解決辦法

    1.01 + 2.01 = 3.02
    2.01 * 2.01 = 4 0401

   不知你注意沒(méi)有,這個(gè)很尋常的等式,你如果將它放在C++中,Java中,Basic中,它
居然是不成立的。計算機在開(kāi)玩笑嗎?噢,對了,隱約記得這好象是浮點(diǎn)數的問(wèn)題,似乎
很多很多年前,老師說(shuō)過(guò)。還有某位姓林的先生在某本書(shū)里提過(guò)=0的判斷。
   嗯,如果你不遇到此問(wèn)題,那你完全可以把它拋到火星上去,可惜,偶不好彩,這樣的
問(wèn)題,被俺遇到了。唉!
   why?how?
  
   沒(méi)辦法,硬著(zhù)頭皮,從頭開(kāi)始。

一:為何不成立?Why?
   這得從浮點(diǎn)數的在計算機內的存儲開(kāi)始說(shuō)起,我這里閑話(huà)少說(shuō)。我們只談雙精度double
數(至于float,基本上是五十步和一百步的區別)。
   雙精度數在計算機內的表示方式是:(三部分組成)
   符號(正或負)  階碼(2的N次冪)   尾數(大于等于1小于2的數)
   比如: -(符號) 1.01(尾數) * 2~1(N = 1)  = - 2.02
   具體到計算機的存儲單元:雙精度數共占8字節(64bit)
   符號位(占1個(gè)bit) 階碼(11個(gè)bit) 尾數(52個(gè)bit)
解釋一下:
   符號位:0表示正 1表示負
   階碼:是一個(gè)偏移量,1023的偏移量,它的1023相當于0,小于1023時(shí)為負,
大于1023時(shí)為正,如:10000000001表示指數為1025 - 1023 = 2,表示真值為2^2。

好了,知道了原理,我們開(kāi)始分析上述等式為何為不等。
(相應數的存儲值,可以簡(jiǎn)單用C語(yǔ)言的指針?lè )绞饺〕?
1.01 表示為:
0   0111111 1111    0000 00101000 11110101 11000010 10001111 01011100 00101001 
2.01 表示為;
0   1000000 0000    0000 00010100 01111010 11100001 01000111 10101110 00010100
3.02 表示為:
0   1000000 0000    1000 00101000 11110101 11000010 10001111 01011100 00101001
2.01+1.01 在編程語(yǔ)言中的計算結果 表示為:
0   1000000 0000    1000 00101000 11110101 11000010 10001111 01011100 00101000
好了,我們可以比較一下3.02和計算結果,果然有所不同,只不過(guò)最后一個(gè)bit不同嘿。
為了驗證一下,可以用手工計算一下2.01+1.01:
先把1.01的冪次變?yōu)?(與2.01的階碼相同),于是,將尾數右移一位。得到:
  1000 00010100 01111010 11100001 01000111 10101110 000101001
加上2.01的尾數。
  0000 00010100 01111010 11100001 01000111 10101110 00010100
得到:
  1000 00101000 11110101 11000010 10001111 01011100 00101000
嗯,與計算機的計算結果相同,我們的運算思路是正確的。

因此,結論出來(lái)了,因為浮點(diǎn)數在計算機內的存儲存在偏差,導致運算時(shí),與實(shí)際期望的結
果不同。很多時(shí)候,你可以不理它,但是,可以肯定負責任的說(shuō),發(fā)射衛星的運算時(shí),你
需要知道,否則,衛星一轉眼就不見(jiàn)了。

二:不成立的的原因找到了,那怎么解決這個(gè)問(wèn)題呢,How?
一個(gè)簡(jiǎn)單的解決辦法是:
不要用浮點(diǎn)數來(lái)存儲浮點(diǎn),對于VC,Java,Basic,最好的辦法是用Decimal來(lái)保存它。
下面是分別的實(shí)現:(以加法為例,其它四則運算處理相同)
VC中:
double doublAdd(double dbl1, double dbl2)
{
 double dblResult;
 DECIMAL dec1,dec2,decResult;
 ::VarDecFromR8(dbl1,&dec1);
 ::VarDecFromR8(dbl2,&dec2);
 ::VarDecAdd(&dec1,&dec2,&decResult);
 ::VarR8FromDec(&decResult,&dblResult);
 return dblResult;
}
VB中:
Private Function doubleAdd(ByVal dbl1 As Double, ByVal dbl2 As Double) As Double
    doubleAdd = CDec(dbl1) + CDec(dbl2)
End Function

Java中:
public static double add(double v1, double v2) {
    BigDecimal b1 = new BigDecimal(Double.toString(v1));
    BigDecimal b2 = new BigDecimal(Double.toString(v2));
    return b1.add(b2).doubleValue();
}
解決思路就是:用其它精確的表示法來(lái)存儲浮點(diǎn)數,就這么簡(jiǎn)單。
注意:VC示例中,VarDecFromR8是做了手腳地,如果能直接用VarDecFromStr那更好。

三:在C/C++中,似乎很不情愿看到類(lèi)似上例中的代碼,因為它看起來(lái)很低效,還有其它方法嗎?
好象還有,對了,只是好象。
我們再來(lái)看看雙精度數的表示法:
尾數一共有52個(gè)bit,也就是最小能表示的數是 2^-52,取對數可得出,約是
在小數點(diǎn)后16位,那也就是說(shuō)小數點(diǎn)后15位是可以精確表示的,加上前置的默認1,一共有16位
數字是精確可靠的。
我們來(lái)試驗一下,看上述結論是否成立。
看看VC調試器的顯示值。
2.01 的顯示值: 2.0099999999999998
如果只取16位有效數字,那么將最后一位8四舍五入,我們得到正確的表示。
好了,這能說(shuō)明什么呢?

四:我們先看比較簡(jiǎn)單的加,減法運算。
對于加法:dbl1 + dbl2:
假設dbl1=1.01 那么,16減去整數位1,我們可以假定,在計算機表示中:
小數點(diǎn)后的15位都是精確的。
假設dbl2=100.01 那么 16-3,假定小數點(diǎn)后13位是精確的。
憑經(jīng)驗我們可以知道,兩個(gè)小數相加,小數點(diǎn)后的精度不會(huì )大于精度銷(xiāo)大的一個(gè)。
所以,我們判定得出結果的精確度可以用較大的一個(gè)為準。
于是,將得出的結果,去掉不精確的位數,則應該可以得到準確值。
VC實(shí)現如下:
#define DELTA_RATE  16
int getRound(double dbl)
{
 COleVariant var(dbl);
 COleVariant varForLog(dbl);
 ::VarRound(&varForLog,0,&varForLog);
 int nIntCount = log10(varForLog.dblVal>0?varForLog.dblVal:-varForLog.dblVal) + 1;
 int nRound = DELTA_RATE - nIntCount;
 return nRound;
}
double doublAdd2(double dbl1, double dbl2)
{
 COleVariant var(dbl1+dbl2);
 int r1 = getRound(dbl1);
 int r2 = getRound(dbl2);
 ::VarRound(&var,max(r1,r2),&var);
 return var.dblVal;
}
做過(guò)一些實(shí)驗,好象是正確的。同理可以實(shí)現doubleSub2的函數。
注意:這里并不用下面五所提的取精度的方式,因為取精度的運算更低效。

五:對于乘除法呢?問(wèn)題有些復雜,先找出一個(gè)需要處理的例子。
如:2.01*2.01=4.0401。
試了一下,不成立。
用方法一的Decimal方式測試,可以通過(guò)。
那么方法二呢?
再做假設吧,假設dbl1有兩位小數,dbl2也有兩位小數,按理論,
可得出相乘后,最大可能是2+2位小數。那么,我們按照 4位小數
進(jìn)行Round處理,可能會(huì )得出正確的結果。
實(shí)際上,要取一個(gè)雙精度的10進(jìn)制表達的小數位,我沒(méi)有找到什么好辦法,
我能想到的:也就是將數字轉為字串,然后查找.后的位數。這樣,顯然是
非常低效的,這里,我就不再寫(xiě)出代碼了。

六:比較方法一和方法二。方法二并不高效,并且還有一些不定因素,所以,
最好采用方法一來(lái)統一處理浮點(diǎn)數的運算。
至于效率,實(shí)際上最佳方法是從程序的設計著(zhù)手,將double從程序中去除掉。
比如在VC中,可以用Variant::Decimal來(lái)徹底替換double,這樣,就不存在
中間的轉換了,效率自然就提高了。有關(guān)Decimal的常用函數是:
VarDecFromStr VarDecAdd VarDecSub VarDecMul VarDecDiv ……
VarBstrFromDec
至于Java和VB,也可以方便的找到相應函數。

很想找到一種更好的方法,總覺(jué)得用Decimal來(lái)進(jìn)行運算很不爽,但真的沒(méi)找到?
其實(shí)呢,做了一下測試,Decimal的運算并不慢,如果可以將內部存儲改為Decimal,
那就可以徹底解決問(wèn)題了。

本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
float和double有什么區別?
C#中對于float,double,decimal的誤解
double類(lèi)型與零值比較時(shí)不能用==和!=
C# 基礎知識系列- 1 數據類(lèi)型
系統設計 | 高精度計算
Swift 中如何避免精度丟失
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久