我正在玩普通的老C.我只是计算一些便士,然后在一定次数的迭代中以指数方式增加数量。在这个过程的最后,我想要在屏幕上以美元和美分显示大量的美分/便士。
我的输出电流如下:
"You have 805306365 pennies"
我试图通过将新变量定义为float来将此数字转换为美元:
float totalD = total / 100.00;
输出显示8053063.50
。为什么这个int的划分造成了15美分的损失?
我试图通过“printf”显示它,如果这有帮助:
printf("You have %.2f dollars", totalD);
我知道我可以将我的美分转换为字符串并尝试格式化它,但我很喜欢,但我很困惑为什么浮点数会这样做。任何人都可以告诉我为什么会发生这种情况以及如何处理它?</ p>
答案 0 :(得分:5)
更新了准确性和完整性
您遇到的是浮点数中 precision 的(相当常见)问题。在许多编译器中,浮点数只有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
递增一位,float
代表64
代表printf("%.0f %08x", fpennies, *(unsigned int*)(&fpennies));
!最好通过稍微查看浮点数的二进制表示来解释这一点。
首先,这是32位浮点数的组织(来自http://upload.wikimedia.org/wikipedia/commons/d/d2/Float_example.svg):
我们可以使用一些显式的转换来获取数字的十六进制表示:
805306368 4e400000
805306432 4e400001
对于我们之前看到的跨越跳跃的两个值,结果为
1
正如您所看到的,有效数字的“最低有效位”增加了0x4e40 = 0100 1110 0100 0000 in binary
,但是指数意味着乘数为64.为什么64?好吧,让我们扩大前几位:
1001 1100 = 0x9c = 156
由于最高位是符号位(0 =正),接下来的8位是指数,这就是指数
value = (-1)^(sign bit) * (1 + sum(i=1 to 23, bit(23-i)*2^(-i))) * 2^(exponent - 127)
现在,从浮点中的位到其值(参见http://en.wikipedia.org/wiki/Single-precision_floating-point_format)的规则是
1
在这种情况下,最低有效位(位0)中2^(-23) * 2^( 156 - 127 ) = 2^6 = 64
的更改会增加64
因此,对于这个数量的数字,可以表示的最小步长为long int dollars, cents, pennies;
...
dollars = pennies / 100;
cents = pennies % 100;
,如输出中所示。
如果你想解决这个问题,你可以做一些在Vaughn的答案中建议的事情 - 使用代表便士的长整数,并使用整数数学(除法,模数)来获得“全部美元,全部美分”金额。
float pennies = 805306365;
printf("you have %f pennies\n", pennies);
通过这种方式,您可以在不损失精确度的情况下代表一些相当大的金钱。
在练习中,当你写作
You have 805306368 pennies
你得到了
double
如果你使用{{1}}类型,你会有更好的运气(在这种情况下)。
答案 1 :(得分:2)
我认为你会以这种方式做得更好:
printf("You have %d.%02d dollars", total/100,total%100);
答案 2 :(得分:-1)
考虑使用long int或long long来表示便士。浮动或双打不适合你想要做的事情。我建议你阅读discussion on StackOverflow。