Strange contour issue

I'm not sure if this is a mistake or not, so I thought that you might want to take a look.

The problem is this code:

for i=0,1,.05 do print(i) end 

The output should be:

 0 .05 .1 --snip-- .95 1 

Instead, the output is:

 0 .05 .1 --snip-- .95 

The same problem occurred with the while loop:

 w = 0 while w <= 1 do print(w) w = w + .05 end --output: 0 .05 .1 --snip-- .95 

The value of w is 1, which can be checked by the print statement after the loop.

I have verified as much as possible that any step that is less than or equal to .05 will lead to this error. Any step above 0.05 should be perfect. I checked that 1/19 (0.052631579) actually prints 1. (Obviously, a decimal denominator such as 19.9 or 10.5 will not output the output from [0,1] inclusive.) Is there a chance that this is not language mistake? Both the interpreter and the regular Lua file generate this error.

+7
source share
4 answers

This is a rounding issue. The problem is that 0.05 is represented as a binary floating-point number and does not have an exact representation in binary format. In base 2 (binary), this is a repeating decimal number, like digits, like 1/3 in base 10. When added repeatedly, rounding results in a number that is slightly larger than 1. This is very, very slightly larger than 1, so if you print it, 1 will be displayed as output, but it's not exactly 1.

 > x=0.05+0.05+0.05+0.05+0.05+0.05+0.05+0.05+0.05+0.05+0.05+0.05+0.05+0.05+0.05+0.05+0.05+0.05+0.05+0.05 > print(x) 1 > print(1==x) false > print(x-1) 2.2204460492503e-16 

So, as you can see, although really close to 1, this is actually a bit more.

A similar situation can occur in decimal when we repeat fractions. If we collected 1/3 + 1/3 + 1/3, but we had to round to six digits for work, we would add 0.333333 + 0.333333 + 0.333333 and get 0.999999, which is actually not 1. This is a similar case for binary mathematics. 1/20 cannot be represented exactly in binary format.

Note that rounding is slightly different for multiplication, so

 > print(0.05*20-1) 0 > print(0.05*20==1) true 

As a result, you can rewrite your code to say

 for i=0,20,1 do print(i*0.05) end 

And it will work correctly. In general, it is recommended that you avoid using floating point numbers (i.e. decimal point numbers) to control loops when this can be avoided.

+8
source

This is the result of floating point inaccuracy. The binary64 floating-point number cannot store 0.05, so the result will be rounded to a number that is slightly larger than 0.05. This rounding error remains in the repeated sum, and ultimately the final value will be a little more than 1.0, and therefore will not be displayed.

+3
source

This is a floating point thing. Computers do not exactly represent floating point numbers. Tiny rounding errors make 20 +0.05 additions not exactly 1.0. Check out this article: “ What Every Programmer Should Know About Floating-Point Arithmetic.

To get the desired behavior, you can loop I on 1..20 and set f = i * 0.05

+2
source

This is not a bug in Lua. The same thing happens in program C below. As others have explained, this is due to floating point inaccuracies, more precisely, the fact that 0.05 is not a binary fraction (i.e., it does not have a finite binary representation).

 #include <stdio.h> int main(void) { double i; for (i=0; i<=1; i+=0.05) printf("%g\n",i); return 0; } 
+1
source

All Articles