mirror of
https://github.com/python/cpython
synced 2024-10-18 19:23:19 +00:00
4acc25bd39
1. Comments at the beginning of the module, before functions, and before classes have been turned into docstrings. 2. Tabs are normalized to four spaces. Also, removed the "remove" function from dircmp.py, which reimplements list.remove() (it must have been very old).
142 lines
3.9 KiB
Python
142 lines
3.9 KiB
Python
"""General floating point formatting functions.
|
|
|
|
Functions:
|
|
fix(x, digits_behind)
|
|
sci(x, digits_behind)
|
|
|
|
Each takes a number or a string and a number of digits as arguments.
|
|
|
|
Parameters:
|
|
x: number to be formatted; or a string resembling a number
|
|
digits_behind: number of digits behind the decimal point
|
|
"""
|
|
|
|
import re
|
|
|
|
# Compiled regular expression to "decode" a number
|
|
decoder = re.compile(r'^([-+]?)0*(\d*)((?:\.\d*)?)(([eE][-+]?\d+)?)$')
|
|
# \0 the whole thing
|
|
# \1 leading sign or empty
|
|
# \2 digits left of decimal point
|
|
# \3 fraction (empty or begins with point)
|
|
# \4 exponent part (empty or begins with 'e' or 'E')
|
|
|
|
try:
|
|
class NotANumber(ValueError):
|
|
pass
|
|
except TypeError:
|
|
NotANumber = 'fpformat.NotANumber'
|
|
|
|
# Return (sign, intpart, fraction, expo) or raise an exception:
|
|
# sign is '+' or '-'
|
|
# intpart is 0 or more digits beginning with a nonzero
|
|
# fraction is 0 or more digits
|
|
# expo is an integer
|
|
def extract(s):
|
|
res = decoder.match(s)
|
|
if res is None: raise NotANumber, s
|
|
sign, intpart, fraction, exppart = res.group(1,2,3,4)
|
|
if sign == '+': sign = ''
|
|
if fraction: fraction = fraction[1:]
|
|
if exppart: expo = int(exppart[1:])
|
|
else: expo = 0
|
|
return sign, intpart, fraction, expo
|
|
|
|
# Remove the exponent by changing intpart and fraction
|
|
def unexpo(intpart, fraction, expo):
|
|
if expo > 0: # Move the point left
|
|
f = len(fraction)
|
|
intpart, fraction = intpart + fraction[:expo], fraction[expo:]
|
|
if expo > f:
|
|
intpart = intpart + '0'*(expo-f)
|
|
elif expo < 0: # Move the point right
|
|
i = len(intpart)
|
|
intpart, fraction = intpart[:expo], intpart[expo:] + fraction
|
|
if expo < -i:
|
|
fraction = '0'*(-expo-i) + fraction
|
|
return intpart, fraction
|
|
|
|
# Round or extend the fraction to size digs
|
|
def roundfrac(intpart, fraction, digs):
|
|
f = len(fraction)
|
|
if f <= digs:
|
|
return intpart, fraction + '0'*(digs-f)
|
|
i = len(intpart)
|
|
if i+digs < 0:
|
|
return '0'*-digs, ''
|
|
total = intpart + fraction
|
|
nextdigit = total[i+digs]
|
|
if nextdigit >= '5': # Hard case: increment last digit, may have carry!
|
|
n = i + digs - 1
|
|
while n >= 0:
|
|
if total[n] != '9': break
|
|
n = n-1
|
|
else:
|
|
total = '0' + total
|
|
i = i+1
|
|
n = 0
|
|
total = total[:n] + chr(ord(total[n]) + 1) + '0'*(len(total)-n-1)
|
|
intpart, fraction = total[:i], total[i:]
|
|
if digs >= 0:
|
|
return intpart, fraction[:digs]
|
|
else:
|
|
return intpart[:digs] + '0'*-digs, ''
|
|
|
|
# Format x as [-]ddd.ddd with 'digs' digits after the point
|
|
# and at least one digit before.
|
|
# If digs <= 0, the point is suppressed.
|
|
def fix(x, digs):
|
|
if type(x) != type(''): x = `x`
|
|
try:
|
|
sign, intpart, fraction, expo = extract(x)
|
|
except NotANumber:
|
|
return x
|
|
intpart, fraction = unexpo(intpart, fraction, expo)
|
|
intpart, fraction = roundfrac(intpart, fraction, digs)
|
|
while intpart and intpart[0] == '0': intpart = intpart[1:]
|
|
if intpart == '': intpart = '0'
|
|
if digs > 0: return sign + intpart + '.' + fraction
|
|
else: return sign + intpart
|
|
|
|
# Format x as [-]d.dddE[+-]ddd with 'digs' digits after the point
|
|
# and exactly one digit before.
|
|
# If digs is <= 0, one digit is kept and the point is suppressed.
|
|
def sci(x, digs):
|
|
if type(x) != type(''): x = `x`
|
|
sign, intpart, fraction, expo = extract(x)
|
|
if not intpart:
|
|
while fraction and fraction[0] == '0':
|
|
fraction = fraction[1:]
|
|
expo = expo - 1
|
|
if fraction:
|
|
intpart, fraction = fraction[0], fraction[1:]
|
|
expo = expo - 1
|
|
else:
|
|
intpart = '0'
|
|
else:
|
|
expo = expo + len(intpart) - 1
|
|
intpart, fraction = intpart[0], intpart[1:] + fraction
|
|
digs = max(0, digs)
|
|
intpart, fraction = roundfrac(intpart, fraction, digs)
|
|
if len(intpart) > 1:
|
|
intpart, fraction, expo = \
|
|
intpart[0], intpart[1:] + fraction[:-1], \
|
|
expo + len(intpart) - 1
|
|
s = sign + intpart
|
|
if digs > 0: s = s + '.' + fraction
|
|
e = `abs(expo)`
|
|
e = '0'*(3-len(e)) + e
|
|
if expo < 0: e = '-' + e
|
|
else: e = '+' + e
|
|
return s + 'e' + e
|
|
|
|
# Interactive test run
|
|
def test():
|
|
try:
|
|
while 1:
|
|
x, digs = input('Enter (x, digs): ')
|
|
print x, fix(x, digs), sci(x, digs)
|
|
except (EOFError, KeyboardInterrupt):
|
|
pass
|
|
|