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
= regex
.compile( \
18 '^\([-+]?\)0*\([0-9]*\)\(\(\.[0-9]*\)?\)\(\([eE][-+]?[0-9]+\)?\)$')
20 # \1 leading sign or empty
21 # \2 digits left of decimal point
22 # \3 fraction (empty or begins with point)
23 # \5 exponent part (empty or begins with 'e' or 'E')
25 NotANumber
= 'fpformat.NotANumber'
27 # Return (sign, intpart, fraction, expo) or raise an exception:
29 # intpart is 0 or more digits beginning with a nonzero
30 # fraction is 0 or more digits
33 if decoder
.match(s
) < 0: raise NotANumber
34 (a1
, b1
), (a2
, b2
), (a3
, b3
), (a4
, b4
), (a5
, b5
) = decoder
.regs
[1:6]
35 sign
, intpart
, fraction
, exppart
= \
36 s
[a1
:b1
], s
[a2
:b2
], s
[a3
:b3
], s
[a5
:b5
]
37 if sign
== '+': sign
= ''
38 if fraction
: fraction
= fraction
[1:]
39 if exppart
: expo
= eval(exppart
[1:])
41 return sign
, intpart
, fraction
, expo
43 # Remove the exponent by changing intpart and fraction
44 def unexpo(intpart
, fraction
, expo
):
45 if expo
> 0: # Move the point left
47 intpart
, fraction
= intpart
+ fraction
[:expo
], fraction
[expo
:]
49 intpart
= intpart
+ '0'*(expo
-f
)
50 elif expo
< 0: # Move the point right
52 intpart
, fraction
= intpart
[:expo
], intpart
[expo
:] + fraction
54 fraction
= '0'*(-expo
-i
) + fraction
55 return intpart
, fraction
57 # Round or extend the fraction to size digs
58 def roundfrac(intpart
, fraction
, digs
):
61 return intpart
, fraction
+ '0'*(digs
-f
)
65 total
= intpart
+ fraction
66 nextdigit
= total
[i
+digs
]
67 if nextdigit
>= '5': # Hard case: increment last digit, may have carry!
70 if total
[n
] != '9': break
76 total
= total
[:n
] + chr(ord(total
[n
]) + 1) + '0'*(len(total
)-n
-1)
77 intpart
, fraction
= total
[:i
], total
[i
:]
79 return intpart
, fraction
[:digs
]
81 return intpart
[:digs
] + '0'*-digs
, ''
83 # Format x as [-]ddd.ddd with 'digs' digits after the point
84 # and at least one digit before.
85 # If digs <= 0, the point is suppressed.
87 if type(x
) != type(''): x
= `x`
89 sign
, intpart
, fraction
, expo
= extract(x
)
92 intpart
, fraction
= unexpo(intpart
, fraction
, expo
)
93 intpart
, fraction
= roundfrac(intpart
, fraction
, digs
)
94 while intpart
and intpart
[0] == '0': intpart
= intpart
[1:]
95 if intpart
== '': intpart
= '0'
96 if digs
> 0: return sign
+ intpart
+ '.' + fraction
97 else: return sign
+ intpart
99 # Format x as [-]d.dddE[+-]ddd with 'digs' digits after the point
100 # and exactly one digit before.
101 # If digs is <= 0, one digit is kept and the point is suppressed.
103 if type(x
) != type(''): x
= `x`
104 sign
, intpart
, fraction
, expo
= extract(x
)
106 while fraction
and fraction
[0] == '0':
107 fraction
= fraction
[1:]
110 intpart
, fraction
= fraction
[0], fraction
[1:]
115 expo
= expo
+ len(intpart
) - 1
116 intpart
, fraction
= intpart
[0], intpart
[1:] + fraction
118 intpart
, fraction
= roundfrac(intpart
, fraction
, digs
)
120 intpart
, fraction
, expo
= \
121 intpart
[0], intpart
[1:] + fraction
[:-1], \
122 expo
+ len(intpart
) - 1
124 if digs
> 0: s
= s
+ '.' + fraction
126 e
= '0'*(3-len(e
)) + e
127 if expo
< 0: e
= '-' + e
131 # Interactive test run
135 x
, digs
= input('Enter (x, digs): ')
136 print x
, fix(x
, digs
), sci(x
, digs
)
137 except (EOFError, KeyboardInterrupt):