Math Isnt Working

BlitzMax Forums/BlitzMax Programming/Math Isnt Working

KrayzBlu(Posted 2006) [#1]
Hi, my BM math isnt working quite right.... is there a fix for this?

For num = 0 To 10
	Local calc = (2^num)
	Print calc
Next


The output, unfortunately, is thus:

1
2
4
7
15
32
63
127
255
511
1024


Not exactly the greatest standard....

Thnx
KrayzBlu


KrayzBlu(Posted 2006) [#2]
Another One:

Print 10^10
Local pnum = 10^10
Print pnum


Makes:

10000000000.000000
1410065408


This makes me think its a variable type problem, but the numbers aren't out of range....


Gabriel(Posted 2006) [#3]
Strange. Only seems to happen if it's a variable instead of a constant. Perhaps someone else has an idea why :

num=3
calc = (2^num)
Print calc
calc = (2^3)
Print calc



TomToad(Posted 2006) [#4]
Appears to be a bug to me.


TomToad(Posted 2006) [#5]
I found the problem. Blitz uses the formula Exp(y * Log(x)) to find x^y. The problem is that Log() and Exp() work on doubles and floating point numbers are subject to rounding errors. If you make calc a float, you'll see that it calculates 2^3 to 7.9999999 which when converted to int, becomes 7. If you change the program to read
Local calc = (2^num)+.5

Then the result will be correct.


N(Posted 2006) [#6]
Or you could just do Ceil(2^num) I think.


TomToad(Posted 2006) [#7]
Hmm, weird. I was messing around with the source changing things and recompiling. I then set it back to what it was originally and now Local calc:float = (2^num) gives correct results. 8.000000 for 2^3. But it still gives incorrect results if I use calc:int = (2^num). When I had calc as a float before making changes, it resulted in 7.9999999


Floyd(Posted 2006) [#8]
For num = 0 To 10
	Print 2^num
Next
The number 511.99999999999994 becomes 511 when when assigned to an integer variable. BlitzMax converts floating point values to integer by truncation, simply throwing away everything after the decimal point. This kind of 'off by one' error is bound to happen if you depend on floating point results to have exact integer values.

The other question is why there may be different results when constants are used rather than variables. BlitzMax originally computed x^y using the pow() function in the C++ math library. This generated a lot of complaints because the MingW pow() was very slow. So a native BlitzMax pow() was written. An expression like 2^9 is evaluated by the compiler before the program runs and still uses the old pow(). 2^nine, where nine is a variable, uses the new pow().

The new pow() does not exactly match the old one. The original pow() has code to detect special cases where the result can be computed exactly.

The important lesson here is that you should not depend on floating point arithmetic to give exact integer results.


Dreamora(Posted 2006) [#9]
You should neve depend on correct floating point calculations anyway. Where it might be somewhere next correction the area of +-0 it will become less and less correct, the further you are away from 0 as the "gaps" between possible floating point values become larger and larger. (its already unprecise enough in the area of 0-1 if you try to do complex calculations which is why I wrap those calculations to 1-1000 or 1-10000 with ints where possible)


TomToad(Posted 2006) [#10]
The important lesson here is that you should not depend on floating point arithmetic to give exact integer results.

But you wouldn't think that using a built in Blitz math function on 2 integers would result in float errors. Shouldn't Blitz use different methods depending on whether the arguments are floats or ints? The function that KrayzBlu posted is a common one when masking data on a bit per bit basis. It can result in huge errors when the result is off by one. C++ pow() function does not have this problem.


Defoc8(Posted 2006) [#11]
if your working with bitmasks..use shifting operations..
val=1 shl n
n=0, val=1 shl 0 =1
n=1, val=1 shl 1 =2
n=2, val=1 shl 2 =4
....
....

using power operations to determine bitmasks seems a little
pointless..this is what you are trying to do right?
anyway - perhaps the above logic will help..


WendellM(Posted 2006) [#12]

Shouldn't Blitz use different methods depending on whether the arguments are floats or ints?

I agree that this approach makes sense. AFAIK, it should be possible for the "^" function to determine whether both parameters are integers and, if so, use an integer-type solution (for/next multiplication?) rather than a double-type one. The last-minute conversion to Double (needed since a return can only be one type) would presumably avoid the ".99999" problem (if not, perhaps something like 0.00000001 could be added if parameters were Integers?).