浮动结果不准确

可能重复:
在Objective-C中浮动的问题

这可能听起来像一个基本的问题,但我想了2天解决。 我search了一个解决scheme,我发现唯一的解释是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之间的浮点数。

如果我不能用double,我能做什么? 谢谢。

如果你写了下面的代码呢?

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

你不会感到惊讶,它不打印出10.5 ,对不对? 你会说“int不准确”吗? 当然不是。

在你的例子中发生了完全相同的事情。 就像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 ,但是每次加法后结果都回到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); } 

这是100.f因为i + 1100.f都是以floatforms表示的。 因此在分区中只有一个四舍五入的结果,所以float结果尽可能接近你想要的数字; 你无法靠近。

使用x = (i + 1) * 0.01; 而不是x = x + 0.01;float不能完全代表0.84 ,但至less不会累积错误。