C# float浮點數與二進位制相互轉換(IEEE754)

冰封百度發表於2022-05-04

Float32轉二進位制

C#中浮點數的二進位制格式遵循IEEE754標準(IEEE二進位制浮點數算術標準)。

以小數11.25為例,float32的二進位制值為:
01000001001101000000000000000000

這個值是怎麼來的呢?
IEEE754是一個關於浮點數的標準,它把浮點數分成3個部分:
Sign(符號)|Exponent(指數)|Mantissa(尾數)

Sign(符號) 表示浮點數的正負(大於等於0為0,小於0為1)
Exponent(指數) 表示浮點數的指數(類似科學計數法的指數部分)
Mantissa(尾數) 表示有效數字(類似科學計數法的有效數字)
以下內容簡稱這3部分分別為S、E、M

Float32佔32位,8位一位元組共4位元組:

S(1位)|E(8位 偏移127)|M(23位) 即:
0 | 1000 0010 | 01101000000000000000000

S、E、M 這3部分是怎麼確定的呢?

比如11.25 表示成十進位制的科學計數法: 1.125x101
符號是+,指數是1,有效數字是1.125

IEEE754是先把小數轉成二進位制,用二進位制的科學計數法表示該小數。
還是以11.25為例:
S為0 (這個數字大於等於0 符號S=0,如果小於0,符號S=1)
把浮點數的整數部分和小數部分分別轉成二進位制,再拼到一起。

整數部分轉二進位制:

轉換方法:整數除以2取餘 倒序排列(除到整數部分為0)
整數部分I為11,轉成二進位制為:1011

$$ 2\sqrt{11}=5···1 \qquad\enspace \\=2···1 \\=1···0 \\=0···1 $$

小數部分轉二進位制:

轉換方法:小數乘以2取整 正序排列(乘到小數部分為0,即餘數為0)
小數部分F為0.25,轉成二進位制為:01

$$ 0.25\times2=0···0.5 \qquad\enspace\enspace \\=1···0 $$

整數和小數拼到一起:

IF = 1011.01

以二進位制科學計數法表達:

IF = 1.01101x23

指數部分E:

E = 3, IEEE754規定這個值要加127
E = 130 轉成二進位制為:1000 0010
整數除以2取餘 倒序排列(除到整數部分為0)

$$ 2\sqrt{130}=65···0 \qquad\enspace\enspace \\=32···1 \\=16···0 \\=\enspace 8···0 \\=\enspace 4···0 \\=\enspace 2···0 \\=\enspace 1···0 \\=\enspace 0···1 $$

尾數部分M:

M = 101101
由於二進位制的科學計數法首位一定為1, 1可以省略不寫,尾數部分就變成了01101
M = 01101

S、E、M三部分拼到一起:

0 | 1000 0010 | 01101 (後面補0夠23位)
0 | 1000 0010 | 0110 1000 0000 0000 0000 000

11.25轉成二進位制最終結果就是 0 1000 0010 0110 1000 0000 0000 0000 000
《線上浮點數轉換工具》驗證一下,結果正確。

二進位制轉Float32

二進位制11000000110110000000000000000000轉Float32怎麼轉呢?
還是先把二進位制分為S、E、M三部分,分別轉換後計算最終結果。

1 | 1000 0001 | 1011 0···
S = 1 符號為- 是負數
E = 1000 0001
M = 1011 0···

指數E轉整數:

E = 1000 0001 轉整數
轉換方法為:從低位到高位 按位轉10進位制相加
1x27 + 0x26 + 0x25 + 0x24 + 0x23 + 0x22 + 0x21 + 1x20 =
128 + 0 + 0 + 0 + 0 + 0 + 0 + 1 = 129

減去127(轉二進位制之前加了127 還原時需要減回來)
E = 129 - 127 = 2

尾數M轉小數:

M = 1011
首位補1(為了節省空間 轉換時去掉了首位的1 還原時需要補回來)
M = 11011
有效數字 科學計數法
M = 1.1011 x 22
M = 110.11
整數部分 I = 110 小數部分 F = .11

整數部分I轉10進位制:

I = 110 轉成10進製為:6
1x22 + 1x21 + 0x20 =
4 + 2 + 0 = 6
I = 6

小數部分F轉10進位制:
1x(1/21) + 1x(1/22) =
1x0.5 + 1x0.25 =
0.5 + 0.25 = 0.75
F = 0.75

整數和小數部分拼接到一起:
6 + 0.75 = 6.75

加上符號位:
S = 1 (負數 符號為-)
float32 = -6.75

