Floating Math Problem

BlitzMax Forums/BlitzMax Programming/Floating Math Problem

Tibit(Posted 2004) [#1]
I got confused when I found a bug which I just don't understand. Can someone explain the strange result I get when running this:
Graphics 800,600,32,75

A% = 100

Timer = MilliSecs() + 15

While Timer > MilliSecs() 

	A:-100*0.05
	A:+100*0.05
	Print A

Wend

I guess it has something to do with rounding up the decimals, but why on earth does it only apply it I use Graphics mode? Because if you comment out Graphics it returns A=100 all the way. I hope you get the same result as me, cause I get A =100 and then it decreases each loop until A=5.


BODYPRINT(Posted 2004) [#2]
yes, it is definately a rounding problem because if you make A a float it works.

What graphics has to do with it is beyond me??

I would bug report it.


Floyd(Posted 2004) [#3]
There is a problem, but not the one you might think.
The 99,98,97... behavior is correct.

First, consider a simpler example:
delta# = 0.1
A% = 3

For n = 1 To 4
	A = A + delta
	A = A - delta
	Print A
next
What happens is that 'A + delta' is a floating point calculation. With A = 3 the result is 3.0 + 0.1 = 3.1, which must be converted back to an integer and assigned to A. This is done by chopping off the digits after the decimal point, so A is 3.

Then 'A - delta' evaluates to 2.9, and is chopped to an integer so A is 2.

When A reaches zero the results are 0.1 chopped to 0, and then -0.1 also chopped to 0. So A stays at 0.

In the original code 100.0 * 0.05 is not exactly 5.0. It is, in fact, very slightly larger than 5.0. This is what accounts for the 99,98,97...

The real problem is the second example, which produces 100, 100...

Ironically, this happens because the calculation A :- 100.0*0.05 is done slightly LESS accurately, so that A does not become slightly smaller and get chopped down to the next lower integer.

Why would the second code use slightly less accurate arithmetic? It must be that in the first (Graphics) example some intermediate result is being held in a register, which is a little more accurate than a number which has been written out to memory.

I'm not sure why the second example can't do the same thing. But be aware that you are always going see this sort of problem when mixing integers and floating point numbers. For example, the numbers 2.000001 and 1.999999 are nearly identical. But when converted to integers they become 2 and 1.


Hotcakes(Posted 2004) [#4]
Then 'A - delta' evaluates to 2.9, and is chopped to an integer so A is 2.

So BlitzMax no longer rounds numbers?


Tibit(Posted 2004) [#5]
Thanks for the explination Floyd, now I get why it rounds it the wrong way =) (Feelt so unlogical at first)

It seems like that if you use Graphics then BlitzMax automatically rounds floats to the closest int? If not it just cuts the float at the decimal point?

Btw You can always use Int(somefoat) to round to the closest int if you want to be sure.


Floyd(Posted 2004) [#6]
BlitzMax always chops, previous versions of Blitz rounded.

A%=Int(somefloat) is the same as the automatic conversion in A%=somefloat, namely chop.

The different graphics/nongraphics results really are caused by slightly different accuracy in the floating point arithmetic done before the conversion to integer. The real mystery is why that accuracy should be different.


ashmantle(Posted 2004) [#7]
Could it have something to do with the fact that Mark explained in another thread, that locals will try to use CPU registers if available, but resort to memory if not?


Who was John Galt?(Posted 2004) [#8]
I hate that Max chops numbers with float to int conversions. Surely you're going to want the closest whole number as standard, just like in previous versions of Blitz and every other language under the sun?


Kanati(Posted 2004) [#9]
Well... you can always add your own I guess...

Function Round:Int(flt:Float)
	If flt - Int(flt) >= 0.5 Then
		Return Int(flt) + 1
	else
		Return Int(flt)
	endif
End Function


Add a mod for it. :)


Hotcakes(Posted 2004) [#10]
Or...

  A:Int=(B:Float+.5)


Will do it. K's function is a bit fartsy =]


Kanati(Posted 2004) [#11]
I'm just a fartsy kinda guy. :)


Massimo32(Posted 2005) [#12]
For both positive and negative numbers:

A:Int = Sgn(B:Float)*Int(Abs(B:float) + 0.5)



Robert(Posted 2005) [#13]
Surely you're going to want the closest whole number as standard, just like in previous versions of Blitz and every other language under the sun?


Every other language I know does the same as BlitzMAX - it chops off the decimal part of the float.