This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
Masterx's profile picture

For-Do loop bad math?

Started by Masterx, 16 March 2013 - 04:36 AM
Masterx #1
Posted 16 March 2013 - 05:36 AM
Title: For-Do loop bad math?
I'm writing a script that has a user variable and multiple nested for loops that have a step of "1/<var>", and while I was bug testing I noticed that I was getting unexpected results from my for loop. Because it is simple math to determine how many times the for loop executes the code, I decided to pre-calculate it as it would use less memory, and less time (1000s of calculations add up) than a nested counter variable. However, my results were off, At first I thought I did my math wrong somewhere, but I hadn't. So I distilled the code down to just the loop and executed it line by line in a terminal. Here are the results:


For those curious, the full code input here is:

Q = peripheral.wrap("right")
Q.clear()
Q.setCursorBlink(true)
Q.setCursorPos(25,1)
for a=0,1,1/4 do Q.write(a) x, y = Q.getCursorPos() Q.setCursorPos(25,y+1) end
Q.setCursorPos(x, y+2)   --this was a screw up
Q.setCursorPos(25, y+2)
for a=0,1,1/20 do Q.write(a) x, y = Q.getCursorPos() Q.setCursorPos(25,y+1) end

If you can't see the problem, count the number of times the code is executed. It is 5 for step size 1/4 and 20 for 1/20.
I would like to keep my code as for loops for convenience, but this problem is almost making me switch to repeat-until loops.
Can somebody please explain what is happening here?
Lyqyd #2
Posted 16 March 2013 - 05:43 AM
Split into new topic.

Why are you using fractional steps in your for loop? Use the loop with integers, divide them (if necessary!) in the loop for use.

`for i = 1, 4 do` is much easier to deal with.
GopherAtl #3
Posted 16 March 2013 - 06:31 AM
pretty sure this is floating point imprecision yer running into. 1/4, or 1/anything that's a power of 2, will work fine. 20 is not a power of 2, and 1/20 can't actually be expressed precicely as a floating point, only approximated. Details in spoiler
Spoilercomputers don't think in decimal, they think in binary. For whole numbers, this is fine, any whole number can be expressed in binary, decimal, hex, base 13, or whatever. For fractional values, not so much.
Decimal is base 10, which is 2 * 5, so anything that's multiples of 2 and 5 works exactly. 1/2 is .5, 1/5 is .2, 1/20 is .05, etc. But try to do 1/3, and you know what happens - it goes on forever, because 3 goesn't go into the base of decimal numbers, 10, evenly.
Binary uses base 2. So 1/2 still works fine - in binary, it's 0.1 - but 1/5, not so much. Everything in base 2 is multiples and divisions of 2, so 1/2, 1/4, 1/8, etc. if you try to come up with 1/20, it goes on forever, just like 1/3 in decimal. Computers can't represent this infinity properly, so they're forced to round it off, and this rounding off leads to errors like the one you just found.

If you do as lyqyd suggests, it will work. Generally, instead of

for i=0,1,1/fraction do
...
end

do instead

for i=0,fraction do
  i=i / fraction
...
end

you could use another variable than i inside the loop, but the i will be corrected each time the loop repeats, so might as well reuse it.
Masterx #4
Posted 16 March 2013 - 08:00 AM
Split into new topic.

Why are you using fractional steps in your for loop? Use the loop with integers, divide them (if necessary!) in the loop for use.

`for i = 1, 4 do` is much easier to deal with.

I wanted to do fractional, because as I said in first post, I'm using multiple nested for loops inside one another (6 of them o.O) and I wanted to save from doing more operations than needed, and because I'm running the output number into an unknown equation (loadstring anyone?) I would have to divide the number multiple times (and over 30000+ iterations that adds up quickly) and would have much rather had lua compile the variable at run time once and keep adding it, which is already happening due to the nature of the for loop, thus saving more time as this program will potentially be running for hours and even cutting one operation will save exponential amounts of time.

pretty sure this is floating point imprecision yer running into. 1/4, or 1/anything that's a power of 2, will work fine. 20 is not a power of 2, and 1/20 can't actually be expressed precicely as a floating point, only approximated. Details in spoiler
-snip-


Funny thing is with this, when I was trying to figure out where the error in my calculations was coming from, 10, 15, and 19 worked perfectly fine when I was running it, I still got out the outputs as I would expect. These numbers are not powers of 2, so you may be a little off there.


Thanks for the help, I'm going to see if switching to a repeat loop will solve this issue and if not, I'm going to do as suggested, and multiply the step into the finish point and divide in loop. For whatever reason, that solution didn't pop into my head as one of the possibilities of how to fix this loop issue.
Lyqyd #5
Posted 16 March 2013 - 12:14 PM
Given your description of the program, it sounds like you are probably doing things the hard way. If you post the whole code, we may be able to suggest a more elegant solution. Even if not, it would be interesting to see what you are working on!