日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

c语言两个浮点数相乘,两个浮点变量相乘结果为什么不精确

發(fā)布時(shí)間:2024/1/8 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 c语言两个浮点数相乘,两个浮点变量相乘结果为什么不精确 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

浮點(diǎn)數(shù)精確運(yùn)算的分析和解決辦法1。01+2。01=3。022。01*2。01=40401不知你注意沒有,這個(gè)很尋常的等式,你如果將它放在C++中,Java中,Basic中,它居然是不成立的。計(jì)算機(jī)在開玩笑嗎?噢,對(duì)了,隱約記得這好象是浮點(diǎn)數(shù)的問題,似乎很多很多年前,老師說過。

還有某位姓林的先生在某本書里提過=0的判斷。嗯,如果你不遇到此問題,那你完全可以把它拋到火星上去,可惜,偶不好彩,這樣的問題,被俺遇到了。唉!why?how?沒辦法,硬著頭皮,從頭開始。一:為何不成立?Why?這得從浮點(diǎn)數(shù)的在計(jì)算機(jī)內(nèi)的存儲(chǔ)開始說起,我這里閑話少說。

我們只談雙精度double數(shù)(至于float,基本上是五十步和一百步的區(qū)別)。雙精度數(shù)在計(jì)算機(jī)內(nèi)的表示方式是:(三部分組成)符號(hào)(正或負(fù))階碼(2的N次冪)尾數(shù)(大于等于1小于2的數(shù))比如:-(符號(hào))1。01(尾數(shù))*2~1(N=1)=-2。

02具體到計(jì)算機(jī)的存儲(chǔ)單元:雙精度數(shù)共占8字節(jié)(64bit)符號(hào)位(占1個(gè)bit)階碼(11個(gè)bit)尾數(shù)(52個(gè)bit)解釋一下:符號(hào)位:0表示正1表示負(fù)階碼:是一個(gè)偏移量,1023的偏移量,它的1023相當(dāng)于0,小于1023時(shí)為負(fù),大于1023時(shí)為正,如:10000000001表示指數(shù)為1025-1023=2,表示真值為2^2。

好了,知道了原理,我們開始分析上述等式為何為不等。(相應(yīng)數(shù)的存儲(chǔ)值,可以簡單用C語言的指針方式取出)1。01表示為:00111111111100000010100011110101110000101000111101011100001010012。

01表示為;01000000000000000001010001111010111000010100011110101110000101003。02表示為:01000000000010000010100011110101110000101000111101011100001010012。

01+1。01在編程語言中的計(jì)算結(jié)果表示為:0100000000001000001010001111010111000010100011110101110000101000好了,我們可以比較一下3。02和計(jì)算結(jié)果,果然有所不同,只不過最后一個(gè)bit不同嘿。

為了驗(yàn)證一下,可以用手工計(jì)算一下2。01+1。01:先把1。01的冪次變?yōu)?(與2。01的階碼相同),于是,將尾數(shù)右移一位。得到:10000001010001111010111000010100011110101110000101001加上2。

01的尾數(shù)。0000000101000111101011100001010001111010111000010100得到:1000001010001111010111000010100011110101110000101000嗯,與計(jì)算機(jī)的計(jì)算結(jié)果相同,我們的運(yùn)算思路是正確的。

因此,結(jié)論出來了,因?yàn)楦↑c(diǎn)數(shù)在計(jì)算機(jī)內(nèi)的存儲(chǔ)存在偏差,導(dǎo)致運(yùn)算時(shí),與實(shí)際期望的結(jié)果不同。很多時(shí)候,你可以不理它,但是,可以肯定負(fù)責(zé)任的說,發(fā)射衛(wèi)星的運(yùn)算時(shí),你需要知道,否則,衛(wèi)星一轉(zhuǎn)眼就不見了。二:不成立的的原因找到了,那怎么解決這個(gè)問題呢,How?一個(gè)簡單的解決辦法是:不要用浮點(diǎn)數(shù)來存儲(chǔ)浮點(diǎn),對(duì)于VC,Java,Basic,最好的辦法是用Decimal來保存它。

下面是分別的實(shí)現(xiàn):(以加法為例,其它四則運(yùn)算處理相同)VC中:doubledoublAdd(doubledbl1,doubledbl2){doubledblResult;DECIMALdec1,dec2,decResult;::VarDecFromR8(dbl1,&dec1);::VarDecFromR8(dbl2,&dec2);::VarDecAdd(&dec1,&dec2,&decResult);::VarR8FromDec(&decResult,&dblResult);returndblResult;}VB中:PrivateFunctiondoubleAdd(ByValdbl1AsDouble,ByValdbl2AsDouble)AsDoubledoubleAdd=CDec(dbl1)+CDec(dbl2)EndFunctionJava中:publicstaticdoubleadd(doublev1,doublev2){BigDecimalb1=newBigDecimal(Double。

toString(v1));BigDecimalb2=newBigDecimal(Double。toString(v2));returnb1。add(b2)。doubleValue();}解決思路就是:用其它精確的表示法來存儲(chǔ)浮點(diǎn)數(shù),就這么簡單。

