1

I am getting different answers for below two implementations in C and python. In Python

print(pow(15, 47413144071, 94826288143))

Prints 1

But in C

#include<stdio.h>
unsigned long Power(unsigned long A, unsigned long B, unsigned long X)
{
    unsigned long res = 1;
    while( B > 0 )
    {
        if( B & 1UL )
        {
            res = ( res * A ) % X;
        }
        A = ( A * A ) % X;
        B >>= 1UL;
    }
    return res;
}
int main()
{
    printf("%lu", Power(15, 47413144071, 94826288143));
    return 0;
}

Prints: 893231448 Any Help Is Appreciated.

Shashank
  • 9
  • 5
  • 3
    An `unsigned long` is only guaranteed to be able to represent values in the range `0` to `4294967295` (although the standard *permits* representing a larger range, it is not required). Both the values `B` and `C` you are passing exceed that. If your implementation provides a 32-bit `unsigned long` then the values passed will be reduced modulo `4294967296`. – Peter Mar 05 '21 at 12:19
  • I did that but it is still giving the output as mentioned above. Output: 893231448 – Shashank Mar 05 '21 at 13:36
  • `47413144071` -- Why did you assume that Python uses an `unsigned long` here? Since you tagged this as `C++`, there is a `uint64_t` type that you could have used. – PaulMcKenzie Mar 05 '21 at 13:43
  • Used uint64_t also. Still its not working. – Shashank Mar 05 '21 at 13:53
  • C does not have arbitrary-precision integers. You must either implement yourself, or use an external library, such as `gmp`. – M. Nejat Aydin Mar 05 '21 at 14:42
  • @ShashankRai [Python has arbitrary length integers](https://rushter.com/blog/python-integer-implementation/). This goes back to my first comment -- you should not have assumed that Python used a fixed-size integer type. But even then, Python implements arbitrary length integers programmatically using their own "library", no different than if you used a third-party C library to implement the same thing. – PaulMcKenzie Mar 05 '21 at 16:04

2 Answers2

0

As noted in the comments unsigned long is only guaranteed for values up to 4294967295, and your B and X are larger, otherwise you get values mod 2^32 mod X instead of values mod X. For the inputs you could replace them by unsigned long long.

However, you don't only need B and X to be representable as unsigned long long but also res*A and A*A, and since A can be as large as X-1 it does not suffice to use unsigned long long in C to handle this.

Hans Olsson
  • 11,123
  • 15
  • 38
0

Here is a proof that your C code generates overflows even with uint64_t type.

I have just converted the C code into Python, adding an overflow test. The first value not representable as an uint64_t is 2**64 or 0x10000000000000000 or 18446744073709551616. So I tested all products before the mod operation:

def Power(A, B, X):
    res = 1
    step = 1
    while (B > 0):
        if B & 1:
            if (res * A) >= 0x1000000000000000:
                print("uint64_t overflow at res step", step)
            res = (res * A) % X
        if (A * A) >= 0x1000000000000000:
            print("uint64_t overflow at A step", step)
        A = (A * A) % X
        B >>= 1
        step += 1
    return res

>>> Power(15, 47413144071, 94826288143)
uint64_t overflow at A step 4
uint64_t overflow at A step 5
uint64_t overflow at A step 6
uint64_t overflow at A step 7
uint64_t overflow at A step 8
uint64_t overflow at A step 9
uint64_t overflow at res step 10
uint64_t overflow at A step 10
uint64_t overflow at A step 11
uint64_t overflow at res step 12
uint64_t overflow at A step 12
uint64_t overflow at A step 13
uint64_t overflow at res step 14
uint64_t overflow at A step 14
uint64_t overflow at A step 15
uint64_t overflow at A step 16
uint64_t overflow at res step 17
uint64_t overflow at A step 17
uint64_t overflow at res step 18
uint64_t overflow at A step 18
uint64_t overflow at A step 19
uint64_t overflow at res step 20
uint64_t overflow at A step 20
uint64_t overflow at A step 21
uint64_t overflow at A step 22
uint64_t overflow at A step 23
uint64_t overflow at A step 24
uint64_t overflow at A step 25
uint64_t overflow at res step 26
uint64_t overflow at A step 26
uint64_t overflow at A step 27
uint64_t overflow at res step 28
uint64_t overflow at A step 28
uint64_t overflow at A step 29
uint64_t overflow at A step 30
uint64_t overflow at A step 31
uint64_t overflow at A step 32
uint64_t overflow at res step 33
uint64_t overflow at A step 33
uint64_t overflow at res step 34
uint64_t overflow at A step 34
uint64_t overflow at A step 35
uint64_t overflow at res step 36
uint64_t overflow at A step 36
1

This definitely proves that the algo is correct (we get 1 in the end) but also that you get a number of overflows if you try to use 64 bits integers.


Long story made short, if you need to implement that in C, you need either a system having a uint128_t type, or use a multiprecision library like gmplib.

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252