1 """General floating point formatting functions.
7 Each takes a number or a string and a number of digits as arguments.
10 x: number to be formatted; or a string resembling a number
11 digits_behind: number of digits behind the decimal point
16 __all__
= ["fix","sci","NotANumber"]
18 # Compiled regular expression to "decode" a number
19 decoder
= re
.compile(r
'^([-+]?)0*(\d*)((?:\.\d*)?)(([eE][-+]?\d+)?)$')
21 # \1 leading sign or empty
22 # \2 digits left of decimal point
23 # \3 fraction (empty or begins with point)
24 # \4 exponent part (empty or begins with 'e' or 'E')
27 class NotANumber(ValueError):
30 NotANumber
= 'fpformat.NotANumber'
33 """Return (sign, intpart, fraction, expo) or raise an exception:
35 intpart is 0 or more digits beginning with a nonzero
36 fraction is 0 or more digits
38 res
= decoder
.match(s
)
39 if res
is None: raise NotANumber
, s
40 sign
, intpart
, fraction
, exppart
= res
.group(1,2,3,4)
41 if sign
== '+': sign
= ''
42 if fraction
: fraction
= fraction
[1:]
43 if exppart
: expo
= int(exppart
[1:])
45 return sign
, intpart
, fraction
, expo
47 def unexpo(intpart
, fraction
, expo
):
48 """Remove the exponent by changing intpart and fraction."""
49 if expo
> 0: # Move the point left
51 intpart
, fraction
= intpart
+ fraction
[:expo
], fraction
[expo
:]
53 intpart
= intpart
+ '0'*(expo
-f
)
54 elif expo
< 0: # Move the point right
56 intpart
, fraction
= intpart
[:expo
], intpart
[expo
:] + fraction
58 fraction
= '0'*(-expo
-i
) + fraction
59 return intpart
, fraction
61 def roundfrac(intpart
, fraction
, digs
):
62 """Round or extend the fraction to size digs."""
65 return intpart
, fraction
+ '0'*(digs
-f
)
69 total
= intpart
+ fraction
70 nextdigit
= total
[i
+digs
]
71 if nextdigit
>= '5': # Hard case: increment last digit, may have carry!
74 if total
[n
] != '9': break
80 total
= total
[:n
] + chr(ord(total
[n
]) + 1) + '0'*(len(total
)-n
-1)
81 intpart
, fraction
= total
[:i
], total
[i
:]
83 return intpart
, fraction
[:digs
]
85 return intpart
[:digs
] + '0'*-digs
, ''
88 """Format x as [-]ddd.ddd with 'digs' digits after the point
89 and at least one digit before.
90 If digs <= 0, the point is suppressed."""
91 if type(x
) != type(''): x
= repr(x
)
93 sign
, intpart
, fraction
, expo
= extract(x
)
96 intpart
, fraction
= unexpo(intpart
, fraction
, expo
)
97 intpart
, fraction
= roundfrac(intpart
, fraction
, digs
)
98 while intpart
and intpart
[0] == '0': intpart
= intpart
[1:]
99 if intpart
== '': intpart
= '0'
100 if digs
> 0: return sign
+ intpart
+ '.' + fraction
101 else: return sign
+ intpart
104 """Format x as [-]d.dddE[+-]ddd with 'digs' digits after the point
105 and exactly one digit before.
106 If digs is <= 0, one digit is kept and the point is suppressed."""
107 if type(x
) != type(''): x
= repr(x
)
108 sign
, intpart
, fraction
, expo
= extract(x
)
110 while fraction
and fraction
[0] == '0':
111 fraction
= fraction
[1:]
114 intpart
, fraction
= fraction
[0], fraction
[1:]
119 expo
= expo
+ len(intpart
) - 1
120 intpart
, fraction
= intpart
[0], intpart
[1:] + fraction
122 intpart
, fraction
= roundfrac(intpart
, fraction
, digs
)
124 intpart
, fraction
, expo
= \
125 intpart
[0], intpart
[1:] + fraction
[:-1], \
126 expo
+ len(intpart
) - 1
128 if digs
> 0: s
= s
+ '.' + fraction
130 e
= '0'*(3-len(e
)) + e
131 if expo
< 0: e
= '-' + e
136 """Interactive test run."""
139 x
, digs
= input('Enter (x, digs): ')
140 print x
, fix(x
, digs
), sci(x
, digs
)
141 except (EOFError, KeyboardInterrupt):