注意:VC示例中,VarDecFromR8是做了手腳地,如果能直接用VarDecFromStr那更好。三:在C/C++中,似乎很不情愿看到類似上例中的代碼,因?yàn)樗雌饋砗艿托?#xff0c;還有其它方法嗎?好象還有,對(duì)了,只是好象。我們再來看看雙精度數(shù)的表示法:尾數(shù)一共有52個(gè)bit,也就是最小能表示的數(shù)是2^-52,取對(duì)數(shù)可得出,約是在小數(shù)點(diǎn)后16位,那也就是說小數(shù)點(diǎn)后15位是可以精確表示的,加上前置的默認(rèn)1,一共有16位數(shù)字是精確可靠的。

我們來試驗(yàn)一下,看上述結(jié)論是否成立。看看VC調(diào)試器的顯示值。2。01的顯示值:2。0099999999999998如果只取16位有效數(shù)字,那么將最后一位8四舍五入,我們得到正確的表示。好了,這能說明什么呢?四:我們先看比較簡單的加,減法運(yùn)算。

對(duì)于加法:dbl1+dbl2:假設(shè)dbl1=1。01那么,16減去整數(shù)位1,我們可以假定,在計(jì)算機(jī)表示中:小數(shù)點(diǎn)后的15位都是精確的。假設(shè)dbl2=100。01那么16-3,假定小數(shù)點(diǎn)后13位是精確的。憑經(jīng)驗(yàn)我們可以知道,兩個(gè)小數(shù)相加,小數(shù)點(diǎn)后的精度不會(huì)大于精度銷大的一個(gè)。

所以,我們判定得出結(jié)果的精確度可以用較大的一個(gè)為準(zhǔn)。于是,將得出的結(jié)果,去掉不精確的位數(shù),則應(yīng)該可以得到準(zhǔn)確值。VC實(shí)現(xiàn)如下:#defineDELTA_RATE16intgetRound(doubledbl){COleVariantvar(dbl);COleVariantvarForLog(dbl);::VarRound(&varForLog,0,&varForLog);intnIntCount=log10(varForLog。

dblVal>0?varForLog。dblVal:-varForLog。dblVal)+1;intnRound=DELTA_RATE-nIntCount;returnnRound;}doubledoublAdd2(doubledbl1,doubledbl2){COleVariantvar(dbl1+dbl2);intr1=getRound(dbl1);intr2=getRound(dbl2);::VarRound(&var,max(r1,r2),&var);returnvar。

dblVal;}做過一些實(shí)驗(yàn),好象是正確的。同理可以實(shí)現(xiàn)doubleSub2的函數(shù)。注意:這里并不用下面五所提的取精度的方式,因?yàn)槿【鹊倪\(yùn)算更低效。五:對(duì)于乘除法呢?問題有些復(fù)雜,先找出一個(gè)需要處理的例子。如:2。01*2。01=4。0401。

試了一下,不成立。用方法一的Decimal方式測試,可以通過。那么方法二呢?再做假設(shè)吧,假設(shè)dbl1有兩位小數(shù),dbl2也有兩位小數(shù),按理論,可得出相乘后,最大可能是2+2位小數(shù)。那么,我們按照4位小數(shù)進(jìn)行Round處理,可能會(huì)得出正確的結(jié)果。

實(shí)際上,要取一個(gè)雙精度的10進(jìn)制表達(dá)的小數(shù)位,我沒有找到什么好辦法,我能想到的:也就是將數(shù)字轉(zhuǎn)為字串,然后查找。后的位數(shù)。這樣,顯然是非常低效的,這里,我就不再寫出代碼了。六:比較方法一和方法二。方法二并不高效,并且還有一些不定因素,所以,最好采用方法一來統(tǒng)一處理浮點(diǎn)數(shù)的運(yùn)算。

至于效率,實(shí)際上最佳方法是從程序的設(shè)計(jì)著手,將double從程序中去除掉。比如在VC中,可以用Variant::Decimal來徹底替換double,這樣,就不存在中間的轉(zhuǎn)換了,效率自然就提高了。有關(guān)Decimal的常用函數(shù)是:VarDecFromStrVarDecAddVarDecSubVarDecMulVarDecDiv……VarBstrFromDec至于Java和VB,也可以方便的找到相應(yīng)函數(shù)。

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

全部

總結(jié)

以上是生活随笔為你收集整理的c语言两个浮点数相乘,两个浮点变量相乘结果为什么不精确的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。