欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳談浮點(diǎn)精度(float、double)運(yùn)算不精確的原因

 更新時(shí)間:2021年12月10日 15:34:09   作者:marco__  
這篇文章主要介紹了詳談浮點(diǎn)精度(float、double)運(yùn)算不精確的原因,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

為什么浮點(diǎn)精度運(yùn)算會(huì)有問(wèn)題

我們平常使用的編程語(yǔ)言大多都有一個(gè)問(wèn)題——浮點(diǎn)型精度運(yùn)算會(huì)不準(zhǔn)確。比如

double num = 0.1 + 0.1 + 0.1;
// 輸出結(jié)果為 0.30000000000000004
double num2 = 0.65 - 0.6;
// 輸出結(jié)果為 0.05000000000000004

筆者在測(cè)試的時(shí)候發(fā)現(xiàn) C/C++ 竟然不會(huì)出現(xiàn)這種問(wèn)題,我最初以為是編譯器優(yōu)化,把這個(gè)問(wèn)題解決了。但是 C/C++ 如果能解決其他語(yǔ)言為什么不跟進(jìn)?根據(jù)這個(gè)問(wèn)題的產(chǎn)生原因來(lái)看,編譯器優(yōu)化解決這個(gè)問(wèn)題邏輯不通。后來(lái)發(fā)現(xiàn)是打印的方法有問(wèn)題,打印輸出方法會(huì)四舍五入。使用 printf("%0.17f\n", num); 以及 cout << setprecision(17) << num2 << endl; 多打印幾位小數(shù)即可看到精度運(yùn)算不準(zhǔn)確的問(wèn)題。

那么精度運(yùn)算不準(zhǔn)確這是為什么呢?

我們接下來(lái)就需要從計(jì)算機(jī)所有數(shù)據(jù)的表現(xiàn)形式二進(jìn)制說(shuō)起了。如果大家很了解二進(jìn)制與十進(jìn)制的相互轉(zhuǎn)換,那么就能輕易的知道精度運(yùn)算不準(zhǔn)確的問(wèn)題原因是什么了。如果不知道就讓我們一起回顧一下十進(jìn)制與二進(jìn)制的相互轉(zhuǎn)換流程。

一般情況下二進(jìn)制轉(zhuǎn)為十進(jìn)制我們所使用的是按權(quán)相加法。十進(jìn)制轉(zhuǎn)二進(jìn)制是除2取余,逆序排列法。很熟的同學(xué)可以略過(guò)。

// 二進(jìn)制到十進(jìn)制
10010 = 0 * 2^0 + 1 * 2^1 + 0 * 2^2 + 0 * 2^3 + 1 * 2^4 = 18  
 
// 十進(jìn)制到二進(jìn)制
18 / 2 = 9 .... 0 
9 / 2 = 4 .... 1 
4 / 2 = 2 .... 0 
2 / 2 = 1 .... 0 
1 / 2 = 0 .... 1
 
10010

那么,問(wèn)題來(lái)了十進(jìn)制小數(shù)和二進(jìn)制小數(shù)是如何相互轉(zhuǎn)換的呢?

十進(jìn)制小數(shù)到二進(jìn)制小數(shù)一般是整數(shù)部分除 2 取余,逆序排列,小數(shù)部分使用乘 2 取整數(shù)位,順序排列。二進(jìn)制小數(shù)到十進(jìn)制小數(shù)還是使用按權(quán)相加法。

// 二進(jìn)制到十進(jìn)制
10.01 = 1 * 2^-2 + 0 * 2^-1 + 0 * 2^0 + 1 * 2^1 = 2.25
 
// 十進(jìn)制到二進(jìn)制
// 整數(shù)部分
2 / 2 = 1 .... 0
1 / 2 = 0 .... 1
// 小數(shù)部分
0.25 * 2 = 0.5 .... 0 
0.5 * 2 = 1 .... 1 
 
// 結(jié)果 10.01

轉(zhuǎn)小數(shù)我們也了解了,接下來(lái)我們回歸正題,為什么浮點(diǎn)運(yùn)算會(huì)有精度不準(zhǔn)確的問(wèn)題。接下來(lái)我們看一個(gè)簡(jiǎn)單的例子 2.1 這個(gè)十進(jìn)制數(shù)轉(zhuǎn)成二進(jìn)制是什么樣子的。

2.1 分成兩部分
// 整數(shù)部分
2 / 2 = 1 .... 0
1 / 2 = 0 .... 1
 
