"""General floating point formatting functions.
Each takes a number or a string and a number of digits as arguments.
x: number to be formatted; or a string resembling a number
digits_behind: number of digits behind the decimal point
__all__
= ["fix","sci","NotANumber"]
# Compiled regular expression to "decode" a number
decoder
= re
.compile(r
'^([-+]?)0*(\d*)((?:\.\d*)?)(([eE][-+]?\d+)?)$')
# \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')
class NotANumber(ValueError):
NotANumber
= 'fpformat.NotANumber'
"""Return (sign, intpart, fraction, expo) or raise an exception:
intpart is 0 or more digits beginning with a nonzero
fraction is 0 or more digits
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:])
return sign
, intpart
, fraction
, expo
def unexpo(intpart
, fraction
, expo
):
"""Remove the exponent by changing intpart and fraction."""
if expo
> 0: # Move the point left
intpart
, fraction
= intpart
+ fraction
[:expo
], fraction
[expo
:]
intpart
= intpart
+ '0'*(expo
-f
)
elif expo
< 0: # Move the point right
intpart
, fraction
= intpart
[:expo
], intpart
[expo
:] + fraction
fraction
= '0'*(-expo
-i
) + fraction
def roundfrac(intpart
, fraction
, digs
):
"""Round or extend the fraction to size digs."""
return intpart
, fraction
+ '0'*(digs
-f
)
total
= intpart
+ fraction
nextdigit
= total
[i
+digs
]
if nextdigit
>= '5': # Hard case: increment last digit, may have carry!
if total
[n
] != '9': break
total
= total
[:n
] + chr(ord(total
[n
]) + 1) + '0'*(len(total
)-n
-1)
intpart
, fraction
= total
[:i
], total
[i
:]
return intpart
, fraction
[:digs
]
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."""
if type(x
) != type(''): x
= repr(x
)
sign
, intpart
, fraction
, expo
= extract(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."""
if type(x
) != type(''): x
= repr(x
)
sign
, intpart
, fraction
, expo
= extract(x
)
while fraction
and fraction
[0] == '0':
intpart
, fraction
= fraction
[0], fraction
[1:]
expo
= expo
+ len(intpart
) - 1
intpart
, fraction
= intpart
[0], intpart
[1:] + fraction
intpart
, fraction
= roundfrac(intpart
, fraction
, digs
)
intpart
, fraction
, expo
= \
intpart
[0], intpart
[1:] + fraction
[:-1], \
if digs
> 0: s
= s
+ '.' + fraction
"""Interactive test run."""
x
, digs
= input('Enter (x, digs): ')
print x
, fix(x
, digs
), sci(x
, digs
)
except (EOFError, KeyboardInterrupt):