計算機中的浮點數 - 為什么十進制的 0.1 在計算機中是一個無限循環小數

計算機中的浮點數 - 為什么十進制的 0.1 在計算機中是一個無限循環小數

flyfish

用 float 或 double 來存儲小數時不是精確值

浮點數在計算機中是以二進制形式存儲的,通常使用 IEEE 754 標準。浮點數由三個部分組成:符號位、指數位和尾數位。
先看一個例子

#include <iostream>
#include <iomanip>using namespace std;int main()
{cout << "Hello World!" << endl;double x = 1.0 / 10.0;double y = 1.0 - 0.9;double z = 1.0 + 0.1;// 設置輸出精度cout << fixed << setprecision(17);// 觀察 x、y、z 的結果cout << "x = " << x << endl;cout << "y = " << y << endl;cout << "z = " << z << endl;return 0;
}
Hello World!
x = 0.10000000000000001
y = 0.09999999999999998
z = 1.10000000000000009

浮點數比較

由于浮點數運算可能產生微小的誤差,在比較浮點數時,應避免直接使用 ==。可以定義一個非常小的數(稱為 epsilon)來進行比較。

#include <cmath>
#include <iostream>bool isEqual(double a, double b, double epsilon = 1e-10) {return std::fabs(a - b) < epsilon;
}int main() {double a = 0.1 * 3;double b = 0.3;if (isEqual(a, b)) {std::cout << "a and b are equal." << std::endl;} else {std::cout << "a and b are not equal." << std::endl;}return 0;
}
a and b are equal.

float 和 double 類型的 0.1 并不相等,因為它們在二進制中的表示不完全相同

#include <iostream>
#include <iomanip>int main() {float a = 0.1f;double b = 0.1;std::cout << std::setprecision(20);std::cout << "float a = 0.1f: " << a << std::endl;std::cout << "double b = 0.1: " << b << std::endl;if (a == b) {std::cout << "a and b are equal." << std::endl;} else {std::cout << "a and b are not equal." << std::endl;}return 0;
}
float a = 0.1f: 0.10000000149011611938
double b = 0.1: 0.10000000000000000555
a and b are not equal.

float 和 double 的精度差異

#include <iostream>
#include <iomanip>
#include <string>
#include <sstream>int main() {float floatNum = 1.0f / 7.0f;double doubleNum = 1.0 / 7.0;// 設置輸出精度std::cout << std::fixed << std::setprecision(64);// 輸出 float 和 double 的值std::cout << "float:  " << floatNum << std::endl;std::cout << "double: " << doubleNum << std::endl;return 0;
}
float:  0.1428571492433547973632812500000000000000000000000000000000000000
double: 0.1428571428571428492126926812488818541169166564941406250000000000

將循環小數轉換為分數

#include <iostream>
#include <string>
#include <sstream>
#include <cmath>
#include <iomanip>// 定義一個結構來表示分數
struct Fraction {long long numerator;long long denominator;
};// 最大公約數
long long gcd(long long a, long long b) {return b == 0 ? a : gcd(b, a % b);
}// 將小數部分轉換為分數
Fraction repeatingDecimalToFraction(const std::string& decimal) {size_t pos = decimal.find('(');std::string nonRepeatingPart = decimal.substr(0, pos);std::string repeatingPart = decimal.substr(pos + 1, decimal.size() - pos - 2);// 非循環部分和循環部分長度int n = nonRepeatingPart.size() - 2; // 減去 "0." 的長度int m = repeatingPart.size();// 非循環部分的小數double nonRepeatingDecimal = std::stod(nonRepeatingPart);// 構造非循環部分的分數long long nonRepeatingNumerator = static_cast<long long>(nonRepeatingDecimal * std::pow(10, n));long long nonRepeatingDenominator = std::pow(10, n);// 構造循環部分的分數long long repeatingNumerator = std::stoll(repeatingPart);long long repeatingDenominator = std::pow(10, m) - 1;// 將循環部分的分數移動到正確的位置repeatingNumerator += nonRepeatingNumerator * repeatingDenominator;repeatingDenominator *= nonRepeatingDenominator;// 簡化分數long long divisor = gcd(repeatingNumerator, repeatingDenominator);repeatingNumerator /= divisor;repeatingDenominator /= divisor;return {repeatingNumerator, repeatingDenominator};
}int main() {std::string decimal = "0.285714(285714)";Fraction fraction = repeatingDecimalToFraction(decimal);std::cout << "Fraction: " << fraction.numerator << "/" << fraction.denominator << std::endl;return 0;
}
Fraction: 2/7

查看浮點數的IEEE 754表示

IEEE 754表示:這是浮點數在計算機內存中的存儲格式,包含了符號、指數和尾數。用于浮點數計算和存儲。

#include <iostream>
#include <bitset>
#include <iomanip>void printFloatBinary(float number) {// 將 float 類型重新解釋為 uint32_t 類型uint32_t binary = *reinterpret_cast<uint32_t*>(&number);std::bitset<32> bits(binary);std::cout << "Float: " << number << std::endl;std::cout << "Binary: " << bits << std::endl;
}void printDoubleBinary(double number) {// 將 double 類型重新解釋為 uint64_t 類型uint64_t binary = *reinterpret_cast<uint64_t*>(&number);std::bitset<64> bits(binary);std::cout << "Double: " << number << std::endl;std::cout << "Binary: " << bits << std::endl;
}int main() {float floatNum = 0.1f;double doubleNum = 0.1;printFloatBinary(floatNum);printDoubleBinary(doubleNum);return 0;
}
Float: 0.1
Binary: 00111101110011001100110011001101
Double: 0.1
Binary: 0011111110111001100110011001100110011001100110011001100110011010

符號位:第 1 位
指數位:
對于 float(32 位):第 2 到第 9 位(共 8 位)
對于 double(64 位):第 2 到第 12 位(共 11 位)
尾數位:
對于 float(32 位):第 10 到第 32 位(共 23 位)
對于 double(64 位):第 13 到第 64 位(共 52 位)

手工將0.1轉換為二進制

轉換整數部分:0(已經是零)

  1. 0.1 × 2 = 0.2 (整數部分:0)
  2. 0.2 × 2 = 0.4 (整數部分:0)
  3. 0.4 × 2 = 0.8 (整數部分:0)
  4. 0.8 × 2 = 1.6 (整數部分:1)
  5. 0.6 × 2 = 1.2 (整數部分:1)
  6. 0.2 × 2 = 0.4 (整數部分:0)
  7. 0.4 × 2 = 0.8 (整數部分:0)
  8. 0.8 × 2 = 1.6 (整數部分:1)
  9. 0.6 × 2 = 1.2 (整數部分:1)
  10. 0.2 × 2 = 0.4 (整數部分:0)

合并整數部分

將上述每一步的整數部分合并起來:

0. 1 10 = 0.0001100110011001100110011001100 … 2 0.1_{10} = 0.0001100110011001100110011001100 \ldots_2 0.110?=0.00011001100110011001100110011002?

最終得到的二進制表示是一個無限循環小數:
0. 1 10 = 0. ( 0001100110011001100110011001100 … ) 2 0.1_{10} = 0.(0001100110011001100110011001100 \ldots)_2 0.110?=0.(0001100110011001100110011001100)2?
其中,上面的橫線表示循環節: 0001 1001  ̄ 0001\overline{1001} 00011001

IEEE 754表示與32位二進制表示的關系

小數二進制表示

我們前面計算的0.1的小數二進制表示(0.0001100110011001100110011001100…)是直接將小數部分轉換為二進制的結果,這是一個無限循環的小數。

IEEE 754 二進制浮點數表示

而“00111101110011001100110011001101”是0.1在計算機中存儲時的IEEE 754標準的32位單精度浮點數表示。IEEE 754標準規定了浮點數的存儲格式,包括符號位、指數位和尾數(或稱為有效數字位)。

IEEE 754 單精度浮點數表示解釋

