Created
May 7, 2016 19:01
-
-
Save Gabriel-p/1a557a678d082c5d814bb7fd11f1581b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment