Is there another way to cut the numbers after the 10th decimal without callig round()?

Viewed 48

I wanted to create a function that whatever the number received as input, it would cut-off (not round, which could take some decimal higher) every number after the 10th one. It normally works fine, but if I call the function with a number like this:

1234567.123456789123456789

It returns: 1234567.1234567892

And not 1234567.1234567891, as expected.

def cut_to_10(number):
    number = str(number)
    j = 0
    if "." in number:
        for i in number:
            j += 1
            if i == ".":
                before_dot = number[:j - 1]
                after_dot = number[j:]
                if len(after_dot) > 10:
                    after_dot = after_dot[:10]

                return float(before_dot + "." + after_dot)

    if "." not in number:
        return round(float(number), 10)

EDIT 1:

Also tried this, and the same unexpected result returned.

import numpy as np

def cut_to_10(number):
    return np.floor(number * 1e10) / 1e10

EDIT 2:

Considering the amazing explanation given by ShadowRanger, but, considering also that I need more a ceiling precision than a floor one, and given the fact that I still had to work with float, I just decided to change

return float(before_dot + "." + after_dot)

to

return float(before_dot + "." + after_dot) - 2e-10

and it worked fine. 1e-10 didn't work.

2 Answers

1234567.123456789123456789 is not an actual float. You can type it, but it becomes 1234567.1234567892, the closest representable float value; the rounding is occurring before you actually call round.

>>> 1234567.123456789123456789
1234567.1234567892

If you need that much precision, you need to move to decimal.Decimal, which will work just fine:

>>> decimal.Decimal('1234567.123456789123456789')
Decimal('1234567.123456789123456789')

>>> round(_, 10)
Decimal('1234567.1234567891')

Just don't convert back to float, because 1234567.1234567891 doesn't exist (if it did, that's what 1234567.123456789123456789 would have produced), and you'll be back where you started with 1234567.1234567892.

See the other answer for the correct solution. Leaving this one up just because it seems like a more straightforward approach to doing the truncation once you work past the floating point limitation.

import numpy as np
    
def cut_to_10(number):
    return np.floor(number * 1e10) / 1e10
Related