IEEE 754單精度浮點數使用32位來表示一個浮點數,其中:

  • 1位用于符號位
  • 8位用于指數位
  • 23位用于尾數位
    以0.1 為例
  1. 符號位:0 表示正數。
  2. 將0.1轉化為二進制:0.0001100110011001100110011001100110011001100110011001100…(無限循環)
  3. 規格化二進制:將其表示為 1.xxxxxx × 2^(-4) 的形式,所以 0.1 = 1.10011001100110011001101 × 2^(-4)
  4. 指數:由于偏移量為127,所以儲存的指數為 -4 + 127 = 123(即二進制的01111011)
  5. 尾數:取1后面的23位:10011001100110011001101
    合并這些部分后得到IEEE 754表示:
    0 ∣ 01111011 ∣ 10011001100110011001101 0 | 01111011 | 10011001100110011001101 0∣01111011∣10011001100110011001101

這就對應我們之前看到的32位二進制:
00111101110011001100110011001101 00111101110011001100110011001101 00111101110011001100110011001101

數據類型大小指數位尾數位偏移量
binary1616 位5 位10 位15
binary3232 位8 位23 位127
binary6464 位11 位52 位1023
binary128128 位15 位112 位16383

ratio來處理有理數

#include <iostream>
#include <ratio>int main() {// 定義分數類型using MyRatio = std::ratio<1, 3>;// 獲取分子和分母constexpr int numerator = MyRatio::num;constexpr int denominator = MyRatio::den;std::cout << "Fraction: " << numerator << "/" << denominator << std::endl;return 0;
}
Fraction: 1/3

自定義類實現用分數精確表達浮點數

#include <iostream>
#include <numeric> // for std::gcd
#include <iomanip>
class Fraction {
public:Fraction(long long numerator, long long denominator) : numerator(numerator), denominator(denominator) {reduce();}// 加法運算Fraction operator+(const Fraction& other) const {long long new_numerator = numerator * other.denominator + other.numerator * denominator;long long new_denominator = denominator * other.denominator;return Fraction(new_numerator, new_denominator);}// 減法運算Fraction operator-(const Fraction& other) const {long long new_numerator = numerator * other.denominator - other.numerator * denominator;long long new_denominator = denominator * other.denominator;return Fraction(new_numerator, new_denominator);}// 乘法運算Fraction operator*(const Fraction& other) const {return Fraction(numerator * other.numerator, denominator * other.denominator);}// 除法運算Fraction operator/(const Fraction& other) const {return Fraction(numerator * other.denominator, denominator * other.numerator);}// 輸出friend std::ostream& operator<<(std::ostream& os, const Fraction& fraction) {os << fraction.numerator << "/" << fraction.denominator;return os;}private:long long numerator;long long denominator;// 約分void reduce() {long long gcd_value = std::gcd(numerator, denominator);numerator /= gcd_value;denominator /= gcd_value;if (denominator < 0) {numerator = -numerator;denominator = -denominator;}}
};int main() {Fraction frac1(1, 10); // 0.1Fraction frac2(1, 3);  // 1/3std::cout << "Fraction 1: " << frac1 << std::endl;std::cout << "Fraction 2: " << frac2 << std::endl;Fraction sum = frac1 + frac2;Fraction diff = frac1 - frac2;Fraction prod = frac1 * frac2;Fraction quot = frac1 / frac2;std::cout << "Sum: " << sum << std::endl;std::cout << "Difference: " << diff << std::endl;std::cout << "Product: " << prod << std::endl;std::cout << "Quotient: " << quot << std::endl;return 0;
}
Fraction 1: 1/10
Fraction 2: 1/3
Sum: 13/30
Difference: -7/30
Product: 1/30
Quotient: 3/10

64位的存儲空間,雖然范圍很大,但如果分子和分母的值超出這個范圍,仍然會發生溢出。
對于非常大的數,gcd 函數的計算可能會變得非常慢,因為它需要計算兩個大數的最大公約數。
如果要處理極其巨大的數,即使它們沒有溢出,內存消耗也是一個問題。
在實踐中可以先測試下 Boost Multiprecision 這樣的庫。

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

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

相關文章

【2024】LeetCode HOT 100——圖論