// 小數(shù)部分
0.1 * 2 = 0.2 .... 0
0.2 * 2 = 0.4 .... 0
0.4 * 2 = 0.8 .... 0
0.8 * 2 = 1.6 .... 1
0.6 * 2 = 1.2 .... 1
0.2 * 2 = 0.4 .... 0
0.4 * 2 = 0.8 .... 0
0.8 * 2 = 1.6 .... 1
0.6 * 2 = 1.2 .... 1
0.2 * 2 = 0.4 .... 0
0.4 * 2 = 0.8 .... 0
0.8 * 2 = 1.6 .... 1
0.6 * 2 = 1.2 .... 1
............

落入無(wú)限循環(huán)結(jié)果為 10.0001100110011........ , 我們的計(jì)算機(jī)在存儲(chǔ)小數(shù)時(shí)肯定是有長(zhǎng)度限制的,所以會(huì)進(jìn)行截取部分小數(shù)進(jìn)行存儲(chǔ),從而導(dǎo)致計(jì)算機(jī)存儲(chǔ)的數(shù)值只能是個(gè)大概的值,而不是精確的值。

從這里看出來(lái)我們的計(jì)算機(jī)根本就無(wú)法使用二進(jìn)制來(lái)精確的表示 2.1 這個(gè)十進(jìn)制數(shù)字的值,連表示都無(wú)法精確表示出來(lái),計(jì)算肯定是會(huì)出現(xiàn)問(wèn)題的。

精度運(yùn)算丟失的解決辦法

現(xiàn)有有三種辦法

  • 如果業(yè)務(wù)不是必須非常精確的要求可以采取四舍五入的方法來(lái)忽略這個(gè)問(wèn)題。
  • 轉(zhuǎn)成整型再進(jìn)行計(jì)算。
  • 使用 BCD 碼存儲(chǔ)和運(yùn)算二進(jìn)制小數(shù)(感興趣的同學(xué)可自行搜索學(xué)習(xí))。

一般每種語(yǔ)言都用高精度運(yùn)算的解決方法(比一般運(yùn)算耗費(fèi)性能),比如 Python 的 decimal 模塊,Java 的 BigDecimal,但是一定要把小數(shù)轉(zhuǎn)成字符串傳入構(gòu)造,不然還是有坑,其他語(yǔ)言大家可以自行尋找一下。

# Python 示例
from decimal import Decimal
 
num = Decimal('0.1') + Decimal('0.1') + Decimal('0.1')
print(num)
// Java 示例
import java.math.BigDecimal;
 
BigDecimal add = new BigDecimal("0.1").add(new BigDecimal("0.1")).add(new BigDecimal("0.1"));
System.out.println(add);

拓展:詳解浮點(diǎn)型

上面既然提到了浮點(diǎn)型的存儲(chǔ)是有限制,那么我們看一下我們的計(jì)算機(jī)是如何存儲(chǔ)浮點(diǎn)型的,是不是真的正如我們上面提到的有小數(shù)長(zhǎng)度的限制。

那我們就以 Float 的數(shù)據(jù)存儲(chǔ)結(jié)構(gòu)來(lái)說(shuō),根據(jù) IEEE 標(biāo)準(zhǔn)浮點(diǎn)型分為符號(hào)位,指數(shù)位和尾數(shù)位三部分(各部分大小詳情見(jiàn)下圖)。

IEEE 754 標(biāo)準(zhǔn)

一般情況下我們表示一個(gè)很大或很小的數(shù)通常使用科學(xué)記數(shù)法,例如:1000.00001 我們一般表示為 1.00000001 * 10^3,或者 0.0001001 一般表示為 1.001 * 10^-4。

符號(hào)位

0 是正數(shù),1 是負(fù)數(shù)

指數(shù)位

指數(shù)很有意思因?yàn)樗枰硎菊?fù),所以人們創(chuàng)造了一個(gè)叫 EXCESS 的系統(tǒng)。這個(gè)系統(tǒng)是什么意思呢?它規(guī)定 最大值 / 2 - 1 表示指數(shù)為 0。我們使用單精度浮點(diǎn)型舉個(gè)例子,單精度浮點(diǎn)型指數(shù)位一共有八位,表示的十進(jìn)制數(shù)最大就是 255。那么 255 / 2 - 1 = 127,127 就代表指數(shù)為 0。如果指數(shù)位存儲(chǔ)的十進(jìn)制數(shù)據(jù)為 128 那么指數(shù)就是 128 - 127 = 1,如果存儲(chǔ)的為 126,那么指數(shù)就是 126 - 127 = -1。

尾數(shù)位

比如上述例子中 1.00000001 以及 1.001 就屬于尾數(shù),但是為什么叫尾數(shù)呢?因?yàn)樵诙M(jìn)制中比如 1.xx 這個(gè)小數(shù),小數(shù)點(diǎn)前面的 1 是永遠(yuǎn)存在的,存了也是浪費(fèi)空間不如多存一位小數(shù),所以尾數(shù)位只會(huì)存儲(chǔ)小數(shù)部分。也就是上述例子中的 00000001 以及 001 存儲(chǔ)這樣的數(shù)據(jù)。

