0

我正在玩普通的旧 C。我只是在计算一些便士,然后在一定次数的迭代中以指数方式增加数量。在该过程结束时,我有大量的美分/便士,我想在屏幕上显示为美元和美分。

我的输出电流如下所示:

"You have 805306365 pennies"

我尝试通过将新变量定义为浮点数来将此数字转换为美元:

float totalD = total / 100.00;

输出显示8053063.50。为什么这个 int 的除法会造成 15 美分的损失?如果有帮助,我正在尝试通过“printf”显示它:

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

我知道我可以将我的美分转换为字符串并尝试按照我喜欢的方式对其进行格式化,但我对为什么浮点数会这样做感到困惑。谁能告诉我为什么会发生这种情况以及如何处理它?

4

3 回答 3

5

更新的准确性和完整性

您遇到的是浮点数精度的(相当常见的)问题。在许多编译器上,浮点数只有 32 位长,它们使用一定数量的这些位 (23) 作为它们的“有效位”(有时称为“尾数”)、1 个符号位和 8 位作为“指数”(在像 1.23E45 这样的数字中,“1.23”是有效数,“45”是指数。在二进制中,您实际上只有相对较少 (24) 个可用的 1 和 0,因此您在大约数字 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,将 增加long1float会使数字的表示增加64!最好通过仔细查看浮点数的二进制表示来解释这一点。

首先,这里是一个 32 位浮点数的组织(来自http://upload.wikimedia.org/wikipedia/commons/d/d2/Float_example.svg):

在此处输入图像描述

我们可以通过一些显式转换获得数字的十六进制表示:

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类型,你会有更好的运气(在这种情况下)。

于 2013-04-08T02:49:29.053 回答
2

我认为这样做会更好:

printf("You have %d.%02d dollars", total/100,total%100);
于 2013-04-08T02:58:41.373 回答
-1

考虑使用 long int 或 long long 来表示便士。浮动或双打不适合您尝试做的事情。我建议阅读有关 StackOverflow 的讨论

于 2013-04-08T02:52:43.093 回答