0

I am trying to implement a reversible physics engine so I have decided to use the decimal module. So this obviously works.

>>> from decimal import *
>>> a = Decimal('1')
>>> b = Decimal('0.82')
>>> a = a*b/b
>>> print(a)
1

However, when this operation is repeated, i.e. "multiply 100 times and then divide 100 times", the result does not precisely equal to a again.

>>> for _ in range(100):
...     a = a*b
...
>>> for _ in range(100):
...     a = a/b
...
>>> a
Decimal('0.9999999999999999999999999965')

Am I doing something wrong? Is it possible to do these calculations reversably, so that I get the initial result?

mlg556
  • 419
  • 3
  • 13
  • Does this answer your question? [Is floating point math broken?](https://stackoverflow.com/questions/588004/is-floating-point-math-broken) – Tom Burrows Apr 28 '20 at 21:54
  • 5
    Decimal doesn't have infinite precision. You can [increase its precision](https://docs.python.org/3/library/decimal.html#quick-start-tutorial) if you're finding it too inaccurate. – khelwood Apr 28 '20 at 21:55
  • 1
    You're not doing anything wrong, that's just how floating point maths works. Computers can never work with decimals perfectly. – Tom Burrows Apr 28 '20 at 21:56
  • I understand, I thought using arbitrary precision would magically solve the problem! – mlg556 Apr 28 '20 at 21:57
  • 1
    The [`mpmath`](http://mpmath.org/doc/current/) module might do what you want. You can get it from [pypi](https://pypi.org/project/mpmath/). – martineau Apr 28 '20 at 21:58
  • 1
    Reversible physics simulation is a lot harder than "just throw more precision at it". There are problems involved that additional precision cannot solve. – user2357112 Apr 28 '20 at 22:00
  • @juanpa.arrivillaga: `a/b` results in floating point number in Python 3. – martineau Apr 28 '20 at 22:00
  • The link in the first problem addresses what is happening here. For example 1/3 cannot be represented exactly in decimal, no matter how much precision you have. Of course, since physics calculations can result in irrational numbers you will need to re-think how you want to model this "reversibility". Some irrational numbers, like square and other roots, can represented exactly by suitably extending the rationals, but that gets very hard very quickly. – President James K. Polk Apr 29 '20 at 13:18

3 Answers3

2

Decimal doesn't have infinite precision. You can increase its precision if you're finding it too inaccurate.

from decimal import *
getcontext().prec = some larger number
khelwood
  • 55,782
  • 14
  • 81
  • 108
2

You can use the fractions module instead:

>>> from fractions import Fraction
>>> a = Fraction(1)
>>> b = Fraction(82, 100)
>>> for _ in range(100):
...     a *= b
... 
>>> a
Fraction(189839102486063226543090986563273122284619337618944664609359292215966165735102377674211649585188827411673346619890309129617784863285653302296666895356073140724001, 78886090522101180541172856528278622967320643510902300477027893066406250000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000)
>>> for _ in range(100):
...     a /= b
... 
>>> a
Fraction(1, 1)
Francisco
  • 10,918
  • 6
  • 34
  • 45
1

You can also take advantage of Python's implementation of infinitely large integers by making the desired decimal numbers integers instead by multiplying them all by a sufficiently large factor, and divide the result by the same factor afterwards:

a = 100 # 1 times a factor of 100
b = 82 # 0.82 times a factor of 100
for _ in range(100):
    a = a*b
for _ in range(100):
    a = a//b
print(a)

This outputs:

100 # divide this by a factor of 100 to get back 1
blhsing
  • 91,368
  • 6
  • 71
  • 106