Question

I don't mean precision as in how many numbers are displayed after the decimal. I mean precision as in the decimal I am trying to use in this pictograph function keeps coming up one tenth shy of what it should be. I have tried using multiple different strategies including importing the decimal module. Here is the function I am trying to use.

values = [('tens', 10), ('fives', 5), ('ones', 1), ('tenths', 0.1)]

def get_digits(num):
    num = int(num * 10)
    num = float(num) / 10

    output_dict = {}
    for place, value in values:
        output_dict[place] = int(num // value)
        num = num % value

    return output_dict

using get_digits(277.9), yields {'tens': 27, 'ones': 2, 'tenths': 8, 'fives': 1}

I need for it to say {'tens': 27, 'ones': 2, 'tenths': 9, 'fives': 1}

Fixing it by adding 1 to the tenths after the dictionary is populated does not work, because not every decimal comes out imprecisely.

get_digits(277.6) returns {'fives': 1, 'tenths': 6, 'tens': 27, 'ones': 2}

I've been scouring the forums for an answer, but nothing quite gets it. Any assistance would be greatly appreciated.

Was it helpful?

Solution

As I tried to explain in comments, the problem is that you're using floating point numbers.

For more information on floating point, see 1, 2.

The issue is that 277.9 is not actually stored as 277.9, but rather something "very close":

print('%.40f' % a)
277.8999999999999772626324556767940521240234

The solution isn't to use some arbitrary heuristic to tell whether the math is slightly off and try to correct for it. We know the math is slightly off -- that's the gift and curse given to us by floating point.

The real solution is to use fixed point math, for example, with Python's decimal module.


Edit

from decimal import Decimal

values = [
    ('tens',   Decimal(10)),
    ('fives',  Decimal(5)),
    ('ones',   Decimal(1)),
    ('tenths', Decimal('0.1'))
]

def get_digits(num):
    output_dict = {}
    for place, value in values:
        output_dict[place] = int(num // value)  # Cast from Decimal to int
        num = num % value
    return output_dict

num = Decimal('277.9')
print(get_digits(num))
# {'tens': 27, 'ones': 2, 'tenths': 9, 'fives': 1}

num = Decimal('277.6')
print(get_digits(num))
#{'tens': 27, 'ones': 2, 'tenths': 6, 'fives': 1}

The above code is very similar to yours, but uses Python's decimal module. No heuristic check is needed. The code just works, because decimals are represented accurately.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top