Math Isnt Working
BlitzMax Forums/BlitzMax Programming/Math Isnt Working
| ||
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 |
| ||
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.... |
| ||
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 |
| ||
Appears to be a bug to me. |
| ||
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. |
| ||
Or you could just do Ceil(2^num) I think. |
| ||
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 |
| ||
For num = 0 To 10 Print 2^num NextThe 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. |
| ||
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) |
| ||
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. |
| ||
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.. |
| ||
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?). |