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.