目錄 1. 島嶼數量1.1 C++實現1.2 Python實現1.3 時空分析2. 腐爛的橘子2.1 C++實現2.2 Python實現2.3 時空分析3. 課程表3.1 C++實現3.2 Python實現3.3 時空分析4. 實現 Trie (前綴樹)4.1 C++實現4.2 Python實現4.3 時空分析1. 島嶼數量 ?? 原題鏈接:200. 島嶼數量 經典的Fl…

鴻蒙應用開發之OpenGL的EGL

要開發OpenGL程序,那么這個程序就需要與操作系統進行交流,由于存在不同的操作系統,這樣就面臨著開發不同的程序的困難,為了解決這個問題,就引入了一個中間層的軟件庫,這個軟件庫叫做EGL。 眾所周知,Opengl是跨平臺的,那么面對各種平臺的差異性,Opengl是如何抹平而做到…

CleanMyMacX2024讓你的蘋果電腦重獲生機!

在電腦使用過程中&#xff0c;你是否遇到過這樣的問題&#xff1a;運行速度變慢、磁盤空間不足、系統出現故障……這些問題不僅影響你的工作效率&#xff0c;還會讓電腦的使用壽命大大縮短。那么&#xff0c;如何輕松解決這些問題呢&#xff1f;答案就是CleanMyMac X。 CleanM…

蘇州大廠面試題JAVA 面試集

基礎知識1、強引用、軟引用、弱引用、幻象引用有什么區別?(java基礎) 答案參考:https://time.geekbang.org/column/article/6970 2、 對比Hashtable、HashMap、TreeMap有什么不同?(數據結構) 答案參考:https://time.geekbang.org/column/article/8053 3、一個線程調用兩次…

ubuntu20.04安裝kazam桌面屏幕錄制工具

在Ubuntu 20.04上安裝Kazam可以通過以下步驟進行&#xff1a; 1.打開終端&#xff1a;可以通過按下Ctrl Alt T組合鍵來打開終端。 2.添加PPA源&#xff1a;Kazam不再在官方Ubuntu倉庫中&#xff0c;但可以通過PPA源進行安裝。在終端中輸入以下命令來添加PPA&#xff1a; su…

AI繪畫:P圖如此絲滑,OpenAI上線ChatGPT圖像編輯功能,DallE-3繪畫如此簡單

大家好我是極客菌&#xff0c;用ChatGPT的DallE-3進行AI繪畫對很多人來說是一個門檻很低的選擇&#xff0c;現在OpenAI又重磅上線了圖像編輯器功能(DallE editor)&#xff0c;可以很方便的對圖片的局部進行修改&#xff0c;而且支持中文&#xff0c;主打一個功能強大且好用&…

Jquery 獲得Form下的所有text、checkbox等表單的值

Jquery使用表單我主要是想獲得某一個表單下的所有text獲得checkbox的值: 可以這樣寫: var parameter{}; $("input[typetext]",document.forms[0]).each(function(){ alert(this.name); }); 獲得所有名為hobby的選中的checkbox的值和form2下的所有text的值 function s…

【云原生】Prometheus 使用詳解

目錄 一、前言 二、服務監控概述 2.1 什么是微服務監控 2.2 微服務監控指標 2.3 微服務監控工具 三、Prometheus概述 3.1 Prometheus是什么 3.2 Prometheus 特點 3.3 Prometheus 架構圖 3.3.1 Prometheus核心組件 3.3.2 Prometheus 工作流程 3.4 Prometheus 應用場景…

Elasticsearch 配置

Elasticsearch提供良好的默認設置&#xff0c;并且只需要很少的配置。可以使用群集更新設置API在正在運行的群集上更改大多數設置。 配置文件應包含特定于節點的設置&#xff08;如node.name和paths&#xff09;&#xff0c;或節點為了能夠加入集群而需要的設置&#xff0c;如…

webrtc-m120編譯 (m126)

WebRTC實時互動入門 環境 lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 22.04.4 LTS Release: 22.04 Codename: jammyuname -a Linux yqw-Lenovo-XiaoXinPro-13ARE-2020

Agent技術在現代軟件開發與應用中的探索

一、引言 隨著計算機科學的快速發展&#xff0c;Agent技術作為人工智能和分布式計算領域的重要分支&#xff0c;已經滲透到軟件開發的各個方面。Agent技術通過賦予軟件實體自主性和交互性&#xff0c;使得軟件系統能夠更加智能、靈活地響應環境變化和用戶需求。本文將對Agent技…

java基于ssm+jsp 個人交友網站

1前臺首頁功能模塊 個人交友網站&#xff0c;在系統首頁可以查看首頁、交友信息、線下活動、系統公告、論壇信息、我的、跳轉到后臺、客服等內容&#xff0c;如圖1所示。 圖1系統功能界面圖 用戶注冊&#xff0c;在用戶注冊頁面可以填寫用戶賬號、密碼、用戶姓名、年齡等信息進…

深入理解 Spring MVC:原理與架構解析

文章目錄 前言一、MVC二、Spring MVC三、Spring MVC 工作流程四、小結推薦閱讀 前言 Spring MVC 是一種基于 Java 的 Web 應用開發框架&#xff0c;它通過模型-視圖-控制器&#xff08;Model-View-Controller, MVC&#xff09;的設計模式來組織和管理 Web 應用程序。本文將深入…

java基于ssm+jsp 二手車交易網站

1用戶功能模塊 定金支付管理&#xff0c;在定金支付管理頁面可以填寫訂單編號、車型、品牌、分類、車身顏色、售價、訂金金額、付款日期、備注、用戶名、姓名、聯系方式、是否支付等信息&#xff0c;進行詳情、修改&#xff0c;如圖1所示。 圖1定金支付管理界面圖 預約到店管…

1.搭建篇——帝可得后臺管理系統

目錄 前言項目搭建一、搭建后端項目1.初始化項目Maven構建 2.MySQL相關導入sql配置信息 3. Redis相關啟動配置信息 4.項目運行 二、 搭建前端項目1.初始化項目2.安裝依賴3.項目運行 三、問題 前言 提示&#xff1a;本篇講解 帝可得后臺管理系統 項目搭建 項目搭建 一、搭建后…

單段時間最優S型速度規劃算法

一&#xff0c;背景 在做機械臂軌跡規劃的單段路徑的速度規劃時&#xff0c;除了參考《Trajectory Planning for Automatic Machines and Robots》等文獻之外&#xff0c;還在知乎找到了這位大佬 韓冰 寫的在線規劃方法&#xff1a; https://zhuanlan.zhihu.com/p/585253101/e…

單片機的學習(15)--LCD1602

LCD1602 14.1LCD1602的基礎知識1.LCD1602介紹2.引腳及應用電路3.內部結構框圖4.時序結構5.LCD1602指令集6.字符值7.LCD1602操作流程 14.2LCD1602功能函數代碼1.顯示一個字符&#xff08;1&#xff09;工程目錄&#xff08;2&#xff09;main.c函數&#xff08;3&#xff09;LCD…

oj E : 投資項目的方案

Description 有n種基礎的投資項目&#xff0c;每一種的單位收益率為profitn&#xff0c;存在m種投資組合&#xff0c;限制每一種的投資總額不能超過invest_summ 每種投資組合中項目所需的單位投入是不同的&#xff0c;為costmn 求&#xff1a;使得收益率之和最高的每種項目投…

基于機器學習的制冷系統過充電和欠充電故障診斷(采用紅外熱圖像數據,MATLAB)

到目前為止&#xff0c;制冷系統故障診斷方法已經產生很多種&#xff0c;概括起來主要有三大類&#xff1a;基于分析的方法&#xff0c;基于知識的方法和基于數據驅動的方法。基于分析的方法主要獲得制冷系統的數學模型&#xff0c;通過殘差來檢測和診斷故障。如果存在殘差且很…

[JS]BOM操作

介紹 BOM(Browser Object Model)是瀏覽器對象模型 window對象是一個全局對象, 也是JS中的頂級對象通過var定義在全局作用域中的變量和函數都會變成window對象的屬性和方法window對象下的屬性和方法調用時一般省略window 間歇函數 定時器 定時器是間歇函數的一種, 可以每個每…