IEEE 754 標(biāo)準(zhǔn)

通過(guò)上述程序我們得到的存儲(chǔ) 1.25 的 float 二進(jìn)制結(jié)構(gòu)的具體值為 00111111101000000000000000000000 ,我們拆分一下 0 為符號(hào)位他是個(gè)正值。01111111 為指數(shù)位,01000000000000000000000 是尾數(shù)。接下來(lái)我們驗(yàn)證一下 01111111 轉(zhuǎn)為十進(jìn)制是 127,那么經(jīng)過(guò)計(jì)算指數(shù)為 0。尾數(shù)是 01000000000000000000000 加上默認(rèn)省略的 1 為 1.01(省略后面多余的 0),轉(zhuǎn)換為十進(jìn)制小數(shù)就是 1.25。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • linux c程序中獲取shell腳本輸出的實(shí)現(xiàn)方法

    linux c程序中獲取shell腳本輸出的實(shí)現(xiàn)方法

    以下是對(duì)在linux下c程序中獲取shell腳本輸出的實(shí)現(xiàn)方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友可以過(guò)來(lái)參考下
    2013-08-08
  • C++ string字符串的修改與替換方法詳析

    C++ string字符串的修改與替換方法詳析

    這篇文章主要給大家介紹了關(guān)于C++ string字符串修改與替換方法的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • C語(yǔ)言代碼實(shí)現(xiàn)掃雷游戲

    C語(yǔ)言代碼實(shí)現(xiàn)掃雷游戲

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言代碼實(shí)現(xiàn)掃雷游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-02-02
  • 詳解C++編程中的文件流與字符串流

    詳解C++編程中的文件流與字符串流

    這篇文章主要介紹了C++編程中的文件流與字符串流,是C++入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下
    2015-09-09
  • Qt實(shí)現(xiàn)網(wǎng)易云音樂(lè)進(jìn)度條效果

    Qt實(shí)現(xiàn)網(wǎng)易云音樂(lè)進(jìn)度條效果

    這篇文章主要為大家詳細(xì)介紹了Qt實(shí)現(xiàn)網(wǎng)易云音樂(lè)進(jìn)度條效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-08-08
  • C++淺析虛函數(shù)使用方法

    C++淺析虛函數(shù)使用方法

    對(duì)C++了解的人都應(yīng)該知道虛函數(shù)(Virtual Function)是通過(guò)一張?zhí)摵瘮?shù)表(Virtual Table)來(lái)實(shí)現(xiàn)的。簡(jiǎn)稱為V-Table。本文就將詳細(xì)講講虛函數(shù)表的原理與使用,需要的可以參考一下
    2022-08-08
  • C/C++ 進(jìn)程通訊(命名管道)的實(shí)例

    C/C++ 進(jìn)程通訊(命名管道)的實(shí)例

    下面小編就為大家?guī)?lái)一篇C/C++ 進(jìn)程通訊(命名管道)的實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-08-08
  • Visual Studio Code 配置C、C++環(huán)境/編譯并運(yùn)行的流程分析

    Visual Studio Code 配置C、C++環(huán)境/編譯并運(yùn)行的流程分析

    這篇文章主要介紹了Visual Studio Code 配置C、C++環(huán)境/編譯并運(yùn)行的流程分析,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-05-05
  • C++中String增刪查改模擬實(shí)現(xiàn)方法舉例

    C++中String增刪查改模擬實(shí)現(xiàn)方法舉例

    這篇文章主要給大家介紹了關(guān)于C++中String增刪查改模擬實(shí)現(xiàn)方法的相關(guān)資料,String是C++中的重要類型,程序員在C++面試中經(jīng)常會(huì)遇到關(guān)于String的細(xì)節(jié)問(wèn)題,甚至要求當(dāng)場(chǎng)實(shí)現(xiàn)這個(gè)類,需要的朋友可以參考下
    2023-11-11
  • C語(yǔ)言實(shí)現(xiàn)數(shù)組移位、前移、后移與整體移動(dòng)實(shí)例代碼

    C語(yǔ)言實(shí)現(xiàn)數(shù)組移位、前移、后移與整體移動(dòng)實(shí)例代碼

    C語(yǔ)言中通??梢允褂醚h(huán)語(yǔ)句實(shí)現(xiàn)數(shù)組的移動(dòng),下面這篇文章主要給大家介紹了關(guān)于C語(yǔ)言實(shí)現(xiàn)數(shù)組移位、前移、后移與整體移動(dòng)的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-03-03

最新評(píng)論