浮动结果是不准确的 [英] float result is inaccurate

查看:113
本文介绍了浮动结果是不准确的的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述


可能重复:

在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

但是当我把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之间的浮点数。



如果可以的话不能用双,我能做什么?
Thanks。

解决方案

如果您编写了以下代码:

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



$ b $ p
$ b

你不会感到惊讶,它不会输出 3.5 7.0 10.5 ,对吧?你会说int不准确吗?当然不是。



完全一样的事情发生在你的例子中。就像 3.5 不能表示整数一样, 0.01 不能表示为double。您得到的实际价值是:

pre $ code> 0.01000000000000000020816681711721685132943093776702880859375

现在,您不仅累积了 0.1 的初始表示形式错误,而且还会累加得到舍入错误,因为不是所有的中间和是可以表示的。第二个错误来源在 int 示例中不会发生。所计算出的实际的中间和值是:

  0.01000000000000000020816681711721685132943093776702880859375 
0.0200000000000000004163336342344337026588618755340576171875
0.0299999999999999988897769753748434595763683319091796875
0.040000000000000000832667268468867405317723751068115234375
0.05000000000000000277555756156289135105907917022705078125
0.060000000000000004718447854656915296800434589385986328125
...
0.990000000000000657252030578092671930789947509765625
1.0000000000000006661338147750939242541790008544921875

当您通过%f 格式说明符将这些数字舍入到小数点后六位时,您还有第三个四舍五入的来源,但是这些错误都足够小现在让我们来看看在使用 float 时会发生什么,而不是使用 ;由于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 






现在,您可以做些什么呢?误差最终变得足够大,以便在用六位十进制数字打印时显示的原因是,每次连续添加都会导致错误累积错误。如果你可以避免这种积累,错误将保持足够小,不会造成这种麻烦。有几个简单的方法可以避免这种累积误差;也许最简单的是:
$ b $ pre $ for(int i = 0; i <100; i ++){
float x =第(i + 1)/100.f;
NSLog(@%d,x);



$ b $ p $这是因为 i + 1 100.f 恰好在 float 中表示。因此只有一个四舍五入的结果出现在分区中,所以 float 结果尽可能接近你想要的数字;没有办法可以走得更近。


Possible Duplicate:
Trouble with floats in Objective-C

It might sound to you like a basic issue, but I'm trying for 2 days to figure it out. I searched for a solution and the only explanation I found is that float uses only 4-bytes of memory. (that's helpful...) I have the following loop:

double x = 0.0;
        for(int i = 0; i< 100; i++)
        {
            x = x + 0.01;
            NSLog(@"%f",x);
        }

and it prints:

0.010000
0.020000
0.030000
.
.
1.000000

but when I change the double to float:

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

As you can see, the computer can't calculate 0.840000 as float -_- The problem is that I have to use float because I'm using the UIProgressView that can take a float number between 0.0 to 1.0.

If I can't use double, what can I do? Thanks.

解决方案

What if you wrote the following code:

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

you wouldn't be surprised that it doesn't print out 3.5, 7.0, 10.5, right? Would you say that "int is inaccurate"? Of course not.

Exactly the same thing is happening in your example. Just like 3.5 isn't representable as an integer, 0.01 isn't representable as a double. The actual value you get is:

0.01000000000000000020816681711721685132943093776702880859375

Now, you accumulate not only the initial representation error from rounding 0.1 to double, but you also get rounding errors because not all of the intermediate sums are representable. This second source of error does not occur in the int example. The actual intermediate sums that are computed are:

0.01000000000000000020816681711721685132943093776702880859375
0.0200000000000000004163336342344337026588618755340576171875
0.0299999999999999988897769753748434595763683319091796875
0.040000000000000000832667268468867405317723751068115234375
0.05000000000000000277555756156289135105907917022705078125
0.060000000000000004718447854656915296800434589385986328125
...
0.990000000000000657252030578092671930789947509765625
1.0000000000000006661338147750939242541790008544921875

when you round these to six decimal places via the %f format specifier, you get yet a third source of rounding, but the errors are all small enough that the results you "expect" are printed out.

Now let's look at what happens when you use float instead of double; because of the C arithmetic operand promotion rules, the additions are all carried out in double, but the result is rounded back to float after each addition -- yet another rounding. The sequence of intermediate values is as follows:

0.00999999977648258209228515625
0.0199999995529651641845703125
0.02999999932944774627685546875
0.039999999105930328369140625
0.0500000007450580596923828125
0.060000002384185791015625
...
0.809999525547027587890625
0.819999516010284423828125
0.829999506473541259765625

Up to this point, the errors are small enough that they all still produce the "expected" value when rounded to six decimal digits. However, the next value computed is

0.839999496936798095703125

Because this is just smaller than the exact halfway case for rounding to six decimal digits:

0.8399995

it rounds down, and the number that is printed is:

0.8399999


Now, what can you do about it? The reason that the errors eventually become large enough to appear when printed with six decimal digits is that you are accumulating error with each sequential addition. If you can avoid this accumulation, the error will remain small enough to not cause this trouble. There are a several easy ways to avoid this cumulative error; perhaps the easiest is:

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.

这篇关于浮动结果是不准确的的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