3

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?

Zurechtweiser
  • 1,165
  • 2
  • 16
  • 29
  • `1 - math.log10(n)` gives the number of digits to round to, I think (not extensively tested). – jasonharper Jun 08 '20 at 22:19
  • Does this answer your question? [How to round a number to significant figures in Python](https://stackoverflow.com/questions/3410976/how-to-round-a-number-to-significant-figures-in-python) – Pavel Jun 08 '20 at 22:21
  • `0.1` is actually a bad example. You cannot store `0.1` (*exactly*) in a floating point number, it's always going to be either `0.09999...` or `0.1000...[not-0]`. – Jongware Jun 08 '20 at 22:25
  • It is better to use `decimal.Decimal` . Dealing string becomes difficult when number has exponent like 0.12E-32 – niitsuma Jun 09 '20 at 01:38

5 Answers5

4

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.

Han-Kwang Nienhuys
  • 3,084
  • 2
  • 12
  • 31
2

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]
Danil Speransky
  • 29,891
  • 5
  • 68
  • 79
0

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
Red
  • 26,798
  • 7
  • 36
  • 58
0

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?

Grismar
  • 27,561
  • 4
  • 31
  • 54
0

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)
Red
  • 26,798
  • 7
  • 36
  • 58