二進位制11000000110110000000000000000000 轉成十進位制最終結果就是 -6.75
《線上浮點數轉換工具》驗證一下,結果正確。

Float64轉二進位制

IEEE754標準中規定,Float64佔64位共8位元組 S、E、M三部分分別為:

S(1位)|E(11位 偏移1023)|M(52位)

以9.625為例:

S = 0 (正數)

整數部分轉二進位制:

轉換方法:整數除以2取餘 倒序排列(除到整數部分為0)
整數部分 I = 9 轉二進位制為:1001

$$ 2\sqrt{9}=4···1 \qquad \\=2···0 \\=1···0 \\=0···1 $$

小數部分轉二進位制:

轉換方法:小數乘以2取整 正序排列(乘到小數部分為0,即餘數為0)
小數部分F為0.625,轉成二進位制為:101

$$ 0.625\times2=1···0.25 \qquad\enspace\enspace\enspace\enspace \\=0···0.5 \\=1···0\enspace $$

整數和小數拼到一起:

IF = 1001.101

以二進位制科學計數法表達:

IF = 1.001101 x 23

指數部分E:

E = 3, 加上偏移值1023
E = 1026 轉成二進位制為:1000 0000 010
轉換方法:整數除以2取餘 倒序排列(除到整數部分為0)

$$ 2\sqrt{1026}=513···0 \qquad\enspace\enspace\enspace \\=256···1 \\=128···0 \\=\enspace 64···0 \\=\enspace 32···0 \\=\enspace 16···0 \\=\enspace\enspace 8···0 \\=\enspace\enspace 4···0 \\=\enspace\enspace 2···0 \\=\enspace\enspace 1···0 \\=\enspace\enspace 0···1 $$

尾數部分M:

M = 1001101
由於二進位制的科學計數法首位一定為1, 1可以省略不寫,尾數部分就變成了001101
M = 001101

S、E、M三部分拼到一起:

0 | 1000 0000 010 | 001101 (後面補0夠52位)

9.625轉成二進位制最終結果就是 0 1000 0000 010 001101 0···
《線上浮點數轉換工具》驗證一下,結果正確。

二進位制轉Float64

二進位制110000000101100100010···轉Float64怎麼轉呢?
還是先把二進位制分為S、E、M三部分,分別轉換後計算最終結果。

1 | 1000 0000 101 | 1001 0001 0···
S = 1 符號為- 是負數
E = 1000 0000 101
M = 1001 0001 0···

指數E轉整數:

E = 1000 0000 101 轉整數
轉換方法為:從低位到高位 按位轉10進位制相加
1x210 + 0x29 + 0x28 + 0x27 +
0x26 + 0x25 + 0x24 + 0x23
1x22 + 0x21 + 1x20 =
1024 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 4 + 0 + 1 = 1029

減去1023(轉二進位制之前加了023 還原時需要減回來)
E = 1029 - 1023 = 6

尾數M轉小數:

M = 1001 0001
首位補1(為了節省空間 轉換時去掉了首位的1 還原時需要補回來)
M = 1100 1000 1
有效數字 科學計數法
M = 1.10010001 x 26
M = 1100100.01
整數部分 I = 1100100 小數部分 F = .01

整數部分I轉10進位制:

I = 1100100 轉成10進製為:100
1x26+ 1x25 + 0x24 + 0x23 + 1x22 + 0x21 + 0x20 =
64 + 32 + 0 + 0 + 4 + 0 + 0 = 100
I = 100

小數部分F轉10進位制:
0x(1/21) + 1x(1/22) =
0x0.5 + 1x0.25 =
0 + 0.25 = 0.25
F = 0.25

整數和小數部分拼接到一起:
100 + 0.25 = 100.75

加上符號位:
S = 1 (負數 符號為-)
float64 = -100.25

二進位制110000000101100100010··· 轉成十進位制最終結果就是 -100.25
《線上浮點數轉換工具》驗證一下,結果正確。

總結:

大多數程式語言中,關於浮點數的表示都遵循IEEE754標準,都是由S、E、M三個部分組成,類似科學技術法的表示規則,為了儘量節約空間做了某些特殊規定。由於轉換時,某些資料比如0.1 小數部分乘2以後不能完全乘盡(0.2, 0.4, 0.8, 0.6 …… 無限迴圈) 所以小數會出現丟失精度的情況,不能完全準確的表達這些乘不盡的情況,所以遇到類似精度丟失,資料變大了一點點,變小了一點點的問題,知道是這個原理導致的問題即可,提高精度或四捨五入一下即可解決。

相關文章