How to prevent the "overflow" of multiply several small numbers? - python-3.x

I have a list with x very small numbers and want to create the product of them. I only want to use pure Python or/and numpy.
# List A with x very small numbers
A =[1.20223398e-072 1.53678559e-067 6.04813112e-041 3.26046833e-104
3.09114525e-048 7.65394632e-118 4.58886892e-209 7.02220200e-044
3.40963578e-085 2.79721084e-060 6.99320974e-052 7.65701921e-039
3.05321642e-103 2.33360119e-050 2.92905105e-044 5.13970623e-044
6.46863409e-180 1.78254565e-177 6.26061488e-068 5.86281346e-043]
#creating the product of all elements in A
np.prod(A)
Output:
0.0
And this is a problem, maybe an overflow?!
What have I tried?
I tried to do this in a loop => very bad
running time and did not work
Input:
prod = 1
for i in A):
prod = prod * A
Output:
0.0
I tried to sort by amount and then multiply from the array always the first and last, then second and penultimate, third and third last, ....
=> Did not work ether
Input:
prod = 1
A.sort()
for i in range(len(A)):
prod = prod * (A[i]*A[-i-1])
Output:
0.0
Do any one has an idea how to solve this problem?
Best regards
Christian

The underlying floating point storage is not large enough to hold so small numbers, especially their product, which grows smaller the more you multiply.
>>> A[0]*A[1]
1.8475758562723483e-139
>>> A[0]*A[1]*A[2]
1.1174381032881437e-179
>>> A[0]*A[1]*A[2]*A[3]
3.6433715465062613e-283
>>> A[0]*A[1]*A[2]*A[3]*A[4]
0.0
However, with these specific numbers, the product actually fits in a np.float128 type:
>>> np.prod(A, dtype=np.float128)
6.439950307032109978e-1637
Of course, this just moves the goal post: Multiplying other numbers could again give you a zero.
An alternative is to use the decimal module in Python, which will give you the most flexibility in dealing with exact numbers. It's much slower than IEEE floats, though, but should work:
>>> import decimal
>>> B = [decimal.Decimal(x) for x in A]
>>> B[0]*B[1]
Decimal('1.847575856272348173712320037E-139')
>>> B[0]*B[1]*B[2]
Decimal('1.117438103288143607857271302E-179')
>>> B[0]*B[1]*B[2]*B[3]
Decimal('3.643371546506261078793026112E-283')
>>> B[0]*B[1]*B[2]*B[3]*B[4]
Decimal('1.126219064996798351280705310E-330')

Related

Round a decimal number in python

How can I round this number 838062.5 to 838063 instead of to 838062, using the round() function in Python?
Use math.ceil to round up:
import math
print(math.ceil(838062.5)) # prints 838063
The Python documentation for the round() function explains this interesting phenomenon:
If two multiples are equally close, rounding is done toward the even choice (so, for example, both round(0.5) and round(-0.5) are 0, and round(1.5) is 2).
While you could use floor and ceiling operations, matching the various cases may be a pain. So, I recommend using the decimal module, which allows you to always round half-values up.
>>> import decimal
>>> decimal.getcontext().rounding = decimal.ROUND_HALF_UP
>>> n = decimal.Decimal(838062.5)
>>> n
Decimal('838062.5')
>>> round(n)
838062
All I had to do was add 0.5 and take the floor value:
import math
x = 838062.5 + 0.5
print(math.floor(x))

Why does this n choose r python code not work?

These 2 variations of n choose r code got different answer although followed the correct definition
I saw that this code works,
import math
def nCr(n,r):
f = math.factorial
return f(n) // f(r) // f(n-r)
But mine did not:
import math
def nCr(n,r):
f = math.factorial
return int(f(n) / (f(r) * f(n-r)))
Use test case nCr(80,20) will show the difference in result. Please advise why are they different in Python 3, thank you!
No error message. The right answer should be 3535316142212174320, but mine got 3535316142212174336.
That's because int(a / b) isn't the same as a // b.
int(a / b) evaluates a / b first, which is floating-point division. And floating-point numbers are prone to inaccuracies, roundoff errors and the like, as .1 + .2 == 0.30000000000000004. So, at some point, your code attempts to divide really big numbers, which causes roundoff errors since floating-point numbers are of fixed size, and thus cannot be infinitely precise.
a // b is integer division, which is a different thing. Python's integers can be arbitrarily huge, and their division doesn't cause roundoff errors, so you get the correct result.
Speaking about floating-point numbers being of fixed size. Take a look at this:
>>> import math
>>> f = math.factorial
>>> f(20) * f(80-20)
20244146256600469630315959326642192021057078172611285900283370710785170642770591744000000000000000000
>>> f(80) / _
3.5353161422121743e+18
The number 3.5353161422121743e+18 is represented exactly as shown here: there is no information about the digits after the last 3 in 53...43 because there's nowhere to store it. But int(3.5353161422121743e+18) must put something there! Yet it doesn't have enough information. So it puts whatever it wants to so that float(int(3.5353161422121743e+18)) == 3.5353161422121743e+18.

How to calculate and store the number in scientific notation?

