简体   繁体   中英

Pythonic way to round a float to specific min AND max number of decimal places

I would like to implement a function round(num, min, max) that rounds a float to at least min decimal places and at most max . I want it to return a string. I have something that works, but it was surprisingly long. Can you write something more pythonic than me?

Usage

round(3.14159, 2, 4) --> '3.1416'
round(3.14159, 0, 2) --> '3.14'
round(3.14159, 0, 1) --> '3.1'
round(3.14159, 0, 0) --> '3'
round(3.14159, 4, 6) --> '3.141590'
round(3.14, 4, 6)    --> '3.1400'
round(3, 4, 6)       --> '3.0000'

I think you get the idea. Here's what I've got.

def normalize(amount, min=0, max=2):
    """
    Rounds to a variable number of decimal places - as few as necessary in the range [min,max]
    :param amount: A float, int, decimal.Decimal, or string.
    :param min: the minimum number of decimal places to keep
    :param max: the maximum number of decimal places to keep
    :return: string.
    """
    if not amount:
        return amount

    # To Decimal, round to highest desired precision
    d = round(Decimal(amount), max)
    s = str(d)

    # Truncate as many extra zeros as we are allowed to
    for i in range(max-min):
        if s[-1] == '0':
            s = s[:-1]

    # Lose a trailing decimal point.
    if s[-1] == '.':
        s = s[:-1]

    return s

You're confusing float rounding with print formatting. *

The float 3.14 is a different number than 3.1415 . So rounding 3.1415 to 2 digits makes sense.

But the float 3.00 is the exact same number as 3.0 . So rounding 3.0 to 2 digits isn't going to do anything; it's still going to be the same thing you started with.

Meanwhile, if you want to change the way numbers are printed out, you can do that using the format function, the str.format method, f-strings, % formatting, etc. For example:

>>> pi = 3.1415
>>> indianapi = round(pi, 0)
>>> indianapi
3.0
>>> f'{indianapi:.3f}'
'3.000'

See Format Specification Mini-Language for details on how to use f-strings (and str.format and format ), and printf -Style String Formatting for details on how to use % .


* Either that, or you're expecting floats to keep track of their precision and preserve it through a chain of operations. If that's what you're after, floats can't do that, but decimal.Decimal can, so you may want to look at the decimal module. But I don't think that's what you want here.

Just some small improvements, but keeping the basic idea (convert value, strip zeroes, strip trailing decimal point).

First change is to avoid name clash with builtin functions min and max . I think that using functions designed for the particular purpose ( str.format for value formatting, str.rstrip for stripping from right, str.endswith for testing the last character) makes it little bit more pythonic.

def round(amount, dmin, dmax):
    assert 0 <= dmin <= dmax
    astr = '{:.{prec}f}'.format(amount, prec=dmax)
    if dmax != dmin:
        astr = astr[:dmin-dmax] + astr[dmin-dmax:].rstrip('0')
        if astr.endswith('.'):
            astr = astr[:-1]
    return astr

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM