Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

constexpr unsigned int * unsigned long not evaluated as unsigned long?

The following integer power function returns 0 when the result should be greater than 2^32 if the base argument is unsigned int, but works fine when the base argument type is changed to an unsigned long. Why? Compiler bug?

#include <cstdint>
#include <iostream>
using uint  = uint32_t ;
using ulong = uint64_t ;

static constexpr ulong ipow(uint base, uint exp, ulong ans = 1) // Integer power
{
    return exp < 1 ? ans : ipow(base*base, exp/2, (exp % 2) ? ans * base : ans) ;
}

int main ()
{
    for (int i(2) ; i < 64 ; ++i)
        std::cout << "ipow(2," << i << ") = " << ipow(2,i) << "\n" ;
    return 0 ;
}

Compiled and ran above code (Apple clang version 17.0.0 (clang-1700.3.19.1) Target: arm64-apple-darwin24.6.0).
Extract of incorrect output:

ipow(2,27) = 134217728
ipow(2,28) = 268435456
ipow(2,29) = 536870912
ipow(2,30) = 1073741824
ipow(2,31) = 2147483648
ipow(2,32) = 0
ipow(2,33) = 0
ipow(2,34) = 0
ipow(2,35) = 0
like image 227
user31597808 Avatar asked Dec 03 '25 21:12

user31597808


1 Answers

Why? Compiler bug?

This is not a compiler bug.

The problem is that your base parameter is 32 bit (uint32_t), and so the calculation base*base overflows with values higher than around 2^31.
If you cast the first base to 64 bit it will fix the calculation overflow, but this value is still passed to a 32 bit parameter (base) in the recuresive call and so it will not be preserved.

The solution is to change base parameter type to be 64 bit. This will solve the calculation overflow in your case, as well as preserve the value in the recursive call:

#include <cstdint>
#include <iostream>
using uint  = uint32_t ;
using ulong = uint64_t ;

//--------------------------vvvvv-------------------------------
static constexpr ulong ipow(ulong base, uint exp, ulong ans = 1)
{
    return exp < 1 ? ans : ipow(base*base, exp/2, (exp % 2) ? ans * base : ans) ;
}

int main ()
{
    for (int i(2) ; i < 64 ; ++i)
        std::cout << "ipow(2," << i << ") = " << ipow(2,i) << "\n" ;
}

Output:

ipow(2,2) = 4
ipow(2,3) = 8
ipow(2,4) = 16
.
.
.
ipow(2,61) = 2305843009213693952
ipow(2,62) = 4611686018427387904
ipow(2,63) = 9223372036854775808

Live demo

like image 108
wohlstad Avatar answered Dec 05 '25 11:12

wohlstad



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!