python浮點型精度損失問題_解決float型數據精度損失問題

問題:浮點型數據存儲方式會導致數據精度損失,增大計算誤差。

float fval = 0.45;  // 單步調試發現其真實值為:0.449999988

double dval = 0.45; // 單步調試發現其真實值為:0.45000000000000001

當很多個這樣的單精度浮點型數據進行運算時,就會有累積誤差,使得運算結果達不到理想的結果。尤其是對那種需要判斷相等的情況(浮點型數據判斷相等會有誤差)。

因此我們可以通過把浮點型數據放大1e6倍,把它賦給一個整型變量,把得到的結果再除以1e6,就會使精度損失降到最低。

float a = 0.45;    // 0.449999988double b = 0.45;int c = 1e6 *b;    // 450000double d1 = 2 *a;   // 0.89999997615814209int d = 2 *c;     // 900000double d2 = d / 1e6; // 0.90000000000000002, 推薦方式

double d3 = 2 * 0.45; // 0.90000000000000002

下面是浮點型數據存儲方式,轉自:https://www.cnblogs.com/wuyuan2011woaini/p/4105765.html

C語言和 C#語言中,對于浮點型的數據采用單精度類型(float)和雙精度類型(double)來存儲:

float 數據占用 32bit;

double 數據占用 64bit;

我們在聲明一個變量 float f = 2.25f 的時候,是如何分配內存的呢?

其實不論是 float 類型還是 double 類型,在存儲方式上都是遵從IEEE的規范:

float 遵從的是 IEEE R32.24;

double 遵從的是 IEEE R64.53;

單精度或雙精度在存儲中,都分為三個部分:

符號位(Sign):0代表正數,1代表為負數;

指數位(Exponent):用于存儲科學計數法中的指數數據;

尾數部分(Mantissa):采用移位存儲尾數部分;

單精度 float 的存儲方式如下:

雙精度 double 的存儲方式如下:

R32.24 和 R64.53 的存儲方式都是用科學計數法來存儲數據的,比如:

8.25? 用十進制表示為:8.25 * 100

120.5 用十進制表示為:1.205 * 102

而計算機根本不認識十進制的數據,他只認識0和1。所以在計算機存儲中,首先要將上面的數更改為二進制的科學計數法表示:

8.25?? 用二進制表示為:1000.01

118.5 用二進制表示為:1110110.1

而用二進制的科學計數法表示 1000.1,可以表示為1.0001 * 23

而用二進制的科學計數法表示 1110110.1,可以表示為1.1101101 * 26

任何一個數的科學計數法表示都為1. xxx * 2n,尾數部分就可以表示為xxxx,由于第一位都是1嘛,干嘛還要表示呀?所以將小數點前面的1省略。

由此,23bit的尾數部分,可以表示的精度卻變成了24bit,道理就是在這里。(float有效位數相應的也會發生變化,而double則不會,因達不到)

那 24bit 能精確到小數點后幾位呢?我們知道9的二進制表示為1001,所以 4bit 能精確十進制中的1位小數點,24bit就能使 float 精確到小數點后6位;

而對于指數部分,因為指數可正可負(占1位),所以8位的指數位能表示的指數范圍就只能用7位,范圍是:-127至128。所以指數部分的存儲采用移位存儲,存儲的數據為元數據 +127。

注意:

元數據+127:大概是指“指數”從00000000開始(表示-127)至11111111(表示+128)

所以,10000000表示指數1 (127 + 1 = 128 --> 10000000 ) ;

指數為 3,則為 127 + 3 = 130,表示為 01111111 + 11 = 10000010 ;

下面就看看8.25 和 118.5 在內存中真正的存儲方式:

8.25 用二進制表示為:1000.01

8.25用二進制的科學計數法表示為: 1.0001* 23,按照上面的存儲方式:

符號位為:0,表示為正;

指數位為:3+127=130,即 10000011;

尾數部分為:0001;

故8.25的存儲方式如下圖所示:

而單精度浮點數118.5的存儲方式如下圖所示:

那么如果給出內存中一段數據,并且告訴你是單精度存儲的話,你將如何知道該數據的十進制數值呢?

其實就是對上面運算的反推過程,比如給出如下內存數據:01000010111011010000000000000000,

首先我們現將該數據分段:0? 10000101? 11011010000000000000000,在內存中的存儲就為下圖所示:

根據我們的計算方式,可以計算出這樣一組數據表示為:

1.1101101*2(133-127=6)=1.1101101 * 26= 1110110.1=118.5

而雙精度浮點數的存儲和單精度的存儲大同小異,不同的是指數部分和尾數部分的位數。所以這里不再詳細的介紹雙精度的存儲方式了,只將118.5的最后存儲方式圖給出:

下面就這個知識點來解決一個疑惑,請看下面一段程序,注意觀察輸出結果:

class 浮點數

