1.2. Integers#

1.2.1. Imprementation of Integers#

Digital computers express everything as bit strings as discussed in the previous section. Encoding an integer in a bit string is simple. Just interpret the string as a binary number. Consider a bit string \(b_{N-1} \cdots b_2 b_1 b_0\). Then, the corresponding integer in the binary form is \(I = \sum_{k=0}^{N-1} 2^k b_k\). For example, bit string \(101\) corresponds to integer \(1\times 1+ 2 \times 0 + 4 \times 1 =5\). If \(\ell\) is the length of a bit string, we can express integers up to \(2^\ell\). For example, three bits can encode \(2^3=8\) integers, \(0,1,2,\cdots, 7\).

In principle, huge integers can be implemented as long as there is enough memory. In principle Python allows us to use any integers. When we use an integer, sufficient number of bits are automatically allocated to it.

This feature is rather unique to python and most of common computer languages such as C++ and Fortran use several fixed length of integers, known as int8, int16, int32, and int64 where the numbers indicate the length of bit strings. Therefore, only integers in a limited range are available. Moreover, the type of variable must be declared in advance in these language. Python allocates an appropriate size automatically and increases it dynamically when needed. Python is far more flexible in handling integer numbers than other languages.

Although the python core does not have the size limitation, we still have to worry about it in scientific computation. Since Python lacks mathematical functions, it relies on third-party packages. Moreover, python is an interpreter programming language, its computing speed is rather slow and thus python core alone is not suitable for large scale computation and it again relies on third-party packages. For scientific computation we commonly use numpy and scipy packages which use C++ in backend. In other words, when we use routines in the numpy package, routines written in C++ are called for faster computation. Hence, numpy internally uses the same size of integers as C++. Integers prepared in the python core may not be compatible with integers used in numpy. If so, numpy attempts to convert the python integer to a numpy integer. If python integer is too large for numpy, then overflow error occurs. Therefore, we need to know the range of integers we can use with numpy and scipy.

Consider unsigned integer type uint8 stored in an 8-bit string. A binary string of 8 bits can express 256 (=\(2^8\)) integers, ranging from 0 to 255. Signed integer int8 uses one bit for sign, 0 for \(+\) and 1 for \(-\), and the remaining 7 bits for absolute value. Hence, a half of 256 is negative and the other half is positive. The resulting range is from -128 to +127. Notice that the upper limit is 127 instead of 128 because 0 is a part of \(+\) number in this encoding scheme.

Table Table 1.2 shows the range of other signed integer types. The default size of signed integer is 32 bit in most computer languages. However, 64-bit integer can be used for large scale calculation. Common hardware cannot handle integers larger than 64 bit. If more than 64 bit is needed, you must use a special numerical library.

Table 1.2 The range of signed integers#

dtype

bits

min

max

int8

8

-128

+127

int16

16

-32768

+32767

int32

32

-2147483648

+2147483647

int64

64

-9223372036854775808

+9223372036854775807

You can ask numpy what is the range of integer using iinfo.

import numpy as np

print('max of int64 =',np.iinfo('int64').max)
print('min of int64 =',np.iinfo('int64').min)
max of int64 = 9223372036854775807
min of int64 = -9223372036854775808

In the following example code, x and y are the same integer 123. However, their type is different. x is python integer and y is numpy integer int32.

import numpy as np

# using integer type in python
x = 123
print('x =',x,' type=',type(x))

# using numpy integer
y = np.int32(123)
print('y =',y,' type=',type(y))
x = 123  type= <class 'int'>
y = 123  type= <class 'numpy.int32'>

Example 1.3.1: Huge integer: Exponential

Python can handle even \(2 \times 10^{100}\). However, it does not work in numpy as integer. The error message indicates that Overflow error occurred.

x=2 * 10**100
print(x)
y=np.int64(x)
print(y)
20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
---------------------------------------------------------------------------
OverflowError                             Traceback (most recent call last)
Cell In[4], line 3
      1 x=2 * 10**100
      2 print(x)
