Why is my arithmetic with a long long int behaving this way?

The issue with

LL m = pow(2, n + 1) - 2;

is that pow(2, n + 1) is not a long long. It has the type double (refer to cppreference) and because the value is so large, subtracting 2 from it will not change its value. That means that m will not have the correct value. As you have already found, you need to assign the result first and then do the subtraction. Another alternative is to write your own pow that will return a integer type when given an integer type so you can do the raising to the power and subtraction at the same time.


The pow function returns a value of type double, which only has 53 bits of precision. While the returned value will fit in a double even if n is greater than 53, subtracting 2 results in a value of type double that requires more than 53 bits of precision so the result of the subtraction is rounded to the nearest representable value.

The reason breaking out the subtraction works is because the double value returned from pow is assigned to a long long, then you subtract an int from a long long.

Since you're not dealing with floating point numbers and you're only raising 2 to a power, you can replace the call to pow with a simple left shift:

LL m = (1LL << (n + 1)) - 2;

This keeps all intermediate values at type long long.


I expect both codes to be equivalent, why is this not the case?

Your expectation is wrong. Your second code would be equivalent to this:

auto m = static_cast<LL>( pow(2, n + 1) ) - 2;

as due to conversion rule for arithmetic operators and the fact that std::pow() returns double in this case:

For the binary operators (except shifts), if the promoted operands have different types, additional set of implicit conversions is applied, known as usual arithmetic conversions with the goal to produce the common type (also accessible via the std::common_type type trait). If, prior to any integral promotion, one operand is of enumeration type and the other operand is of a floating-point type or a different enumeration type, this behavior is deprecated. (since C++20)

If either operand has scoped enumeration type, no conversion is performed: the other operand and the return type must have the same type

Otherwise, if either operand is long double, the other operand is converted to long double

Otherwise, if either operand is double, the other operand is converted to double

Otherwise, if either operand is float, the other operand is converted to float

...

(emphasis is mine) your original expression would lead to double - double instead of long long int - long long int as you do in the second case hence the difference.