svn-gvsig-desktop / tags / v1_0_2_Build_907 / extensions / extScripting / scripts / jython / Lib / fpformat.py @ 11015
History | View | Annotate | Download (4.46 KB)
1 |
"""General floating point formatting functions.
|
---|---|
2 |
|
3 |
Functions:
|
4 |
fix(x, digits_behind)
|
5 |
sci(x, digits_behind)
|
6 |
|
7 |
Each takes a number or a string and a number of digits as arguments.
|
8 |
|
9 |
Parameters:
|
10 |
x: number to be formatted; or a string resembling a number
|
11 |
digits_behind: number of digits behind the decimal point
|
12 |
"""
|
13 |
|
14 |
import re |
15 |
|
16 |
__all__ = ["fix","sci","NotANumber"] |
17 |
|
18 |
# Compiled regular expression to "decode" a number
|
19 |
decoder = re.compile(r'^([-+]?)0*(\d*)((?:\.\d*)?)(([eE][-+]?\d+)?)$')
|
20 |
# \0 the whole thing
|
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')
|
25 |
|
26 |
try:
|
27 |
class NotANumber(ValueError): |
28 |
pass
|
29 |
except TypeError: |
30 |
NotANumber = 'fpformat.NotANumber'
|
31 |
|
32 |
def extract(s): |
33 |
"""Return (sign, intpart, fraction, expo) or raise an exception:
|
34 |
sign is '+' or '-'
|
35 |
intpart is 0 or more digits beginning with a nonzero
|
36 |
fraction is 0 or more digits
|
37 |
expo is an integer"""
|
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:]) |
44 |
else: expo = 0 |
45 |
return sign, intpart, fraction, expo
|
46 |
|
47 |
def unexpo(intpart, fraction, expo): |
48 |
"""Remove the exponent by changing intpart and fraction."""
|
49 |
if expo > 0: # Move the point left |
50 |
f = len(fraction)
|
51 |
intpart, fraction = intpart + fraction[:expo], fraction[expo:] |
52 |
if expo > f:
|
53 |
intpart = intpart + '0'*(expo-f)
|
54 |
elif expo < 0: # Move the point right |
55 |
i = len(intpart)
|
56 |
intpart, fraction = intpart[:expo], intpart[expo:] + fraction |
57 |
if expo < -i:
|
58 |
fraction = '0'*(-expo-i) + fraction
|
59 |
return intpart, fraction
|
60 |
|
61 |
def roundfrac(intpart, fraction, digs): |
62 |
"""Round or extend the fraction to size digs."""
|
63 |
f = len(fraction)
|
64 |
if f <= digs:
|
65 |
return intpart, fraction + '0'*(digs-f) |
66 |
i = len(intpart)
|
67 |
if i+digs < 0: |
68 |
return '0'*-digs, '' |
69 |
total = intpart + fraction |
70 |
nextdigit = total[i+digs] |
71 |
if nextdigit >= '5': # Hard case: increment last digit, may have carry! |
72 |
n = i + digs - 1
|
73 |
while n >= 0: |
74 |
if total[n] != '9': break |
75 |
n = n-1
|
76 |
else:
|
77 |
total = '0' + total
|
78 |
i = i+1
|
79 |
n = 0
|
80 |
total = total[:n] + chr(ord(total[n]) + 1) + '0'*(len(total)-n-1) |
81 |
intpart, fraction = total[:i], total[i:] |
82 |
if digs >= 0: |
83 |
return intpart, fraction[:digs]
|
84 |
else:
|
85 |
return intpart[:digs] + '0'*-digs, '' |
86 |
|
87 |
def fix(x, 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 = `x` |
92 |
try:
|
93 |
sign, intpart, fraction, expo = extract(x) |
94 |
except NotANumber:
|
95 |
return 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 |
102 |
|
103 |
def sci(x, digs): |
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 = `x` |
108 |
sign, intpart, fraction, expo = extract(x) |
109 |
if not intpart: |
110 |
while fraction and fraction[0] == '0': |
111 |
fraction = fraction[1:]
|
112 |
expo = expo - 1
|
113 |
if fraction:
|
114 |
intpart, fraction = fraction[0], fraction[1:] |
115 |
expo = expo - 1
|
116 |
else:
|
117 |
intpart = '0'
|
118 |
else:
|
119 |
expo = expo + len(intpart) - 1 |
120 |
intpart, fraction = intpart[0], intpart[1:] + fraction |
121 |
digs = max(0, digs) |
122 |
intpart, fraction = roundfrac(intpart, fraction, digs) |
123 |
if len(intpart) > 1: |
124 |
intpart, fraction, expo = \ |
125 |
intpart[0], intpart[1:] + fraction[:-1], \ |
126 |
expo + len(intpart) - 1 |
127 |
s = sign + intpart |
128 |
if digs > 0: s = s + '.' + fraction |
129 |
e = `abs(expo)`
|
130 |
e = '0'*(3-len(e)) + e |
131 |
if expo < 0: e = '-' + e |
132 |
else: e = '+' + e |
133 |
return s + 'e' + e |
134 |
|
135 |
def test(): |
136 |
"""Interactive test run."""
|
137 |
try:
|
138 |
while 1: |
139 |
x, digs = input('Enter (x, digs): ') |
140 |
print x, fix(x, digs), sci(x, digs)
|
141 |
except (EOFError, KeyboardInterrupt): |
142 |
pass
|