{

static void Main(string[] args)

{

float f = 2.2f;

double d = (double)f;

Console.WriteLine(d.ToString("0.0000000000000"));

//結果:"2.2000000476837"

f = 2.25f;

d = (double)f;

Console.WriteLine(d.ToString("0.0000000000000"));

//結果:"2.2500000000000"

//2.25 - 2.2 = 0.05 ( 但實際結果不是0.05 )

float f2 = 2.25f - 2.2f;

Console.WriteLine(f2.ToString("0.0000000000000"));

//結果:"0.0499999500000"

}

}

輸出的結果可能讓大家疑惑不解:

單精度的 2.2 轉換為雙精度后,精確到小數點后13位之后變為了2.2000000476837

而單精度的 2.25 轉換為雙精度后,變為了2.2500000000000

為何2.2在轉換后的數值更改了,而 2.25卻沒有更改呢?

其實通過上面關于兩種存儲結果的介紹,我們大概就能找到答案。

2.25的單精度存儲方式表示為:0 10000001 00100000000000000000000

2.25的雙精度存儲方式表示為:0 10000000 0010010000000000000000000000000000000000000000000000000

這樣 2.25 在進行強制轉換的時候,數值是不會變的。

而我們再看看2.2,用科學計數法表示應該為:

將十進制的小數轉換為二進制的小數的方法是:將小數*2,取整數部分。

0.2×2=0.4,所以二進制小數第一位為0.4的整數部分0;

0.4×2=0.8,第二位為0.8的整數部分0;

0.8×2=1.6,第三位為1;

0.6×2=1.2,第四位為1;

0.2×2=0.4,第五位為0;

...... 這樣永遠也不可能乘到=1.0,得到的二進制是一個無限循環的排列 00110011001100110011...

對于單精度數據來說,尾數只能表示 24bit 的精度,所以2.2的 float 存儲為:

但是這種存儲方式,換算成十進制的值,卻不會是2.2。

因為在十進制轉換為二進制的時候可能會不準確(如:2.2),這樣就導致了誤差問題!

并且 double 類型的數據也存在同樣的問題!

所以在浮點數表示中,都可能會不可避免的產生些許誤差!

在單精度轉換為雙精度的時候,也會存在同樣的誤差問題。

而對于有些數據(如2.25),在將十進制轉換為二進制表示的時候恰好能夠計算完畢,所以這個誤差就不會存在,也就出現了上面比較奇怪的輸出結果。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/534297.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/534297.shtml
英文地址,請注明出處:http://en.pswp.cn/news/534297.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux配置本地yum源(RHEL8)

https://www.cnblogs.com/itwangqiang/p/13391401.html

如何把照片正面變成反面_各國簽證照片要求大全 (含模板)

對于不是很熟悉簽證的小伙伴來說,面對全球那么多國家的簽證而且每張簽證照片的規格不同為此我們為您整理了各國簽證照片要求大全 東南亞國家的簽證照要求基本相同,就以泰國為例,告訴大家簽證照的注意事項。“泰國,新加坡&#xff…

TensorFlow實驗(3)

模型的保存與恢復 我們來簡單實現一下模型的保存與恢復 訓練完TensorFlow模型后,可將其保存為文件,以便于預測新數據時直接加載使用。 TensorFlow模型主要包含網絡的設計或者圖以及已經訓練好的網絡參數的值。 TensorFlow提供的tf.train.Saver()函數…

ad域 禁用賬號_IST-AD域信息同步平臺來襲

IST的AD域信息同步系統是能幫助域管理員簡化日常的一些管理工作,可以讓AD域系統與其他的業務系統進行用戶信息同步,實現自動的新舊用戶帳戶信息的同步修改、組織架構同步調整,并有簡單易操作的配置頁面系統與操作日志查詢等。通過ODBC、Web S…

Linux基礎(firewalld防火墻配置管理工具的圖形用戶界面)

firewall-config的界面如圖所示 我們先將當前區域中請求http服務的流量設置為允許,但僅限當前生效。具體配置如圖 嘗試添加一條防火墻策略規則,使其放行訪問8080-8088端口(TCP協議)的流量,并將其設置為永久生效&#x…

ios 請求失敗封裝_vue_axios請求封裝、異常攔截統一處理

1、前端網絡請求封裝、異常統一處理vue中采用axios處理網絡請求,避免請求接口重復代碼,以及各種網絡情況造成的異常情況的判斷,采用axios請求封裝和異常攔截操作;axios 請求封裝// 引入axios文件包import axios from axios// POST…

Linux基礎(使用ssh服務管理遠程主機1)

配置網絡參數 使用nmtui命令配置網絡參數,以及通過nmcli命令查看網絡信息并管理網絡會話服務。 執行nmtui命令運行網絡配置工具 進入主界面 選中編輯連接并按下回車鍵 選中要編輯的網卡名稱,然后按下Edit(編輯)按鈕 把網絡IPv4 …

