最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳談浮點精度(float、double)運算不精確的原因

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

為什么浮點精度運算會有問題

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

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

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

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

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

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

// 二進制到十進制
10010 = 0 * 2^0 + 1 * 2^1 + 0 * 2^2 + 0 * 2^3 + 1 * 2^4 = 18  
 
// 十進制到二進制
18 / 2 = 9 .... 0 
9 / 2 = 4 .... 1 
4 / 2 = 2 .... 0 
2 / 2 = 1 .... 0 
1 / 2 = 0 .... 1
 
10010

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

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

// 二進制到十進制
10.01 = 1 * 2^-2 + 0 * 2^-1 + 0 * 2^0 + 1 * 2^1 = 2.25
 
// 十進制到二進制
// 整數(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ù)我們也了解了,接下來我們回歸正題,為什么浮點運算會有精度不準(zhǔn)確的問題。接下來我們看一個簡單的例子 2.1 這個十進制數(shù)轉(zhuǎ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
............

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

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

精度運算丟失的解決辦法

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

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

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

# 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);

拓展:詳解浮點型

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

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

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

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

符號位

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

指數(shù)位

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

尾數(shù)位

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

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

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

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

相關(guān)文章

  • opencv實現(xiàn)圖像傾斜校正

    opencv實現(xiàn)圖像傾斜校正

    這篇文章主要為大家詳細(xì)介紹了opencv實現(xiàn)圖像傾斜校正,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-07-07
  • C++ 類模板、函數(shù)模板全特化、偏特化的使用

    C++ 類模板、函數(shù)模板全特化、偏特化的使用

    這篇文章主要介紹了C++ 類模板、函數(shù)模板全特化、偏特化的使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-02-02
  • C指針原理教程之語法樹及其實現(xiàn)

    C指針原理教程之語法樹及其實現(xiàn)

    本文給大家分享的是如何使用C語言的指針原來來實現(xiàn)語法樹,并給大家提供了詳細(xì)的實例代碼,希望大家能夠喜歡
    2019-02-02
  • C語言數(shù)組的各種操作梳理

    C語言數(shù)組的各種操作梳理

    數(shù)組是一組有序的數(shù)據(jù)的集合,數(shù)組中元素類型相同,由數(shù)組名和下標(biāo)唯一地確定,數(shù)組中數(shù)據(jù)不僅數(shù)據(jù)類型相同,而且在計算機內(nèi)存里連續(xù)存放,地址編號最低的存儲單元存放數(shù)組的起始元素,地址編號最高的存儲單元存放數(shù)組的最后一個元素
    2022-04-04
  • 詳解C語言中常用的失敗退出和成功返回

    詳解C語言中常用的失敗退出和成功返回

    在 C 語言中,封裝函數(shù)時,我們通常需要判斷函數(shù)調(diào)用是否成功,并據(jù)此采取不同的操作,例如,在調(diào)用系統(tǒng)函數(shù)或庫函數(shù)時,我們通常會使用一些錯誤處理機制,來輸出錯誤信息、退出程序或返回不同的狀態(tài),以下是如何參考使用這些錯誤處理機制的示例,需要的朋友可以參考下
    2024-12-12
  • C++ 內(nèi)存管理原理分析

    C++ 內(nèi)存管理原理分析

    本章主要介紹C++的內(nèi)存管理,以C++的內(nèi)存分布作為引入,介紹C++不同于C語言的內(nèi)存管理方式(new delete對比 malloc free),最后為了加深讀者的理解,會介紹new和delete的底層實現(xiàn)原理
    2021-11-11
  • Qt實現(xiàn)進程界面之間的鼠標(biāo)焦點切換

    Qt實現(xiàn)進程界面之間的鼠標(biāo)焦點切換

    這篇文章主要為大家詳細(xì)介紹了Qt實現(xiàn)進程界面之間的鼠標(biāo)焦點切換,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-09-09
  • C++ OpenCV繪制幾何圖形

    C++ OpenCV繪制幾何圖形

    這篇文章主要為大家詳細(xì)介紹了C++ OpenCV繪制幾何圖形,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-10-10
  • OpenGL實現(xiàn)3D空間中移動圖像

    OpenGL實現(xiàn)3D空間中移動圖像

    這篇文章主要為大家詳細(xì)介紹了OpenGL實現(xiàn)3D空間中移動圖像,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-08-08
  • C++ 中繼承與動態(tài)內(nèi)存分配的詳解

    C++ 中繼承與動態(tài)內(nèi)存分配的詳解

    這篇文章主要介紹了C++ 中繼承與動態(tài)內(nèi)存分配的詳解的相關(guān)資料,這里提供實例幫助大家學(xué)習(xí)理解這部分內(nèi)容,需要的朋友可以參考下
    2017-08-08

最新評論

淮阳县| 平度市| 盖州市| 读书| 旬邑县| 朝阳市| 商丘市| 赤壁市| 江陵县| 尉氏县| 浮梁县| 镇赉县| 乐清市| 台湾省| 望谟县| 蚌埠市| 柳林县| 千阳县| 封丘县| 武陟县| 富蕴县| 平山县| 大姚县| 沁源县| 平乐县| 平山县| 张家港市| 陈巴尔虎旗| 登封市| 枣强县| 周宁县| 搜索| 青田县| 鄂托克旗| 宜川县| 彰化县| 阿城市| 咸丰县| 南和县| 股票| 收藏|