这听起来像是一个基本问题,但我正在尝试2天才弄清楚。 我搜索了一个解决方案,我发现的唯一解释是float只使用4个字节的内存。 (这很有帮助......) 我有以下循环:
double x = 0.0;
for(int i = 0; i< 100; i++)
{
x = x + 0.01;
NSLog(@"%f",x);
}
并打印:
0.010000
0.020000
0.030000
.
.
1.000000
但当我将double更改为float时:
0.010000
0.020000
0.030000
.
.
0.820000
0.830000
0.839999
0.849999
.
.
0.999999
如您所见,计算机无法计算0.840000作为浮点数__- 问题是我必须使用float,因为我使用的UIProgressView可以使用0.0到1.0之间的浮点数。
如果我不能使用双倍,我该怎么办? 感谢。
答案 0 :(得分:3)
使用x = (i + 1) * 0.01;
代替x = x + 0.01;
。它仍然不准确,因为float
根本无法准确表示0.84
,但至少错误不会累积。
答案 1 :(得分:3)
如果您编写以下代码该怎么办:
int x = 0;
for(int i = 0; i< 100; i++)
{
x = x + 3.5;
NSLog(@"%d",x);
}
如果不打印3.5
,7.0
,10.5
,你不会感到惊讶,对吗?你会说“int是不准确的”吗?当然不是。
在你的例子中正好发生了同样的事情。就像3.5
不能表示为整数一样,0.01
不能表示为double。你得到的实际价值是:
0.01000000000000000020816681711721685132943093776702880859375
现在,您不仅累积了从舍入0.1
到double的初始表示错误,而且还得到舍入错误,因为并非所有中间总和是可以代表的。 int
示例中未出现第二个错误源。计算的实际中间总和是:
0.01000000000000000020816681711721685132943093776702880859375
0.0200000000000000004163336342344337026588618755340576171875
0.0299999999999999988897769753748434595763683319091796875
0.040000000000000000832667268468867405317723751068115234375
0.05000000000000000277555756156289135105907917022705078125
0.060000000000000004718447854656915296800434589385986328125
...
0.990000000000000657252030578092671930789947509765625
1.0000000000000006661338147750939242541790008544921875
当你通过%f
格式说明符将它们舍入到六位小数时,你得到了第三个舍入来源,但错误都足够小,以至于打印出你“预期”的结果。
现在让我们看看当您使用float
代替double
时会发生什么;由于C算术操作数提升规则,添加都是在double
中执行的,但结果在每次添加后舍入到float
- 还有另一个舍入。中间值的顺序如下:
0.00999999977648258209228515625
0.0199999995529651641845703125
0.02999999932944774627685546875
0.039999999105930328369140625
0.0500000007450580596923828125
0.060000002384185791015625
...
0.809999525547027587890625
0.819999516010284423828125
0.829999506473541259765625
到目前为止,错误足够小,以至于当舍入到六位小数时它们仍然会产生“预期”值。但是,计算的下一个值是
0.839999496936798095703125
因为这只是更小而不是精确到达六位小数的中间情况:
0.8399995
它向下舍入,打印的数字是:
0.8399999
现在,你能做些什么呢?当使用六位十进制数字打印时,错误最终变得足够大的原因是每次顺序添加时累积错误。如果可以避免这种累积,错误将保持足够小,不会造成这种麻烦。有几种简单的方法可以避免这种累积误差;也许最简单的是:
for (int i=0; i<100; i++) {
float x = (i+1)/100.f;
NSLog(@"%d",x);
}
这是有效的,因为i + 1
和100.f
都在float
中得到了完整的体现。因此,只有一个舍入,发生在除法中,因此float
结果尽可能接近您想要的数字;你无法靠近。