Inconsistent math in multiplication or Python's multiplication implementation is at fault?

279 Views Asked by At

Is it wrong to say every number in mathematics is simply a fractional number, and any operation performed on them would always return the same output ?

If not, then please explain why the following Python snippet doesn't answer exactly the same

me@earth ~ % python3
>>>
>>> 43 * 23
989
>>>
>>> 4.3e+1 * 2.3e+1
989.0
>>>
>>> 4.3 * 2.3 * 1e+2
988.9999999999999
>>>

For non coding background people, let me put the above snippet like the following

$43 * 23 = 989$

$(4.3 * 10^1) * (2.3 * 10^1) = 989.0$

$(4.3 * 2.3) * 10^2 = 988.9999999999999$

You can reason as much as you want that these numbers are almost equal when we round up, but why are these not equal ?

Does it has something to do with how Python is calculating the answer or is this answer right ?

I have verified from some online calculators as well, the answers from online calculators are giving consistent answers, but I can't wrap my head around why the Python is giving this answer

Is math wrong, or Python wrong ?

Obviously, math can't be wrong, but why this behavior ?

1

There are 1 best solutions below

4
On BEST ANSWER

This is because the numbers 4.3e1=43.0 and 2.3e1=23.0 are represented exactly in standard floating-point arithmetic, while 4.3 and 2.3 are not. You can see this if you print the exact underlying numbers using the following little trick:

>>> print("%.310g" % 43.0)
43
>>> print("%.310g" % 23.0)
23
>>> print("%.310g" % 4.3)
4.29999999999999982236431605997495353221893310546875
>>> print("%.310g" % 2.3)
2.29999999999999982236431605997495353221893310546875

So when you ask the computer to calculate 4.3 * 2.3, it really does 4.29999999999999982236431605997495353221893310546875 * 2.29999999999999982236431605997495353221893310546875, which equals 9.8899999999999988276044859958347248670811673365659664691426113114491869282574043609201908111572265625 exactly, but this result gets rounded to the nearest floating-point number, which happens to be

>>> print("%.310g" % (4.3 * 2.3))
9.8899999999999987920773492078296840190887451171875

(the next floating-point number after that one is 9.8900000000000005684341886080801486968994140625 exactly, but that's further away) and when this is multiplied by 100 you get

>>> print("%.310g" % (4.3 * 2.3 * 1e2))
988.9999999999998863131622783839702606201171875

instead of 989.0.