I am using Python to model the statistical physical, so I will deal with small numbers.
For example,
a = 2.22e-300, b = 3e-200
and I want to calculate
a * b = 6.66e-500.
However, in Python 3 it shows 0.0.
I am thinking to design a data type: the first part to store the float number, which is 6.66 here, and the second part stores the magnitude, which is -500.
May I ask how I can implement this? Or is there any better way to deal with the scientific number?
Create a class:
class Sci_note:
def __init__(self, base, exp):
self.base = base
self.exp = exp
def __mul__(self, other):
return Sci_note(self.base * other.base,
self.exp + other.exp)
def __str__(self):
return str(self.base) + 'e' + str(self.exp)
and it functions as you would expect:
>>> a = Sci_note(2.22, -300)
>>> b = Sci_note(3, -200)
>>> c = a * b
>>> c.base
6.66
>>> c.exp
-500
update
I added a __str__ method (above), so they are displayed properly when printed:
>>> print(a)
2.22e-300
Of course, I have only implemented the multiplication method here, but I will leave it up to you to implement the others when required. It may be the case that you only need multiplication so I would be wasting everyone's time if I wrote them now!
In addition, creating a __float__ handler would also not be useful here, as Python can't handle floats of the order ^-300, so it would be useless to return them as we would just get 0!
I strongly suggest you use something like the built-in decimal module and increase its precision to your needs. For example:
>>> from decimal import *
>>> getcontext().prec = 100
>>> a = Decimal("2.22e-300")
>>> b = Decimal("3e-200")
>>> a
Decimal('2.22E-300')
>>> b
Decimal('3E-200')
>>> a*b
Decimal('6.66E-500')
Note that, to be on the safe side, I create a and b using strings such as "3e-200" to let the decimal module parse them correctly. If not, it will first convert them to Python's inexact floating points and muck them up before passing them into Decimal objects.
In the above code, we set the precision to 100.

Distinguishing large integers from near integers in python

I want to avoid my code mistaking a near integer for an integer. For example, 58106601358565889 has as its square root 241,053,109.00000001659385359763188, but when I used the following boolean test, 58106601358565889 fooled me into thinking it was a perfect square:
a = 58106601358565889
b = math.sqrt(a)
print(b == int(b))
The precision isn't necessarily the problem, because if I re-check, I get the proper (False) conclusion:
print(a == b**2)
What would be a better way to test for a true versus a near integer? The math.sqrt is buried in another definition in my code, and I would like to avoid having to insert a check of a squared square root, if possible. I apologize if this is not a good question; I'm new to python.
import numpy as np
import math
from decimal import *
a = 58106601358565889
b = np.sqrt(a)
c = math.sqrt(a)
d = Decimal(58106601358565889).sqrt()
print(d)
print(int(d))
print(c)
print(int(c))
print(b)
print(int(b))
o/p
241053109.0000000165938535976
241053109
241053109.0
241053109
241053109.0
241053109
I would say use decimal.
Expected code :
from decimal import *
d = Decimal(58106601358565889).sqrt()
print(d == int(d))
o/p
False
This isn't a matter of distinguishing integers from non-integers, because b really is an integer*. The precision of a Python float isn't enough to represent the square root of a to enough digits to get any of its fractional component. The second check you did:
print(a == b**2)
only prints False because while b is an integer, b**2 still isn't a.
If you want to test whether very large integers are exact squares, consider implementing a square root algorithm yourself.
*as in 0 fractional part, not as in isinstance(b, int).
It's not the precision of the int that is the problem - it's the limited precision of floats
>>> import math
>>> math.sqrt(58106601358565889)
241053109.0
>>> math.sqrt(58106601358565889) - 241053109
0.0
I think the double check would be the obvious solution
You could also look at the gmpy2 library. It has a function for calculating the integer square root and also the integer square root plus remainder. There are no precision constraints.
>>> import gmpy2
>>> gmpy2.isqrt(58106601358565889)
mpz(241053109)
>>> gmpy2.isqrt_rem(58106601358565889)
(mpz(241053109), mpz(8))
>>>
Disclaimer: I maintain gmpy2.

Python 3.3.2 - Calculating the Carrying of Numbers

Remember back in primary school where you learn to carry numbers?
Example:
123
+ 127
-------
250
You carry the 1 from 3+7 over to the next column, and change the first column to 0?
Anyway, what I am getting at is that I want to make a program that calculates how many carries that the 2 numbers make (addition).
The way I am doing it, is that I am converting both numbers to strings, splitting them into individuals, and turning them back into integers. After that, I am going to run through adding 1 at a time, and when a number is 2 digits long, I will take 10 off it and move to the next column, calculating as I go.
The problem is, I barely know how to do that, and it also sounds pretty slow.
Here is my code so far.
numberOne = input('Number: ')
numberTwo = input('Number: ')
listOne = [int(i) for i in str(numberOne)]
listTwo = [int(i) for i in str(numberTwo)]
And then... I am at a loss for what to do. Could anyone please help?
EDIT:
Some clarification.
This should work with floats as well.
This only counts the amount of times it has carried, not the amount of carries. 9+9+9 will be 1, and 9+9 will also be 1.
The numbers are not the same length.
>>> def countCarries(n1, n2):
... n1, n2 = str(n1), str(n2) # turn the numbers into strings
... carry, answer = 0, 0 # we have no carry terms so far, and we haven't carried anything yet
... for one,two in itertools.zip_longest(n1[::-1], n2[::-1], fillvalue='0'): # consider the corresponding digits in reverse order
... carry = int(((int(one)+int(two)+carry)//10)>0) # calculate whether we will carry again
... answer += ((int(one)+int(two)+carry)//10)>0 # increment the number of carry terms, if we will carry again
... carry += ((int(one)+int(two)+carry)//10)>0 # compute the new carry term
... return answer
...
>>> countCarries(127, 123)
1
>>> countCarries(127, 173)
2

Resources