聯想g510升級方案_聯想智慧中國行,聚焦第一古城,助力企業智能升級

12月29日,聯想智慧中國行“一起聯想 生態綻放”One Lenovo融合品鑒會邢臺站盛大啟幕,現場近70位河北合作伙伴到場參會,共同探討智能時代帶來的多重挑戰和迎戰機遇。“聯想智慧中國行”是聯想致力于推動中國行業智能化升級舉辦的系列活動&…

軟件工程(總體設計②設計原理)

設計原理 模塊化 模塊是由邊界元素限定的相鄰程序元素的序列,而且有一個總體標識符代表它。 按照模塊的定義,過程、函數、子程序和宏,都可作為模塊。 面向對象方法學中的對象是模塊,對象內的方法也是模塊。模塊是構成程序的基…

TensorFlow構建二維數據擬合模型(3)

占位符與數據喂入機制 placeholder是TensorFlow提供的占位符節點,由tf.placeholder()函數創建,其實質上也是一種變量。占位符沒有初始值,只會分配必要的內存,其值由會話中用戶調用的run()函數傳遞。 占位符聲明的方法如表 函數…

合作開發過程產生的專利_被起訴專利侵權怎么辦?專利律師給你出招!

隨著國內企業專利申請量的增加及專利保護意識的逐步提升,專利侵權訴訟作為常用的商業競爭手段和策略,其數量也隨之呈逐年遞增之勢。考慮到目前國內專利數量較多,且很多專利技術互有交叉,因此在進行產品研發時即使未借鑒他人產品&a…

idea怎么設置代碼提示不區分大小寫_IntelliJ IDEA 這樣設置動圖,棒極了!

轉自:IntelliJ-IDEA-Tutorial/Judas.n鏈接:http://suo.im/6sHdelIntelliJ IDEA 有很多人性化的設置我們必須單獨拿出來講解,也因為這些人性化的設置讓我們這些 IntelliJ IDEA 死忠粉更加死心塌地使用它和分享它。推薦設置IntelliJ IDEA 的代碼…

linux課堂筆記(1)

一、linux特點 1.源代碼開發: *.c *.h 可二次開發 2.安全:可檢測安全性 3.穩定:共享內存 內存沖突(Windows,內存沖突,藍屏死機) 4.網絡服務:server(WWW&#xf…

表字段順序有何影響_「品味保定」炸烹蝦段乾隆贊 百年保定柔雅香

【引文】上溯先賢堯帝,保定傳承已逾千載。燕趙之地、畿輔之疆、北控三關、南通九省、翅衛京師說的就是古城保定。千百年來,古城保定形成了獨具特色的飲食文化,精致氣派的直隸官府菜就是這種文化的結晶。品百年保定酒,嘗直隸官府菜…

linux課堂筆記(2)

linux安裝 1,虛擬機:VMware12 (1)注冊:商業軟件(集成序列號,注冊機(根據加密算法生成序列號),文本文件(記錄序列號)) &…

testufo測試刷新率測試_上手體驗微星電競顯示器PAG301CR:200Hz高刷新率只是它的小亮點...

電子競技行業高速發展,逐漸成為體育產業版圖中重要的一塊,產業鏈也在不斷完善,人們對于電子競技的認識也是越來越深刻,同時越來越多的人加入了電子競技行列中,但是電競游戲除了水平外,設備同樣也尤其重要,特…

linux課堂筆記(3)

1,linux廠商版本信息 內核信息:主版本.修正號.發行號.on 硬件平臺. 主機名 登錄 身份認證 localhost login:root password:隱藏口令 提示上一次登錄時間與地點 用戶名主機名 當前目錄 指令提示符 rootlocalhost ]# 注&…

graphpad做折線圖坐標軸數字_pandas做數據可視化具體操作,快來看看吧

來自公眾號:大鄧和他的Python常見的數據可視化庫有:matplotlib 是最常見的2維庫,可以算作可視化的必備技能庫,由于matplotlib是比較底層的庫,api很多,代碼學起來不太容易。seaborn 是建構于matplotlib基礎上&#xff0…

linux課堂筆記(4)

常用指令:mv 1,隱藏文件 (1)windows通過設置屬性隱藏文件 attrib h s 文件 attrib h s d:\net.txt attrib h s d:\net.txt (2)linux系統文件以點開始命名隱藏文件 mv /路徑/源文件 /路徑/目標…

實現輸入提示 layui_ASP.NET Core SignalR :學習消息通訊,實現一個消息通知

什么是 SignalR目前我用業余時間正在做一個博客系統,其中有個功能就是評論通知,就是假如A用戶評論B用戶的時候,如果B用戶首頁處于打開狀態,那么就會提示B用戶有未讀消息。暫時用SignalR來實現這個功能。我也是看了兩天的資料才明白…