0

I have a float that's between 0 and 1 (inclusive) that I'm printing as a percentage:

complete = 999
total = 1000

print(f"Completed {complete} out of {total} ({complete / total:.0%})")

but when I get really close to (but not quite at) 100%, it jumps the gun and prints 100%, which is not what users expect from a loading screen:

Completed 999 out of 1000 (100%)

I would like the above to say 99%, even though it does round up to 100%. Likewise if I've completed 1/1000 I would like to say I've completed 1% instead of nothing (0%).

Boris Verkhovskiy
  • 14,854
  • 11
  • 100
  • 103
  • This question is similar to [Rounding a percentage in Python](https://stackoverflow.com/questions/22250692/rounding-a-percentage-in-python) – Boris Verkhovskiy Jul 05 '20 at 17:36

2 Answers2

1

Here's one way to do it:

complete = 999 
total = 1000

pct = math.floor(complete * 100.0/total)/100
if complete / total >= 0.001:
    pct = max(pct, 0.01)
    
print(f"Completed {complete} out of {total} ({pct:.0%})")

Output:

Completed 999 out of 1000 (99%)

if complete is 1, it'll print 1% even though it's closer to 0.

A more complete solution

A more comprehensive solution that follows the same rational would round up for everything up to 50%, and then round down for everything from 50 to 100%:

def get_pct(complete, total):
    
    pct = (complete * 100.0 / total)
    if pct > 50: 
        pct = math.floor(pct) /100
    else:
        pct = math.ceil(pct) /100
    return pct

complete = 1
total = 1000
print(f"Completed {complete} out of {total} ({get_pct(complete, total):.0%})")
#==> Completed 1 out of 1000 (1%)

complete = 999
total = 1000
print(f"Completed {complete} out of {total} ({get_pct(complete, total):.0%})")
#==> Completed 999 out of 1000 (99%)

complete = 555
total = 1000
print(f"Completed {complete} out of {total} ({get_pct(complete, total):.0%})")
#==> Completed 555 out of 1000 (55%)

complete = 333
total = 1000
print(f"Completed {complete} out of {total} ({get_pct(complete, total):.0%})")
#==> Completed 333 out of 1000 (34%)
Roy2012
  • 11,755
  • 2
  • 22
  • 35
0

Here's how I did it:

def format_loading_percent(f, ndigits=0):
    """Formats a float as a percentage with ndigits decimal points 
    but 0.001 is rounded up to 1% and .999 is rounded down to 99%."""
    limit = 10 ** -(ndigits + 2)
    if limit > f > 0:
        f = limit
    if 1 > f > (1 - limit):
        f = 1 - limit
    return f"{f:.{ndigits}%}"

Usage examples:

format_loading_percent(0.01)                 # '1%'
format_loading_percent(0.001)                # '1%'
format_loading_percent(0.000001, ndigits=2)  # '0.01%'
format_loading_percent(0.999)                # '99%'
format_loading_percent(0.995)                # '99%'
format_loading_percent(0.991)                # '99%'

Edit: on second thought, it's more correct to print <1% and >99%:

def format_loading_percent(f, ndigits=0):
    limit = 10 ** -(ndigits + 2)
    if limit > f > 0:
        return f"<{limit:.{ndigits}%}"
    if 1 > f > (1 - limit):
        return f">{1 - limit:.{ndigits}%}"
    return f"{f:.{ndigits}%}"
Boris Verkhovskiy
  • 14,854
  • 11
  • 100
  • 103