9

我這個簡單的代碼行:爲什麼浮點變量通過以奇怪的方式在點之後切割數字來保存值?

float val = 123456.123456; 

當我打印此VAL或查找範圍,它存儲值123456.13

好吧,這很好,它不能存儲點畢竟這些數字只是在4個字節中,但爲什麼它會在13點之後做出13?難道不是12?

(使用在Win32 VC++ 2010所明示)

+0

感謝大家的答案。我現在明白了。我只是應該在點之後有限數字下降,而不是四捨五入。 – Kosmos 2012-02-17 13:30:05

回答

7

當以浮點形式表示時,您的數字的指數爲16(即,該值是它的mantisse時間2^16或65536)。所述mantisse然後變成

123456.123456/65536 = 1.8837909462890625 

爲了適合在一個32位浮點,所述mantisse被截斷爲23位,所以現在變得1.883791。當乘以65536時,它變成123456.125

注意小數點後第三個位置的5:您使用的C++輸出例程將其舍入,使您的最終數字看起來像123456.13

EDIT舍入的說明:(瑞克里根的評論)

舍入先發生在二進制(24位),十進制到二進制轉換,然後對十進制數,在printf。存儲的值是1.1110001001000000001 x 2^16 = 1.8837909698486328125 x 2^16 = 123456.125。它打印爲123456.13,但僅僅是因爲Visual C++使用「四捨五入」四捨五入。

裏克也有outstanding article on the subject

如果你想與其他數字和自己的浮動陳述玩,這裏是一個very useful IEEE-754 calculator

+0

能否請您解釋一下多一點。我可能無法理解尤其這一行「當表示爲浮動,您的號碼16的指數」 。爲什麼需要它? – 2012-02-17 13:29:17

+0

@RasmiRanjanNayak 32位浮點數表示爲23位mantisse,一個7位二進制指數和一個符號位。從邏輯上講,您將原始數字除以2,並將指數遞增,直到小數點前的剩餘數字爲「1」。對於'123456.123456',需要16個分區。 – dasblinkenlight 2012-02-17 13:35:06

+3

@dasblinkenlight最好這個描述是誤導。舍入首先以二進制(至24位)進行,以十進制轉換爲二進制,然後轉換爲小數,位於printf中。存儲的值是1.1110001001000000001 x 2^16 = 1.8837909698486328125 x 2^16 = 123456.125。它打印爲123456.13,但只是因爲Visual C++使用「半輪遠離零」四捨五入(見我的文章http://www.exploringbinary.com/inconsistent-rounding-of-printed-floating-point-numbers/。) – 2012-02-17 14:19:34

2

嘗試打印的std::numeric_limits<float>::digits10值。這大致上是說基數爲10的浮子有多少精度。你試圖超過它,所以你正在經歷精確度的損失(意味着超出重要數字的數字並不真正有意義)。

參見例如What is the meaning of numeric_limits<double>::digits10

2

它完全依賴於編譯器。在GCC中檢查它。它應該是xxx.12

+5

如果內部使用的格式是IEEE-754,則不應該依賴於編譯器。 – 2012-02-17 13:18:02

+0

檢查http://steve.hollasch.net/cgindex/coding/ieeefloat.html – 2012-02-17 13:19:34

+0

我覺得我們是給「編譯依賴」兩種不同的含義;我和其他人都理解的是,你所說的「從編譯器到編譯器是不同的」。你是說,而是它是一個*編譯器特定的錯誤*? – 2012-02-17 13:22:14

8

存儲在val中的值等於123456.125。你得到.13,因爲你是舍入:

float val = 123456.123456; 
printf("%.4f %.2f\n", val, val); 

輸出:123456.1250 123456.13

你應該在這種情況下使用雙以避免截斷。編譯器還應警告您:「警告C4305:'正在初始化':從'double'截斷爲'float'」

10

在二進制中,123456.123456是11110001001000000.000111111001 ...(無限)。它輪到11110001001000000.001或123456.125。 打印時打印到123456.13。

相關問題