Compile issue with negative exponents

Consider

f=Compile[{{x,_Integer}},2^-x];
g=Compile[{{x,_Integer}},1/2^x];


I have an issue with f:

f[1]
(*CompiledFunction::cfn: Numerical error encountered at instruction 2; proceeding with uncompiled evaluation. *)
(*1/2*)


while g works well:

g[1]
(*0.5*)


Is this a bug?

This is on Windows 7 and Mathematica 11.2.0

• You can circumvent this by using a real 2. instead of an integer 2: f = Compile[{{x, _Integer}}, 2.^-x]; – Henrik Schumacher Feb 21 '18 at 22:15
• Compile can work with integers, reals (i.e., _Real) and complexes. It will not work with rationals (i.e. _Rational). In the first case, the Compiler thinks the result might be an integer, while in the second case it thinks the result is a real. Since a rational can be coerced into a real, the second example works and the first doesn't. – Carl Woll Feb 21 '18 at 22:31
• @CarlWoll which begs the question of how to tell Compile the type of result expected – LLlAMnYP Feb 22 '18 at 13:20
• @LLlAMnYP You can tell it the type of the result (or enforce a certain type on any expression if the casting is possible) by using Module and predefining the variables. For instance, this enforces a complex result, although all computations are perfectly valid with integers: Compile[{{x, _Integer}}, Module[{result = 0. I}, result = 1 + x]]; – halirutan Feb 25 '18 at 5:34
• @halirutan Right, I was thinking of the idiom of result = Most[{0.}] to tell Compile that I'm dealing with a rank 1 real tensor, just didn't realize how to apply it for a lone real. – LLlAMnYP Feb 25 '18 at 11:45

In Mathematica, numbers have certain types and switching between them happens without that you have to care about this. As example take your two expressions:

x = 1;
2^-x
1/2^x
(* 1/2 *)
(* 1/2 *)


The transition from integers to rational numbers just happens during the evaluation. Within compile, everything is different because there, types are associated with each expression and operations on them need to be consistent. In addition, the type Rational does not exist.

In your first example inside compile, the expression 2^-x denotes the integer power of 2 and -x. You can see this pretty clearly when using CompilePrint of the CompiledFunctionTools package:

All the Ix are integer registers and there is no way that I3 allows anything else than an integer in the assignment. When you violate this, then Mathematica catches this error and gives you the result that is computed with the Kernel. In your error message, you additionally see that the error happened at instruction 2 which is exactly the line I3 = Power[ I1, I2].

Looking at your second example g shows why this works:

The 2^x part is still an integer register but for x=1 everything is good because it will result in an integer. Then, a cast from Integer to Real is done with R0=I2 which is fine too. After that, the reciprocal is calculated which is a perfectly valid operation for real numbers.

This is why your second example works for x=1. However, if you read carefully, you see how we can break g. The first instruction relies on the fact that the Power gives an integer which is not true for e.g. x=-1. This is why g[-1] fails

Finally, the way to ensure that the Power is always correctly calculated is to enforce its calculation on real numbers. This can simply be done by using

h = Compile[{{x, _Integer}}, 2.0^-x];
CompilePrint[h]


because then, the compiled code looks like this

The second instruction is now the power of real numbers and not integers.

{h[-1], h[1], h[0]}
(* {2., 0.5, 1.} *)
`