2013-04-08 72 views
0

我玩弄普通的舊C.我只是計算了一些便士,然後通過一定的迭代次數呈指數級增長量。在這個過程結束時,我有大量的美分/便士,我想在屏幕上顯示爲美元和美分。爲什麼float會這樣做?

我的輸出電流是這樣的:

"You have 805306365 pennies" 

我試圖通過定義一個新的變量作爲浮點這個數字轉換成美元:

float totalD = total/100.00; 

輸出顯示8053063.50。爲什麼這個整數的分解導致15美分的損失? 我想通過一個「printf」上顯示它是否有幫助:

printf("You have %.2f dollars", totalD); 

我知道我可以將我美分成字符串和嘗試,但是我喜歡格式化,但我困惑,爲什麼浮動會做到這一點。任何人都可以告訴我爲什麼發生這種情況,也許該如何處理它?

+8

如果您在使用'float',它不會有足夠的精度保持'805306365'。並且不要使用浮點類型來賺錢。 – Mysticial 2013-04-08 02:44:33

+2

[請仔細閱讀本文](http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html)。這將需要一段時間,但它會永遠改變你對漂浮指針數字的任何想法。 – WhozCraig 2013-04-08 02:59:00

+0

當整數足夠時,不要對浮點數進行數學運算。 – 2013-04-08 03:00:14

回答

5

更新的準確性和完整性

您遇到是精度在浮點數(相當常見的)問題。上許多編譯器,浮點數只有32位長,並且它們使用一定數量的那些位(23)的對他們的「有效位數」(有時稱爲「尾數」),1個符號位,並且8位用於「指數」(在很多象1.23E45,在‘1.23’是有效數字,而‘45’是指數在二,你實際上有相對較少的(24)那些和可用零,所以你在奔波了精度十進制表示法中的數字6或7)。

爲了說明的精度這方面的損失,我寫的幾行代碼:

#include <stdio.h> 

int main(){ 
    float fpennies; 
    long lpennies, ii; 

    lpennies = 805306360; 
    for(ii = 0; ii< 100; ii++) { 
     fpennies = lpennies + ii; 
     printf("%ld pennies converted to float: %.0f fpennies\n",ii+ lpennies, fpennies); 
    } 
} 

這種生產類型

805306360 pennies converted to float: 805306368 fpennies 
805306361 pennies converted to float: 805306368 fpennies 
805306362 pennies converted to float: 805306368 fpennies 
... 
805306400 pennies converted to float: 805306368 fpennies 
805306401 pennies converted to float: 805306432 fpennies 

正如你所看到的許多行,對周圍805306400,遞增該long只需一按64遞增數量的float表示!最好通過稍微靠近浮點數的二進制表示來解釋。

首先,這裏是一個32位浮點數字的組織(從http://upload.wikimedia.org/wikipedia/commons/d/d2/Float_example.svg):

enter image description here

我們可以有明確的鑄造得到一個數字的十六進制表示:

printf("%.0f %08x", fpennies, *(unsigned int*)(&fpennies)); 

對於前面我們看到的跨越跳躍的兩個值,這導致

805306368 4e400000 
805306432 4e400001 

正如你所看到的,有效數的「最低顯著位」增加了1,但指數意味着64.爲什麼64事半功倍?好吧,讓我們展開前幾名位:

0x4e40 = 0100 1110 0100 0000 in binary 

由於最高位爲符號位(0 =正),接下來的八位是指數,這使得指數

1001 1100 = 0x9c = 156 

現在用於從浮點到其值的位得到規則(見http://en.wikipedia.org/wiki/Single-precision_floating-point_format)是

value = (-1)^(sign bit) * (1 + sum(i=1 to 23, bit(23-i)*2^(-i))) * 2^(exponent - 127) 

在這種情況下,1在至少顯著位的變化(0位)增加2^(-23) * 2^(156 - 127) = 2^6 = 64

因此,對於這個量級的數字,可以表示的最小步數是64,正如您在輸出中看到的那樣。

如果你想解決這個問題,你可以在Vaughn的答案中提出建議 - 使用代表便士的長整數,並使用整數數學(除法,模)來獲得「全部美元,全部美分」數量。

long int dollars, cents, pennies; 
... 
dollars = pennies/100; 
cents = pennies % 100; 

以這種方式,您可以代表一些非常大的金錢而不會損失精度。

在實踐中,當你寫

float pennies = 805306365; 
printf("you have %f pennies\n", pennies); 

你得到

You have 805306368 pennies 

如果使用double類型,你將有更好的運氣(在這種情況下)。

+2

我確定這是一個錯字。通常,一個「浮點」使用8位的數值和24位(24-1物理位)作爲有效位。 – 2013-04-08 03:08:55

+0

@DanielFischer - 那絕對是我的錯字。感謝您的支持。修復... – Floris 2013-04-08 03:10:03

+2

我想你會發現有1位用於符號,8位用於指數,23位顯式位和1位隱式位用於尾數。請參閱[IEEE 754]上的維基百科(http://en.wikipedia.org/wiki/IEEE_floating_point)。 – 2013-04-08 04:00:23

-1

考慮使用long int或long long來表示便士。花車或雙打不適合你想要做的事情。我建議閱讀這個discussion on StackOverflow

2

我想你有更好的運氣做這種方式:

printf("You have %d.%02d dollars", total/100,total%100); 
+0

+1用於處理問題的後半部分 - 「也許該如何處理它」。以便士的方式明確工作並避免轉換爲「浮動」是維持這種(絕對)精度的方法。 – Floris 2013-04-08 10:50:22

+0

非常有幫助。這是我第一次學習C的幾天,所以我不知道我可以用這種方式格式化我的輸出。再次感謝! – 2013-04-10 15:54:37