Debug mode returns a different number!

BlitzMax Forums/BlitzMax Programming/Debug mode returns a different number!

Grey Alien(Posted 2008) [#1]
Check out this code:

SuperStrict

Global frl!=25
Global currentspeed!=0.2

Local length% = CalcAnimLength(150)
Print length

Function CalcAnimLength#(ms%)
	Return ms/frl/CurrentSpeed
End Function


Man this took me AGES to whittle down. Anyway if you run it in debug mode it prints 30. Run it in normal mode and it prints 29.

If I change the return param of CalcAnimLength from a float to a double or an int, the result is the same in both modes i.e. correct.

Also this is a good example of how a float to int conversion can go badly wrong anyway because according to Windows Calc 150/25/0.2 = 30 but in Blitz the float is like 29.99999 something and this gets truncated down to an int of 29! So basically don't rely on the compile to convert float to int for you, either keep the end result as an in or use a special rounding function to do it properly.

Anyway, if everyone concurs that the compile is working differently in debug mode then I can post this in the bug forum.


degac(Posted 2008) [#2]
Confirm.
Release mode = 29
Debug mode = 30

Tested on Bmax 130 SVN rev.170

A possible solution (if any other way is available) is to add an 'error correction' delta

SuperStrict
Global frl!=25
Global currentspeed!=0.2
Local length% = CalcAnimLength(150)
Print length
Function CalcAnimLength#(ms%)
	Return (ms/frl/CurrentSpeed)+.0000005
End Function


the delta (.0000005) is so small to 'repair' the error.


Floyd(Posted 2008) [#3]
Slight floating point differences in debug versus release have been reported several times. It's not likely to change.

As noted the problem here is that the floating point result is very close to 30, but not exact. If it happens to be slightly less than 30 then it will become 29 when chopped to an integer value. You want to round to integer rather than chop. BlitzMax really should have a Round() function, but it doesn't so you have to do this yourself.

There is one more numerical gotcha to watch out for. The default type for floating point constants is single precision.
Print

x! = 0.2
Print x

x! = 0.2!
Print x


As luck would have it using 0.2! in the original example will make the problem go away. But you can't rely on this. In general you still need to round to integer.


Perturbatio(Posted 2008) [#4]
SuperStrict

Global frl!=25
Global currentspeed!=0.2

Local length% = CalcAnimLength(150)
Print length

Function CalcAnimLength#(ms%)
	Print ms/frl/CurrentSpeed
	Return ms/frl/CurrentSpeed
End Function

If I run this in debug and non-debug, the print statement shows the same result for both (29.999999552965171), but the return value is different.

It looks like it's rounding down in non-debug and up in debug when converting to an int.


GW(Posted 2008) [#5]
what if you use?:

Function CalcAnimLength#(ms%) Nodebug


Grey Alien(Posted 2008) [#6]
The default type for floating point constants is single precision.
. I never knew that, thanks! And yes I normally use my own rounding function when converting from float to int, just didn't in this case. It's easy to fix but I thought I'd bring it up due to the debug/non-debug discrepancy. Is this NOT worth posting on the bugs forum then?

@Perturbatio: Yes I ran the same code before and saw that is indeed what it's doing.


Floyd(Posted 2008) [#7]
Is this NOT worth posting on the bugs forum then?

That's certainly debatable. But it is a known issue which has been reported many times.

What is boils down to is that floating point arithmetic is done in special 80-bit registers, although variables in the program are 64-bit or 32-bit. Such a register might hold a value like 0.99999999, which would become 1.0 if it were stored to memory in a 32-bit ( single precision ) variable.

A value computed by a function is returned in one of these registers. This value might be 0.99999999, which still exists from the earlier calculation, even though the function returns a single precision value. When in Debug mode Local variables routinely get saved to memory and the 0.99999999 gets changed to 1.0 at some point.

This is what happened in your example. A value slightly less that 30 is calculated and held in a register. If saved to memory as a single precision value it becomes 30.0 exactly. When chopped to integer this will become 30 or 29 depending on whether such a save has been done.

If you want to try your luck with a bug report here is a simplified example. You will have to provide your own argument about why this is a good enough reason to change the compiler.

' This will produce an integer result of 0 or 1 depending on Debug or Release build.

mode$ = " Release Mode"

?Debug 
mode = "Debug mode"
?

Print 
Print "*****  " + mode + "  *****"
Print

Local IntVal% = test()

Print "  Returned value chopped to Int = " + IntVal

Function test#( )

   Local dbl! = 0.99999999!
   Return dbl

End Function



Grey Alien(Posted 2008) [#8]
Neat test (and explanation). I'm not that bothered by it but thought I'd mention it. To get it fixed could well screw up other things too I gues...