1 # General floating point formatting functions.
4 # fix(x, digits_behind)
5 # sci(x, digits_behind)
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 # Compiled regular expression to "decode" a number
17 decoder
= re
.compile(r
'^([-+]?)0*(\d*)((?:\.\d*)?)(([eE][-+]?\d+)?)$')
19 # \1 leading sign or empty
20 # \2 digits left of decimal point
21 # \3 fraction (empty or begins with point)
22 # \4 exponent part (empty or begins with 'e' or 'E')
25 class NotANumber(ValueError):
28 NotANumber
= 'fpformat.NotANumber'
30 # Return (sign, intpart, fraction, expo) or raise an exception:
32 # intpart is 0 or more digits beginning with a nonzero
33 # fraction is 0 or more digits
36 res
= decoder
.match(s
)
37 if res
is None: raise NotANumber
, s
38 sign
, intpart
, fraction
, exppart
= res
.group(1,2,3,4)
39 if sign
== '+': sign
= ''
40 if fraction
: fraction
= fraction
[1:]
41 if exppart
: expo
= int(exppart
[1:])
43 return sign
, intpart
, fraction
, expo
45 # Remove the exponent by changing intpart and fraction
46 def unexpo(intpart
, fraction
, expo
):
47 if expo
> 0: # Move the point left
49 intpart
, fraction
= intpart
+ fraction
[:expo
], fraction
[expo
:]
51 intpart
= intpart
+ '0'*(expo
-f
)
52 elif expo
< 0: # Move the point right
54 intpart
, fraction
= intpart
[:expo
], intpart
[expo
:] + fraction
56 fraction
= '0'*(-expo
-i
) + fraction
57 return intpart
, fraction
59 # Round or extend the fraction to size digs
60 def roundfrac(intpart
, fraction
, digs
):
63 return intpart
, fraction
+ '0'*(digs
-f
)
67 total
= intpart
+ fraction
68 nextdigit
= total
[i
+digs
]
69 if nextdigit
>= '5': # Hard case: increment last digit, may have carry!
72 if total
[n
] != '9': break
78 total
= total
[:n
] + chr(ord(total
[n
]) + 1) + '0'*(len(total
)-n
-1)
79 intpart
, fraction
= total
[:i
], total
[i
:]
81 return intpart
, fraction
[:digs
]
83 return intpart
[:digs
] + '0'*-digs
, ''
85 # Format x as [-]ddd.ddd with 'digs' digits after the point
86 # and at least one digit before.
87 # If digs <= 0, the point is suppressed.
89 if type(x
) != type(''): x
= `x`
91 sign
, intpart
, fraction
, expo
= extract(x
)
94 intpart
, fraction
= unexpo(intpart
, fraction
, expo
)
95 intpart
, fraction
= roundfrac(intpart
, fraction
, digs
)
96 while intpart
and intpart
[0] == '0': intpart
= intpart
[1:]
97 if intpart
== '': intpart
= '0'
98 if digs
> 0: return sign
+ intpart
+ '.' + fraction
99 else: return sign
+ intpart
101 # Format x as [-]d.dddE[+-]ddd with 'digs' digits after the point
102 # and exactly one digit before.
103 # If digs is <= 0, one digit is kept and the point is suppressed.
105 if type(x
) != type(''): x
= `x`
106 sign
, intpart
, fraction
, expo
= extract(x
)
108 while fraction
and fraction
[0] == '0':
109 fraction
= fraction
[1:]
112 intpart
, fraction
= fraction
[0], fraction
[1:]
117 expo
= expo
+ len(intpart
) - 1
118 intpart
, fraction
= intpart
[0], intpart
[1:] + fraction
120 intpart
, fraction
= roundfrac(intpart
, fraction
, digs
)
122 intpart
, fraction
, expo
= \
123 intpart
[0], intpart
[1:] + fraction
[:-1], \
124 expo
+ len(intpart
) - 1
126 if digs
> 0: s
= s
+ '.' + fraction
128 e
= '0'*(3-len(e
)) + e
129 if expo
< 0: e
= '-' + e
133 # Interactive test run
137 x
, digs
= input('Enter (x, digs): ')
138 print x
, fix(x
, digs
), sci(x
, digs
)
139 except (EOFError, KeyboardInterrupt):