一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - C/C++ - 詳談浮點精度(float、double)運算不精確的原因

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

2022-03-11 13:56marco__ C/C++

這篇文章主要介紹了詳談浮點精度(float、double)運算不精確的原因,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

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

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

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

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

那么精度運算不準確這是為什么呢?

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

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

// 二進制到十進制
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

那么,問題來了十進制小數和二進制小數是如何相互轉換的呢?

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

// 二進制到十進制
10.01 = 1 * 2^-2 + 0 * 2^-1 + 0 * 2^0 + 1 * 2^1 = 2.25

// 十進制到二進制
// 整數部分
2 / 2 = 1 .... 0
1 / 2 = 0 .... 1
// 小數部分
0.25 * 2 = 0.5 .... 0 
0.5 * 2 = 1 .... 1 

// 結果 10.01

轉小數我們也了解了,接下來我們回歸正題,為什么浮點運算會有精度不準確的問題。接下來我們看一個簡單的例子 2.1 這個十進制數轉成二進制是什么樣子的。

2.1 分成兩部分
// 整數部分
2 / 2 = 1 .... 0
1 / 2 = 0 .... 1

// 小數部分
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
............

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

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

 

精度運算丟失的解決辦法

現有有三種辦法

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

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

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

 

拓展:詳解浮點型

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

那我們就以 Float 的數據存儲結構來說,根據 IEEE 標準浮點型分為符號位,指數位和尾數位三部分(各部分大小詳情見下圖)。

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

IEEE 754 標準

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

符號位

0 是正數,1 是負數

指數位

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

尾數位

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

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

IEEE 754 標準

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

以上為個人經驗,希望能給大家一個參考,也希望大家多多支持服務器之家。

原文鏈接:https://blog.csdn.net/marco__/article/details/102515668

延伸 · 閱讀

精彩推薦
  • C/C++C++之重載 重定義與重寫用法詳解

    C++之重載 重定義與重寫用法詳解

    這篇文章主要介紹了C++之重載 重定義與重寫用法詳解,本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內容,需要的朋友可以參考下...

    青山的青6062022-01-04
  • C/C++深入理解goto語句的替代實現方式分析

    深入理解goto語句的替代實現方式分析

    本篇文章是對goto語句的替代實現方式進行了詳細的分析介紹,需要的朋友參考下...

    C語言教程網7342020-12-03
  • C/C++C/C++經典實例之模擬計算器示例代碼

    C/C++經典實例之模擬計算器示例代碼

    最近在看到的一個需求,本以為比較簡單,但花了不少時間,所以下面這篇文章主要給大家介紹了關于C/C++經典實例之模擬計算器的相關資料,文中通過示...

    jia150610152021-06-07
  • C/C++詳解c語言中的 strcpy和strncpy字符串函數使用

    詳解c語言中的 strcpy和strncpy字符串函數使用

    strcpy 和strcnpy函數是字符串復制函數。接下來通過本文給大家介紹c語言中的strcpy和strncpy字符串函數使用,感興趣的朋友跟隨小編要求看看吧...

    spring-go5642021-07-02
  • C/C++C語言實現電腦關機程序

    C語言實現電腦關機程序

    這篇文章主要為大家詳細介紹了C語言實現電腦關機程序,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    xiaocaidayong8482021-08-20
  • C/C++學習C++編程的必備軟件

    學習C++編程的必備軟件

    本文給大家分享的是作者在學習使用C++進行編程的時候所用到的一些常用的軟件,這里推薦給大家...

    謝恩銘10102021-05-08
  • C/C++C語言中炫酷的文件操作實例詳解

    C語言中炫酷的文件操作實例詳解

    內存中的數據都是暫時的,當程序結束時,它們都將丟失,為了永久性的保存大量的數據,C語言提供了對文件的操作,這篇文章主要給大家介紹了關于C語言中文件...

    針眼_6702022-01-24
  • C/C++c++ 單線程實現同時監聽多個端口

    c++ 單線程實現同時監聽多個端口

    這篇文章主要介紹了c++ 單線程實現同時監聽多個端口的方法,幫助大家更好的理解和學習使用c++,感興趣的朋友可以了解下...

    源之緣11542021-10-27
主站蜘蛛池模板: 美女18隐私羞羞视频网站 | 国产精品亚洲片在线不卡 | 91九色麻豆 | 秘书在办公室疯狂被hd | 日本三级做a全过程在线观看 | 国产情侣偷国语对白 | 九九精品国产亚洲A片无码 九九99热久久999精品 | 女主被当众调教虐np | 麻豆网站视频国产在线观看 | 国产亚洲精品一区二区在线播放 | 亚洲bt区 | 免费一级欧美大片在线观看 | 五月天精品视频在线观看 | 大象传媒2021秘密入口 | 亚洲精品中文字幕第一区 | 天天碰夜夜操 | 女人又色又爽又黄 | 日韩香蕉视频 | 四虎黄色网址 | 欧美一级级a在线观看 | 色噜噜狠狠狠综合曰曰曰88av | 男人狂躁女人下半身 | 99热在线获取最新地址 | 国产欧美日韩不卡一区二区三区 | 国产亚洲综合成人91精品 | 日韩视频一| 亚洲系列国产系列 | 五月婷婷丁香色 | 色欧美在线| 久久综合中文字幕佐佐木希 | 午夜小视频免费 | 亚洲成人免费 | 草草在线影院 | 亚洲高清在线视频 | 美女和男人免费网站视频 | 韩日视频在线观看 | 欧美高清国产 | 欧美一区二区三区四区在线观看 | 亚洲男人的天堂成人 | 国产99久久精品一区二区 | 热辣小秘书办公室 |