Statistics
| Revision:

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