from decimal import Decimal
import numpy as np
def format_5(f):
"""
Round floats that end with a '5' correctly, bypassing Python's issue
with the round() function.
See: http://codereview.stackexchange.com/q/122243/35351
"""
# If the last digit is 5 or larger, round up.
if int(f[-1]) >= 5:
r_up_down = 'ROUND_UP'
else:
r_up_down = 'ROUND_DOWN'
return Decimal.quantize(Decimal(f), Decimal(f[:-1]), rounding=r_up_down)
def count_zeros(sf):
"""
Count the number of leading zeros until the first non-zero digit.
Only works on float < 1.
"""
nd = 0
for d in sf[2:]: # Remove trailing '0.'
if d == '0':
nd += 1
else:
# Stop at the first non-zero digit found.
break
return nd
def round1(f):
"""
Round *positive* float to 1 significant figure.
"""
# Float as string.
sf = str(f)
if f == 0.:
f_r = 0.
elif 0. < f < 1.:
# Number of leading zeros.
nd = count_zeros(sf)
# If the float already has a single non-zero digit.
if len(sf) == (2 + nd + 1):
f_r = f
else:
# Isolate first two non-zero digits.
dn = sf[2+nd:nd+4]
# Create new float using the first two digits to round
# to a single digit.
sf_r = '0.' + dn
# Round digits.
sf_r5 = format_5(sf_r)
# Generate final properly rounded float.
# Catch special case.
if sf_r5 == Decimal('1.0'):
if nd > 0:
f_r = '0.' + '0'*nd + '1'
else:
f_r = 1.
else:
# General case.
f_r = '0.' + '0'*nd + str(sf_r5)[2:]
elif 1. <= f < 10.:
# Create float with first two digits, without the floating point.
sf_r = '0.' + sf[0] + sf[2]
# Round to one digit.
sf_r5 = format_5(sf_r)
# Catch special case.
if sf_r5 == Decimal('1.0'):
f_r = 10.
else:
# General case.
f_r = str(sf_r5)[-1]
else:
# Number of digits before the decimal point.
nb = len(sf.split('.')[0])
# Create float with first two digits.
sf_r = '0.' + sf[:2]
# Round to one digit.
sf_r5 = format_5(sf_r)
# Catch special case.
if sf_r5 == Decimal('1.0'):
f_r = '1' + str(sf_r5)[-1] + '0'*(nb-1) + '.'
else:
# General case.
f_r = str(sf_r5)[-1] + '0'*(nb-1) + '.'
return float(f_r)
f_lst = [0.01, 0.1, 0.99, 0.099, 0.95, 0.094, 0.00012, 0.055, 0.0075, 0.23]
f_lst = [1.0025, 5.57, 6.99, 9.49, 9.55, 9.99]
f_lst = [10., 10.05, 500.5, 556, 9956.2]
f_lst = np.random.uniform(10., 100000., 100)
for f in f_lst:
print 'orig:', f
f_r = round1(f)
print 'round:', f_r, '\n'