6

I want to round like this 42949672 -> 42949700, 2147483647 -> 2147480000, 4252017622 -> 4252020000 etc.

I tried to use following , but only works for the first one. How can I make a more general one? thanks

round(42949672, -2) 
2
  • round(2147483647, -4) gives 2147480000 as desired. You need to tell it how many digits places to round to. Commented Jul 18, 2017 at 23:14
  • yes, but round(42949672, -4) doesn't give 42949700. I guess I need to digit places math myself Commented Jul 18, 2017 at 23:36

7 Answers 7

10

The accepted answer has limitations and does not produce technically correct significant figures in the general case.

numpy.format_float_positional supports the desired behaviour directly. The following fragment returns the float x formatted to 4 significant figures, with scientific notation suppressed.

import numpy as np x=12345.6 np.format_float_positional(x, precision=4, unique=False, fractional=False, trim='k') > 12340. 
Sign up to request clarification or add additional context in comments.

Comments

3

To expand upon Autumn's answer, the reason that the accepted answer fails in the general case is that round is rounding to decimal places and not (necessarily) to significant figures.

The following function addresses the case when there are significant figures both before and after the decimal place:

import math def round_to_nsf(number, nsf=6): integer_part = math.floor(number) return round(number, nsf - len(str(integer_part))) 

although it doesn't address the case for numbers like 0.0000012345678, which rounded to 6 s.f. should be 0.00000123457.

The following code almost works - but is very inefficient, risks stack overflow through its use of recursion and won't always return the correct answer due to the fact that computers use binary arithmetic instead of decimal:

def round_to_nsf(number, nsf=6): integer_part = math.floor(number) if integer_part > 0: return round(number, nsf - len(str(integer_part))) else: return round_to_nsf(10 * number, nsf) / 10 

But I think that the else clause could be modified by transforming the number into a string, using the main function in a single recursive step, then constructing the output from strings in order to obtain a non-recurring decimal.

Comments

1

I assume you want to round to 6 significant figures. If you want to round ints, you can use

def round_sf(number, significant): return round(number, significant - len(str(number))) print(round_sf(4252017622, 6)) 

Result:

4252020000 

Comments

0

To round to any digits that you are targeting, you can simply put the digits as a negative number in your round function and then convert it to integer.

digits = 4 num = 2147483647 rounded_num = int(round(num, -digits)) 

If you are trying to keep 6 significant figures always, that's the pattern that I saw in your question you would want to do this:

def sf_round(num, sig_fig): return int(round(num, -(len(str(num))-sig_fig))) 

Comments

0

Expanded on @david-joy's function. Indeed, it did not work properly in the else clause, and actually not all cases were covered in the if clause. So here is my function.

def round_to_nsf(value, nsf=2): """ Rounds the number to the provided number of significant figures. """ integer_part = math.floor(value) if integer_part > 0: integer_part_len = len(str(integer_part)) return round(value, nsf-integer_part_len) else: str_value = str(value) #if of the form "8e-05" if '-' in str_value: index = int(str_value[str_value.find('-')+1:]) - 1 else: st = {'1', '2', '3', '4', '5', '6', '7', '8', '9'} index = next((i for i, ch in enumerate(str(value)) if ch in st), None) - 2 return round(value, index+nsf) 

Comments

0

These work for me, for ints and floats, both positive and negative. If desired they may be modified to preserve the original int or float type by applying isinstance() to x and then return int(float()) or float().

Note that format beyond around 14 digits can give unexpected results.

# Option 1: use math to find number of digits left of decimal import math def round_sig(x, sigdig=14): """Round x to sigdig significant digits""" if x == 0: return 0 # can't log10(0) # n = digits left of decimal, can be negative n = math.floor(math.log10(abs(x))) + 1 return round(x, sigdig - n) # Option 2: use format to round def round_sig2(x, sigdig=14): """Round x to sigdig significant digits""" f = f'{{:.{sigdig - 1}E}}' s = f.format(x) return float(s) # Option 3: Use format to find number of digits left of decimal def round_sig3(x, sigdig=14): """Round x to sigdig significant digits""" s = '{:.0E}'.format(x) # n = digits left of decimal, can be negative n = int( s[s.index('E') + 1 :] ) + 1 return round(x, sigdig - n) # Bonus: use math to truncate import math def trunc_sig(x, sigdig=14): """Truncate x to sigdig significant digits""" if x == 0: return 0 # can't log10(0) # n = digits left of decimal, can be negative n = math.floor(math.log10(abs(x))) + 1 shift = 10**(sigdig - n) return int(x * shift) / shift # Unexpected format results with large number of significant digits >>> '{:.20E}'.format(10**-1) '1.00000000000000005551E-01' >>> '{:.20E}'.format(10**-10) '1.00000000000000003643E-10' >>> '{:.20E}'.format(10**-20) '9.99999999999999945153E-21' >>> n = .123456789012345678901234567890 >>> '{:.20E}'.format(n) '1.23456789012345677370E-01' 

Comments

0

I've developed a PyPi packaged called sciform which is designed to do this type of rounding. sciform is principally designed to round and format numbers into strings according to many options. For this use case sciform can be used to round a number to a certain number of sig figs and then the string can be converted back to a float (or int in this case).

from sciform import Formatter sform = Formatter( round_mode="sig_fig", # This is the default behavior ndigits=6, ) for num in [42949672, 2147483647, 4252017622]: result = int(sform(num)) print(f'{num} -> {result}') # 42949672 -> 42949700 # 2147483647 -> 2147480000 # 4252017622 -> 4252020000 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.