Round to a number that is not zero

Viewed 652

In python you have round(number, digits).

I have numbers like

0.123525,
0.01346346,
0.0078236346,
3.43568
...

My goal is for round to round up to the first digit that is not zero

ie

0.1
0.01
0.008
3
...

My pseudocode-approach:

convert double to string
for each literal in string check whether it's zero
once it's not, count digits
use digits for digits in round.

Is there a ready made function or an elegant approach?

5 Answers

For a variable x with a value between 1e-4 and 9, a simple way is

  • f'{x:.1g}' as a string
  • float(f'{x:.1g}') as a float.

For example:

>>> x = 0.002345
>>> print(f'{x:.1g}')
0.002

See python format specification for explanation. Briefly, the an f-string f'{x}' converts a variable to string; the :.1g is a format specifier, with .1 indicating the number of significant digits and g indicating that it tries to make a human-friendly representation if possible: rather 0.001 than 1E-03 but 1E-05 rather than 0.00001 because in the latter case, the zeros become hard to count and it takes more space than 1E-05.

Maybe something like this:

import math

def r(n):
    k = 1 - int(math.log10(n))
    return round(n, None if n > 1 else k)

>>> [r(n) for n in [0.123525, 0.01346346, 0.0078236346, 3.43568]]
[0.1, 0.01, 0.008, 3]

Instead of converting to a string and back, you could multiply the number by 10 and keep track of how many times you have to do this before the value is over 1.0.

Example:

0.005869
Times 10
0.05869
loop
0.5869
loop
5.869
break

You looped 3 times (multiplied the value by 10^3), so you can call

round(0.005869, 3) = 0.006

It depends on what you typically expect: how long are these numbers and are they normal numbers, or mostly tiny ones with lots and lots of zeroes?

Also, how do you get these numbers: are they all in float type variables? Do you read them from a text file?

Assuming you have floats:

import math


def to_first(number):
    log10 = math.log(number, 10)
    return number if log10 > 0 else round(number, math.floor(1-log10))


examples = [0.0001, 0.123, 4.3, 0.010000, 0.19]

for x in examples:
    print(f'{x}: {to_first(x)}')

The key of course is math.log(x, 10) - the answer basically tells you what to do and the to_first function does just that.

Note that 0.19 becomes 0.2, not 0.1. I'd assume that's desired behaviour?

Like this:

numbers = [0.123525,0.01346346,0.0078236346,3.43568]
d = [len(str(num).split('.')[1])-len(str(num).split('.')[1].lstrip('0')) for num in numbers]
numbers2 = [round(n,num+1) if num else int(num) for n,num in zip(numbers,d)]
print(numbers2)

Output:

[0, 0.01, 0.008, 3]


For more clarity:

numbers = [0.123525,0.01346346,0.0078236346,3.43568]
numbers2 = []

for num in numbers:

    dec_digits = str(num).split('.')[1] # all decimal digits of the number in a string

    r = len(dec_digits)-len(dec_digits.lstrip('0')) # the amount of zeros on the left side of the string

    if r: # if the number of zeros is greater than 0:
        numbers2.append(round(num,r+1))
    else:
        numbers2.append(int(num))

print(numbers2)
Related