0

可能重复:
Objective-C 中的浮点数问题

这听起来像是一个基本问题,但我正在尝试 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

但是当我将双精度更改为浮动时:

0.010000
0.020000
0.030000
.
.
0.820000
0.830000
0.839999
0.849999
.
.
0.999999

如您所见,计算机无法将 0.840000 计算为浮点数 -_- 问题是我必须使用浮点数,因为我使用的 UIProgressView 可以采用 0.0 到 1.0 之间的浮点数。

如果我不能使用双,我该怎么办?谢谢。

4

2 回答 2

3

使用x = (i + 1) * 0.01;而不是x = x + 0.01;. 它仍然不准确,因为float根本无法准确表示0.84,但至少错误不会累积。

于 2013-01-30T16:03:01.013 回答
3

如果您编写以下代码会怎样:

int x = 0;
for(int i = 0; i< 100; i++)
{
    x = x + 3.5;
    NSLog(@"%d",x);
}

你不会对它没有打印出来感到惊讶,,,,对3.5吧?你会说“int 不准确”吗?当然不是。7.010.5

在您的示例中发生了完全相同的事情。就像3.5不可表示为整数,0.01不可表示为双精度。你得到的实际价值是:

0.01000000000000000020816681711721685132943093776702880859375

现在,您不仅会累积从舍入到加倍的初始表示误差0.1,而且还会得到舍入误差,因为并非所有中间和都是可表示的。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);
}

This works because both i + 1 and 100.f are exactly represented in float. There is thus only a single rounding, which occurs in the division, so the float result is as close to your desired number as possible; there is no way you can come any closer.

于 2013-01-30T16:26:04.680 回答