As discussed earlier Data types in Python are a way or categorizing or classifying data into different types. Data types tell us what kind of operations can be performed on a particular data There are seven different data types in Python, namely Numbers
, Strings
, Lists
, Tuples
, Sets
, Booleans
and Dictionaries
. In this article we will be looking at Data type Numbers.
Numbers in Python are immutable
(cannot be altered/changed after creation) and can be classified into three types:
Integers:
Integers are a special set of whole numbers. They can be either positive, negative or zero. Integers do not have a fractional component.
If you store an integer number in a variable, that variable is called an integer variable. Let's print some integer values.
Casting:
As we assign an integer number to a variable Python dynamically allocates the data type i.e. the variable is automatically declared as an integer variable, the moment you assign an integer value to it. But if you want to specify the type of a variable you can do that by casting.
For casting you can use the int()
function. The int()
function is a built-in function in Python. When you pass any number or a string (which resembles a number) as a parameter to the int()
function It returns an integer i.e. it converts the given number or string to an integer.
The syntax of int() function is:
int(x=0, base=10)
The int() function takes two parameters:
- x - Number or string to be converted to integer. The default value is zero.
- base - Base of the number x.
The int() function returns:
- An integer converted from the given number or string, default base is 10
- If No parameters are given, returns 0
- If base given, treats the string in the given base (0, 2, 8, 10, 16)
Further you can also check the type of the variable using the type()
or isinstance()
function. The type()
function tells us the class type of a given object(here variable) and the isinstance()
function tells us if a given object belongs to a particular class type. The isinstance()
function takes two parameters the object and the class type and it returns True if the object belongs to the specified class type.>
The parameters for a function are passed in between the round brackets iint(parameter)
. We will be discussing functions in detail later.
As we can see above the variable belongs to integer class i.e. the given variable is an integer variable.
int()
function can also be used for different number systems such as binary, hexadecimal or octal.
Different ways of writing integers:
The conventional base-10 method
The conventional method of writing integers is using the base-10 decimal system, the way we have discussed above. Integers are just decimal numbers with the fractional part as zero.
Using underscores
Integers in Python can also be written using underscores in between i.e. the individual digits can be seperated by an underscore>
Using different number systems
You can also write integers in Python using different number systems such as binary (base-2), octal (base-8) or hexadecimal(base-16).
Signed and unsigned integers in Python
Like C there are no signed and unsigned integer data types in Python. The int
in Python acts like a signed integer. The signed integer can take both positive and negative values, whereas an unsigned integer can take only positive values.
The positive integers are stored in the memory in their binary form and the negative integers are stored in the form of 2's complement
Let's see how to convert a signed integer to an unsigned integer in Python:
To convert a signed integer to an unsigned integer we need to take the 2's complement of the number
Let's consider a signed integer number: -100
The binary representation of 100, ignoring the minus sign would be: 00000000000000000000000001100100
(32-bit representation)
Now Let's take 2's complement of the number: 11111111111111111111111110011100
Now if you take 2's complement of the above number, you get back your original number 100
and the left most leading number, 1
which is a signed bit, indicates that the number is negative
Converting the 2's complement from binary to decimal you would get: 4294967196
(signed int to unsigned int conversion)
**
is the exponential operator in Python. It is same as multiplying x by x, y times x^y
The Bitwise left shift <<
operator performs bit manipulation by shifting the left operand bits of the number to the left, by the number of bits specified. It fills 0 on voids left as a result.
x << y Left shifts the integer ‘x’ with ‘y’ integer by y places. It is the same as multiplying x with 2 raised to the power of y 2**y
.
Floats:
Floats are floating-point numbers in Python and they represent real numbers. These are decimal numbers where the integer and fractional part is seperated by a decimal point. They can be either positive, negative or zero.
If you store a floating-point number in a variable that variable is called a float variable. Let's print some float values.
Casting:
As we assign a floating-point number to a variable Python dynamically allocates the data type i.e. the variable is automatically declared as a float variable the moment you assign a floating-point value to it. But if you want to specify the type of a variable you can do that by casting
For casting you can use the float()
function. The float()
function is a built-in function in Python. When you pass any number or a string (which resembles a number) as a parameter to the float()
function it returns an float value i.e. it converts the given number or string to a floating-point number.
The syntax of float() function is:
float(x=0.0)
The float() function takes one parameters:
- x - Number or string to be converted to float. The default value is zero.
The float() function returns:
- A floating-point number converted from the given number or string
- If No parameters are given, returns 0.0
As we can see above the variable belongs to float class i.e. the given variable is a float variable.
Different ways of writing floats:
All the different methods mentioned above, for writing integers are applicable to floating-point numbers, however the integer and the fractional part must be seperated by a decimal point for the number to be a floating-point number.
Using decimals and underscores
Using "e" notation
Floating-point numbers in Python can also be written using e
notation, which stands for exponentiation.
To write a floating-point number with e
notation, type a number followed by the letter e
and then another number. Python takes the number to the left of the e
and multiplies it by 10 raised to the power of the number after the e
. So 1e8 is equivalent to 1×10^8.
Python also uses e
notation to display large floating-point numbers
Float values in Python are represented as 64-bit double-precision values. The maximum value any floating-point number can be is approx 1.8 x 10^308. Any number greater than this will be indicated by inf
in Python, which stands for infinity.
The float number 200000000000000000.0
gets displayed as 4e+20
. The +
sign indicates that the exponent 20 is a positive number.
Negative numbers can also be used as exponents. 1e-4
can be interpreted as 10 raised to the power -4, which is 0.0001
The float number 0.00000000000000000005
gets displayed as 5e-20
. The -
sign indicates that the exponent 20 is a negative number.
Float precision:
Floating-point numbers in Python can be tricky. Let's take an example, suppose we have to add two decimals, 2.2
and 4.4
the result should be 6.6
, right?
We can use the boolean
data type in Python to validate the expression. If the given expression is correct True
is displayed or else False
is displayed.
So what went wrong?
Floating-point numbers are represented in computer hardware as base-2 (binary) fractions. For example, the decimal fraction 0.125
has value 1/10 + 2/100 + 5/1000
.
In the same way the binary fraction 0.001
has value 0/2 + 0/4 + 1/8
.
These two fractions have identical values, the only real difference being that the first is written in base-10 fractional notation, and the second in base-2.
Unfortunately, most decimal fractions cannot be accurately stored in our computer as binary fractions. A consequence is that, most of the decimal floating-point numbers you enter are approximated by the binary floating-point numbers actually stored in the computer.
Let's take an example. Consider the fraction 1/3
. This will give 0.33333333333...
which is an infinitely long decimal and you can only approximate it as a base-10 decimal number 0.3
or 0.33
or 0.333
or so on. No matter how many digits you’re willing to write down, the result will never be exactly 1/3
, but will be an increasingly better approximation of 1/3
.
In the same way, no matter how many base-2 digits you’re willing to use, the decimal value 0.1
cannot be represented exactly as a base-2 fraction. In base-2, 1/10
is an infinitely repeating fraction 0.0001100110011001100110011....
and our computer only stores a finite number of it. Stop at any finite number of bits, and you get an approximation but it will never be equal to the actual value of 1/10
.
Floats are mostly approximated using a binary fraction with the numerator using the first 53 bits
starting with the most significant bit and with the denominator as a power of two. In the case of 1/10
, the binary fraction is 3602879701896397 / 2 ** 55
which is close to but not exactly equal to the true value of 1/10
. The numbers 0.1
and 0.10000000000000001
and 0.1000000000000000055511151231257827021181583404541015625
are all approximated by 3602879701896397 / 2 ** 55
. Since all of these decimal values share the same approximation, any one of them could be displayed
Hence, this is not an error in Python, this is the very nature of binary floating-point. Python only prints a decimal approximation to the true decimal value of the binary approximation stored by the machine.
If Python were to print the true decimal value of the binary approximation stored for 0.1
, it would have to display 0.1000000000000000055511151231257827021181583404541015625
, as shown above. That is more digits than most people find useful, so Python keeps the number of digits manageable by displaying a rounded value instead 0.1
.
Just remember, Even though the printed result looks like the exact value of 1/10
, the actual stored value is the nearest representable binary fraction.
To limit the fractional numbers after the decimal point you can use the round
function in Python. The round
function has it's own limitations which we will discuss when we will be looking at built-in functions in Python
Python Decimals:
To overcome the precision problem with floats, we can use the decimal
module in Python. While floating-point numbers have precision up to 15 decimal places, the precision value for decimal
module is variable and can be set by the user.
To use a module we need to import the module first. We will be discussing modules in detail later.
The precision can be set by the user using the .prec
property of the .getcontext()
function from the decimal module.
The trailing zeros are always preserved in decimal numbers to indicate significance.
The decimal
module is used when we want to carry out decimal calculations just like schoolbooks.
Decimal
is preferred in accounting applications where we need exact decimal representation.
When we want to control the level of precision required and we want to preserve the significant decimal places, we tend to use the decimal
module.
However floating-point numbers are mostly preferred over decimal numbers due to their efficiency. As floating-point calculations are much faster than decimal calculations
Python Fractions:
Python has a built-in fractions
module which lets us perform operations involving fractional numbers
A fraction has a numerator and a denominator, both of which are integers.
The fractions
module supports different arithmetic operations on rational numbers.
Let's have a look at some fractions.
While creating fractions
from floats, we might get some unusual results. This is due to the imperfect binary floating point number representation as discussed above.
Fractions
module also allows us to create fractions from strings. This is the preferred option when using decimal numbers as strings give a better representation of fractional numbers.
To print the numerator and denominator seperately the fractions module has two properties .numerator
and .denominator
. It also has a .limit_denominator()
method which helps to perform rational approximation for a given floating point number.
Complex numbers:
Python provides built-in support for complex numbers
Complex numbers have two distinct components a real
part and an imaginary
part
Complex numbers are mostly used for scientific computing
To create a complex number in Python, you simply write the real part, then a plus sign, then the imaginary part with the letter j at the end
We can seperate the real and imaginary part of a complex number by using two properties of the complex numbers class .real
and .imag
Note that the real and imaginary values returned are floats, even though we specified integers
Complex numbers have a conjugate method that returns complex conjugate of the given complex number
For any complex number, its conjugate is the complex number with the same real and imaginary part but with the opposite sign. So in this case, the complex conjugate of 1 + 2j
is 1 - 2j
.
Interestingly, int and float also have .real
and .imag
properties as well as the .conjugate()
method.
For floats and integers, .real
and .conjugate()
always return the number itself, and .imag
always returns 0. One thing to notice, is that var.real
and var.imag
return an integer if var is an integer and a float if var is a float.
If you are learning Python for data science, web development, machine learning or general programming you may never need to use complex number.
Complex numbers are mostly used in the domains related to scientific computing and computer graphics.
Type conversion:
You can convert from one type to another with the int()
, float()
, and complex()
functions in Python.
Number systems:
There are four different number systems in python. These number systems represent the numbers in computer architecture
- Binary number system (base-2) [only possible digits: 0s and 1s]
- Octal (base-8) [possible digits: 0 to 7]
- Hexadecimal (base-16) [numbers can be represented using digits from 0 to 9 and letters a to f]
- Decimal (base-10) [possible digits: 0 to 9]
We have already discussed how to write different number systems in python here.
Number systems conversion:
Let's have a look at how to convert from one number system to another in python.
- To convert from decimal to binary we can use the
bin()
function. - To convert from decimal to octal we can use the
oct()
function. - To convert from decimal to hexadecimal we can use the
hex()
function.
In python, Binary
numbers have 0b
as the prefix whereas, Octal
and Hexadecimal
numbers have 0o
and 0x
as the prefix respectively.
As you can see above there is no built-in function to convert from float to binary, octal or hexadecimal number, only integer numbers can be converted. If you try to convert a floating-point number you will get a Type error.
Float to binary conversion:
Since there is no built-in function in python to convert from float to binary we can write a simple code of ours which contains two user-defined functions. The float_to_bin()
function converts a floating-point number to binary and the int_to_bin()
function converts an integer number to binary.
Note that here we are not using the built-in function bin()
to convert from integer to binary, instead we have written our own function int_to_bin()
.
To convert a floating-point number to binary we need to split the integer and fractional parts, then convert each of them to binary seperately, and finally join the two binary parts, seperated by a decimal point, to get the desired binary representation
If the floating-point number is negative, the sign is preserved in the final converted output.
Similary, you can write your own code to convert from float to octal or hexadecimal.
We will be discussing loops, functions and mathematical operations in python in further articles.
Size in memory:
Let's see how much memory do these different numeric types require and which one of these can be efficiently used when you have to store large numbers with a limited memory.
We can use the sys
module for this purpose. sys
is a built-in module in Python and has a method .getsizeof()
which gives us the size of any an object in the memory.
As we can see above the size of integer 0 is 24 bytes
that means Python uses 24 bytes
to store the number 0, but number zero requires only 1 bit
. So we can say that 24 bytes
are an overhead to store an integer object. We can see that above, since the size an empty integer object int()
is also 24 bytes
. The next 4 bytes
are used to represent 5 in the memory, since the size of 5 is 28 bytes
.
In Python, value of an integer is not restricted by the number of bits and can expand to the limit of the available memory. Thus we never need any special arrangement for storing large numbers.
The size of an empty Float object float()
is 24 bytes
. Float uses 24 bytes
to store 5.2 at the same time decimal uses 104 bytes
to store the same number 5.2.
To store 2**64 int uses 36 bytes
whereas float uses 24 bytes
. So we can clearly see that using float to store large decimal numbers is much efficient.
Conclusion
In this article, we looked at Numbers Data type in Python. We looked at different numeric types and understood every numeric type in detail. We also discussed how much memory does each numeric type occupy. We also looked at how to convert from one numeric type to another using built-in functions in Python. We also looked at the efficiency of each numeric type and their representation in the memory.