Why does Int ^ Int get cast to Double?

BlitzMax Forums/BlitzMax Programming/Why does Int ^ Int get cast to Double?

Russell(Posted 2006) [#1]
To me this makes no sense since it is mathematically impossible for a whole number taken to the power of another whole number to have a fractional result.
Example:
For Local a = 0 To 31
	Print 2 ^ a
Next

will print
1.0000000000000000
2.0000000000000000
4.0000000000000000
7.9999999999999982
15.999999999999998
32.000000000000000
63.999999999999979
127.99999999999997
255.99999999999994
511.99999999999994
1024.0000000000000
2048.0000000000000
4095.9999999999968
8191.9999999999945
16383.999999999991
32767.999999999985
65535.999999999978
131071.99999999996
262143.99999999994
524287.99999999994
1048576.0000000000
2097152.0000000000
4194304.0000000009
8388608.0000000019
16777215.999999976
33554432.000000015
67108863.999999918
134217728.00000009
268435455.99999970
536870912.00000036
1073741823.9999990
2147483648.0000019

If I try to 'force it' to Integer...
For Local a:Int = 0:Int To 31:Int
	Print Int(2 ^ a)
Next

...the result is:
1.0000000000000000
2.0000000000000000
4.0000000000000000
7.9999999999999982
15.999999999999998
32.000000000000000
63.999999999999979
127.99999999999997
255.99999999999994
511.99999999999994
1024.0000000000000
2048.0000000000000
4095.9999999999968
8191.9999999999945
16383.999999999991
32767.999999999985
65535.999999999978
131071.99999999996
262143.99999999994
524287.99999999994
1048576.0000000000
2097152.0000000000
4194304.0000000009
8388608.0000000019
16777215.999999976
33554432.000000015
67108863.999999918
134217728.00000009
268435455.99999970
536870912.00000036
1073741823.9999990
2147483648.0000019

..which is incorrect. There is no Round function, so how do I get around this almost-a-bug?

When I try 'Debug Pow(2,24)' in PureBasic I get:
'16777216.0' (which, although for some odd reason is still a float, is correct - And this is without using the Round() procedure).

So what's up? Can't these compilers see that all of the operands are integers, and therefore, the result should be, too?

Russell


Jesse(Posted 2006) [#2]
that's not right, becuause when I did your code:
 For Local a:Int = 0:Int To 31:Int
	Print Int(2 ^ a)
Next

I get:

1
2
4
7
15
32
63
127
255
511
1024
2048
4095
8191
16383
32767
65535
131071
262143
524287
1048576
2097152
4194304
8388608
16777215
33554432
67108863
134217728
268435455
536870912
1073741823
-2147483648


which version of bmax are you using. I am using 1.22
try puting brackets like this:

print (int(2 ^ a))

I just noticed the results are wrong also


<EDIT>
this is how I fixed it:
Local b:Int = 2
For Local a:Int = 0:Int To 31:Int
	Local c:Int = (b ^ a+.1)
	Print c
	
Next

but that should not be anyway to correct this problem.
I thing this is a compiler error.


Russell(Posted 2006) [#3]
Yeah, I copied the previous results again by mistake. Your results are what I got, too (version 1.22).

It would be usable if BMax didn't just take the fractional part off and actually rounded correctly (127.99999999999997 becomes 127 and not 128? DUH!) On a side note, this is the traditional function of Int(), which is to return the integer portion of a float. While casting, though, this should not be the case.

I know it has to do with overflows and the limits of number representation, etc, but it's still strange behaviour, if you ask me.

Russell


Jesse(Posted 2006) [#4]
yes but even when I do double I get the same results.
For Local a! = 0! To 31!
	Print (2! ^ a)

Next


what puzzles me is how it get a fractional result by multiplying two whole numbers even if they are type double.


FlameDuck(Posted 2006) [#5]
The ^ operator is a floating point operation, executed on the FPU.


H&K(Posted 2006) [#6]
Its a problem thats been raised before, they have to be floats (or doubles) or it would fail on 3^30, which even thou are both int would fail with int as a result.

For Local a:Int = 0:Int To 31:Int
	Print Int(2 ^ a+0.5)
Next

Thats how you fix that. (Yes it a pain, but thats what you do)


taxlerendiosk(Posted 2006) [#7]
If that's not just an example chosen to show the problem, and 2 actually is the number that you want to raise to the power of an integer, try doing (1 Shl x) instead of (2 ^ x)


Russell(Posted 2006) [#8]
PureBasic also returns (and requires as parameters) floats. But the difference is that PB returns the correct answer. (Pow(2,24) returns 16777216.0, which is correct, wheras BM returns 16777215.xxx, which is incorrect).

If ^ is a floating point operator (not mentioned in the manual, once again), then why not also include an integer version or, better yet, have BM call te correctone depending on whether there are floats/doubles involved or not.

As far as failing on overflow, this can be 'caught' by various means (such as checking the 'carry' flag of the processor).

Another possiblity is to use this library:
http://www.tc.umn.edu/~ringx004/mapm-main.html

which is open source, ready to be converted to a module (hint, hint) and can handle numbers with up to BILLIONS of digits. Yes, I said Billions.

Russell


H&K(Posted 2006) [#9]
But you wouldnt want to use it in a game engine tho. Would you?

Its a libary for really comples exact maths, like slingshot a spaceship round mars. Its for when you "Need" exact, not speed

Mind you This IS stupid
a=3
Print ((2 ^ a) = (2 ^ 3))
Cos its says 2^a when a is 3, and 2^3 are not the same


TomToad(Posted 2006) [#10]
Just replace
double bbFloatPow( double x,double y ){
	double r;
	static const BBInt64 Nan64=0xfff8000000000000LL;
	if( x>0 ) return exp( y * log(x) );
	if( x==0 ){
		if( y>0 ) return 0;
		if( y==0 ) return 1;
		return 1.0/0.0;
	}
	if( floor(y)!=ceil(y) ) return *((double*)&Nan64);	
	r=exp( y * log(-x) );
	return ((BBInt64)floor(y) & 1) ? -r : r;
}
in BlitzMax\Mod\brl.mod\blitz.mod\blitz_cclib.c with
double bbFloatPow( double x,double y ){
	pow(x,y);
}
and recompile the modules.

Seems I remember reading somewhere that BRL decided to rewrite the power function to be faster at the cost of slight inaccuracies.


FlameDuck(Posted 2006) [#11]
(Pow(2,24) returns 16777216.0, which is correct, wheras BM returns 16777215.xxx, which is incorrect)
Just because the result is rounded off does not mean it's more accurate.


Russell(Posted 2006) [#12]
16777216 is exactly correct because we're working with integers. Rounding 16777215.999999976 down to 16777215 makes no sense. Correct rounding would, indeed, give the correct answer. That means rounding to the nearest integer.

On this same note, I discovered something very unusual. If you run
Print 2 ^ 24

..you get 16777216.000000000 (correct). But if you run
For a = 0 To 31
  Print 2 ^ a
Next

..the result for 2 ^ 24 is 16777215.999999976 (wrong!)! Do literals get converted differently than variables, and if so, why?

Russell


H&K(Posted 2006) [#13]
@ Russtle,

I posted that 2^a<>2^24 when a = 24 three posts up, and yesterday


Curtastic(Posted 2006) [#14]
hmm int ^ int should really be int. I don't care what the internal fpu or whatever does. That's why I use blitz...


And constants like 2^24 get simplified so that they aren't calculated each time. I guess it gets calculated differently when it does this. Its similar to that blitz3d bug
blitz3d code:
Const a=-1
Local b=-1
Print a/2
Print b/2
WaitKey
End



Augen(Posted 2006) [#15]
Where are Floyd and sswift? I'm sure they could explain it.


Fabian.(Posted 2006) [#16]
Here's a function which could provide Int ^ Int returning Int:
Strict
Framework brl.blitz

Import brl.standardio

Local loop1 = 1000000 , loop2 = 1000000
Local n , x = 100 , y = 100
Local start1 = MilliSecs ( )
For Local i = 0 Until loop1
  n = x ^ y
Next
Local end1 = MilliSecs ( )
Local start2 = MilliSecs ( )
For Local i = 0 Until loop2
  n = PowInt ( x , y )
Next
Local end2 = MilliSecs ( )
Print ( end1 - start1 ) / Float loop1 + " milliseconds needed for ~qn = x ^ y~q."
Print ( end2 - start2 ) / Float loop2 + " milliseconds needed for ~qn = PowInt ( x , y )~q."

Function PowInt:Int ( x:Int , y:Int ) NoDebug
  If y < 0
    Return
  EndIf
  Local n = 1
  While y
    n :* x
    y :- 1
  Wend
  Return n
EndFunction
It seems to be a bit faster than doing float ^ float + rounding to int.

I think the BlitzMax compiler should compile intvar ^ intvar to a call to this function.


BHoltzman(Posted 2006) [#17]
Visual Basic 2005 casts ^ operations to double as well. My guess is that it's because of the API routine that's being called.