0

At the moment i port a script from perl to python, but somehow, i get a really strange behaviour by a modulo calculation.

32185864684058608501682550623 % 62

In Perl I get 6 as result and with Python i get 25L.

Is there somehow a way to get the number 6?

Also with python3 i get the same result.

demonking
  • 2,584
  • 5
  • 23
  • 28

3 Answers3

3

Perl's built-in support for large numbers is not great. You get an answer of 6 due to overflow. Compare

$ perl -e 'print 32185864684058608501682550623 % 62, "\n"'
6
$ perl -Mbignum -e 'print 32185864684058608501682550623 % 62, "\n"'
25
chepner
  • 497,756
  • 71
  • 530
  • 681
  • the strange thing is, that Matt Davidson answer return 6.0 ;) – demonking Apr 14 '15 at 20:28
  • 1
    `fmod` uses the underlying C library, which works in floating point numbers, so you lose some precision when converting large integers. The `bignum` module in Perl and Python's built-in integer type work with exact integer arithmetic. – chepner Apr 14 '15 at 20:32
  • @chepner It's not about losing a precision. It's about architecture's based number size overflow. The correct result requires arbitrary precision/big numbers arithmetic implementation like [GMP](https://gmplib.org/). – David Unric Apr 14 '15 at 20:37
  • 1
    Converting a large integer to a floating-point type which does not use enough bits to store the exact integer value *is* a loss of precision. Consider 3001: to 4 significant digits, you would write it as 3.001e3, but to only 2 significant digits, it would be 3.0e3, the same as any integer between 2950 and 3049. – chepner Apr 14 '15 at 20:44
2

By default, Perl converts big integer numbers to a float rather than use arbitrary precision:

$ perl -e 'print 32185864684058608501682550623 + 0'
3.21858646840586e+28

Vs Python switching automatically to an arbitrary precision long with a big integer:

>>> 32185864684058608501682550623 + 0
32185864684058608501682550623L

You can see that this is a loss of information about the long integer number in Perl (without using bignum):

$ perl -e 'print 32185864684058608501682550623-32185864684058608501682550620'
0

Vs Python's default:

>>> 32185864684058608501682550623-32185864684058608501682550620
3L

To get the same (incorrect) result on Python, just convert the big num to a float first:

>>> float(32185864684058608501682550623) % 62
6.0

Then convert back to an int if you want an int:

>>> int(float(32185864684058608501682550623) % 62)
6

BTW: 32185864684058608501682550623 % 62 is actually 25, not 6 ;-)

dawg
  • 98,345
  • 23
  • 131
  • 206
1

If you wished to have the same behaviour I'd recommend using the math module. The math module allows you to use fmod, which will mimic the expected Perl behaviour. After this you could always cast back to int if you really wanted to...

In Python 2.7:

>> import math
>> print math.fmod(32185864684058608501682550623, 62)
6.0
>> print int(math.fmod(32185864684058608501682550623, 62))
6

In Python 3:

>> import math
>> print(math.fmod(32185864684058608501682550623, 62))
6.0
>> print(int(math.fmod(32185864684058608501682550623, 62)))
6

However, fmod is for floating point numbers and is inaccurate here. It seems likely that Perl is using fmod (I know little to nothing about Perl). Using Wolfram Alpha, we can verify that Python 2 and 3 default behaviour is correct, and the answer is 25.

Therefore if we wish to get cast our answer to int, this would be sufficient:

>> print 32185864684058608501682550623 % 62
25L
>> print int(32185864684058608501682550623 % 62)
25

This SO answer might help you understand the inaccuracies of fmod:

Modulus of a really really long number (fmod)

Community
  • 1
  • 1
Matt Davidson
  • 728
  • 4
  • 9