----> 3 y=np.int64(x)
      4 print(y)

OverflowError: Python int too large to convert to C long

Example 1.3.2 Huge integer: Factorial

Factorial \(N!\) grows quickly as \(N\) increases. The python core can’t calculate factorial. math package understands python integer and thus can handle huge integers. Function math.factorial() returns integer in python integer type. Let us try 1000! Can you imagine how big it is? Numpy can’t deal with it.

import math

x=math.factorial(1000)

print(type(x),'  This is NOT int64')

print('1000! =',x)
<class 'int'>   This is NOT int64
1000! = 402387260077093773543702433923003985719374864210714632543799910429938512398629020592044208486969404800479988610197196058631666872994808558901323829669944590997424504087073759918823627727188732519779505950995276120874975462497043601418278094646496291056393887437886487337119181045825783647849977012476632889835955735432513185323958463075557409114262417474349347553428646576611667797396668820291207379143853719588249808126867838374559731746136085379534524221586593201928090878297308431392844403281231558611036976801357304216168747609675871348312025478589320767169132448426236131412508780208000261683151027341827977704784635868170164365024153691398281264810213092761244896359928705114964975419909342221566832572080821333186116811553615836546984046708975602900950537616475847728421889679646244945160765353408198901385442487984959953319101723355556602139450399736280750137837615307127761926849034352625200015888535147331611702103968175921510907788019393178114194545257223865541461062892187960223838971476088506276862967146674697562911234082439208160153780889893964518263243671616762179168909779911903754031274622289988005195444414282012187361745992642956581746628302955570299024324153181617210465832036786906117260158783520751516284225540265170483304226143974286933061690897968482590125458327168226458066526769958652682272807075781391858178889652208164348344825993266043367660176999612831860788386150279465955131156552036093988180612138558600301435694527224206344631797460594682573103790084024432438465657245014402821885252470935190620929023136493273497565513958720559654228749774011413346962715422845862377387538230483865688976461927383814900140767310446640259899490222221765904339901886018566526485061799702356193897017860040811889729918311021171229845901641921068884387121855646124960798722908519296819372388642614839657382291123125024186649353143970137428531926649875337218940694281434118520158014123344828015051399694290153483077644569099073152433278288269864602789864321139083506217095002597389863554277196742822248757586765752344220207573630569498825087968928162753848863396909959826280956121450994871701244516461260379029309120889086942028510640182154399457156805941872748998094254742173582401063677404595741785160829230135358081840096996372524230560855903700624271243416909004153690105933983835777939410970027753472000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

scipy has factorial function. It says 1000! = inf, meaning it is infinity. It returns the result in type float64 instead of integer type, which we discuss in next section.

import scipy.special as ss

x=ss.factorial(1000)
print(type(x))
print('1000! =',x)
<class 'numpy.float64'>
1000! = inf

1.2.2. Arithmetic operations between integers#

Python core provides basic arithmetic operations between integers. The result is python integer except for division whose result is python float (float is discussed in the following section.)

  • Addition (+): Adds two operands.

  • Subtraction (-): Subtracts the second operand from the first.

  • Multiplication (*): Multiplies two operands.

  • Division (/): Divides the first operand by the second, always returning a float result.

  • Floor Division (//): Divides the first operand by the second and returns the integer part of the quotient (rounds down to the nearest whole number).

  • Modulo (%): Returns the remainder of the division of the first operand by the second.

  • Exponentiation (**): Raises the first operand to the power of the second operand.

Python adheres to the standard order of operations.

  1. parentheses are evaluated first

  2. followed by exponents,

  3. then multiplication and division (from left to right),

  4. finally addition and subtraction (from left to right).

Example 1.3.3 Floor and Modulo

print(' floor of 10/3 =',10//3)
print('modulo of 10/3 =',10%3)
 floor of 10/3 = 3
modulo of 10/3 = 1

Last modified on 08/04/2025 by R. Kawai.