[Tutor] How to correct decimal addition.

Oscar Benjamin oscar.j.benjamin at gmail.com
Sat Jan 25 21:38:21 CET 2014


On 25 January 2014 08:57, spir <denis.spir at gmail.com> wrote:
> On 01/25/2014 09:46 AM, spir wrote:
>>
>> On 01/24/2014 06:57 PM, Leon S wrote:
>>>
>>> Here is what I'm trying to do, accept a price of gas, but I want to add
>>> the
>>> .009 to the price, so that people do not have to type the full amount.
>>>   Example, 3.49 /gallon would return 3.499 /gallon.
>>>
>>> This is what I have tried and the results of it.
>>>
>>> def gas_price(price):
>>>     price == raw_input("What is the price of gas?")  return price + .09
>>>    3.49=> 3.4899999999999998
>>>
>>> It reduces the number and then adds many decimal points after.
>>>
>>> Thanks for any help, I am sure this is an easy one for someone.
>>
>> This is instead easy for noone ;-)
>>
>> The core issue is that for, say, "fractional numbers" (numbers with a
>> fractional
>> part, but unlike real numbers with definite precision) python like most
>> programming languages uses in standard a binary representation internally.
>> While
>> we normally use decimal notation, both in input (reading) and output
>> (writing).
>> And there is no way to represent decimal fractions in binary, except in
>> the very
>> case where they are a multiple of an exact (negative) power of 2 (for
>> instance
>> 1/2, 3/4, 123/128... are ok).
>>
>> The only right solution is to use decimals internally, and python provides
>> such
>> a numeric type, Decimal:
>> http://docs.python.org/3/library/decimal.html
>
> I did not read correctly. If you're dealing with financial as it seems, the
> right way is to use integers instead.

I would say that using Decimal is the "right" solution for financial
calculations. Using integers (AKA fixed-point arithmetic) is possible
and is the standard solution for languages that lack a Decimal type.

> Since you are adding tenth's of pence,
> this is what your unit means. Then your sum is:
>         3490 + 9
> :-)
>
> Note: AFAIK most financial software use integers for this reason and to
> avoid (or control) rounding errors.

The Decimal module allows you to avoid or control rounding errors. If
it is true that most financial software uses fixed-point arithmetic
instead of floating point decimal arithmetic then I would guess that
the most likely reason is that the language used for the software
doesn't have a library for floating point decimal arithmetic as Python
does.

A significant motivating use-case for adding the Decimal module and
for subsequently speeding it up with a C implementation was to support
using Python in the context of financial software.

> At input and/or output, you may still have to convert to Decimal to get
> "decimally correct" value or expression.
>
>         numeral = input("...")
>         value = Decimal(numeral)
>         ...
>         output = Decimal(result) / Decimal("1000")
>         print(output)
>
> (not sure, not tried)

I would do it like so:

>>> from decimal import Decimal
>>> def adjust_price(price):
...     return Decimal(price) + Decimal('.005')
...
>>> adjust_price('3.49')
Decimal('3.495')
>>> print(adjust_price('3.49'))
3.495

But then what happens if the use has added the .005 themselves:

>>> print(adjust_price('3.495'))
3.500

Perhaps we should round down before adding .005:

>>> from decimal import ROUND_DOWN
>>> def adjust_price(price):
...     # Round down to 2 decimal places
...     price = Decimal(price).quantize(Decimal('1.00'), rounding=ROUND_DOWN)
...     return price + Decimal('.005')
...
>>> print(adjust_price('3.49'))
3.495
>>> print(adjust_price('3.495'))
3.495

Of course now you'll get:

>>> print(adjust_price('3.497'))
3.495

Maybe that's not what was wanted either (I'm not sure exactly what you
do want in this case).


Oscar


More information about the Tutor mailing list