1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | Lib/fpformat.py
"""General floating point formatting functions. Functions: fix(x, digits_behind) sci(x, digits_behind) Each takes a number or a string and a number of digits as arguments. Parameters: x: number to be formatted; or a string resembling a number digits_behind: number of digits behind the decimal point """ from warnings import warnpy3k warnpy3k("the fpformat module has been removed in Python 3.0", stacklevel=2) del warnpy3k import re __all__ = ["fix","sci","NotANumber"] # Compiled regular expression to "decode" a number decoder = re.compile(r'^([-+]?)0*(\d*)((?:\.\d*)?)(([eE][-+]?\d+)?)$') # \0 the whole thing # \1 leading sign or empty # \2 digits left of decimal point # \3 fraction (empty or begins with point) # \4 exponent part (empty or begins with 'e' or 'E') try: class NotANumber(ValueError): pass except TypeError: NotANumber = 'fpformat.NotANumber' def extract(s): """Return (sign, intpart, fraction, expo) or raise an exception: sign is '+' or '-' intpart is 0 or more digits beginning with a nonzero fraction is 0 or more digits expo is an integer""" res = decoder.match(s) if res is None: raise NotANumber, s sign, intpart, fraction, exppart = res.group(1,2,3,4) if sign == '+': sign = '' if fraction: fraction = fraction[1:] if exppart: expo = int(exppart[1:]) else: expo = 0 return sign, intpart, fraction, expo def unexpo(intpart, fraction, expo): """Remove the exponent by changing intpart and fraction.""" if expo > 0: # Move the point left f = len(fraction) intpart, fraction = intpart + fraction[:expo], fraction[expo:] if expo > f: intpart = intpart + '0'*(expo-f) elif expo < 0: # Move the point right i = len(intpart) intpart, fraction = intpart[:expo], intpart[expo:] + fraction if expo < -i: fraction = '0'*(-expo-i) + fraction return intpart, fraction def roundfrac(intpart, fraction, digs): """Round or extend the fraction to size digs.""" f = len(fraction) if f <= digs: return intpart, fraction + '0'*(digs-f) i = len(intpart) if i+digs < 0: return '0'*-digs, '' total = intpart + fraction nextdigit = total[i+digs] if nextdigit >= '5': # Hard case: increment last digit, may have carry! n = i + digs - 1 while n >= 0: if total[n] != '9': break n = n-1 else: total = '0' + total i = i+1 n = 0 total = total[:n] + chr(ord(total[n]) + 1) + '0'*(len(total)-n-1) intpart, fraction = total[:i], total[i:] if digs >= 0: return intpart, fraction[:digs] else: return intpart[:digs] + '0'*-digs, '' def fix(x, digs): """Format x as [-]ddd.ddd with 'digs' digits after the point and at least one digit before. If digs <= 0, the point is suppressed.""" if type(x) != type(''): x = repr(x) try: sign, intpart, fraction, expo = extract(x) except NotANumber: return x intpart, fraction = unexpo(intpart, fraction, expo) intpart, fraction = roundfrac(intpart, fraction, digs) while intpart and intpart[0] == '0': intpart = intpart[1:] if intpart == '': intpart = '0' if digs > 0: return sign + intpart + '.' + fraction else: return sign + intpart def sci(x, digs): """Format x as [-]d.dddE[+-]ddd with 'digs' digits after the point and exactly one digit before. If digs is <= 0, one digit is kept and the point is suppressed.""" if type(x) != type(''): x = repr(x) sign, intpart, fraction, expo = extract(x) if not intpart: while fraction and fraction[0] == '0': fraction = fraction[1:] expo = expo - 1 if fraction: intpart, fraction = fraction[0], fraction[1:] expo = expo - 1 else: intpart = '0' else: expo = expo + len(intpart) - 1 intpart, fraction = intpart[0], intpart[1:] + fraction digs = max(0, digs) intpart, fraction = roundfrac(intpart, fraction, digs) if len(intpart) > 1: intpart, fraction, expo = \ intpart[0], intpart[1:] + fraction[:-1], \ expo + len(intpart) - 1 s = sign + intpart if digs > 0: s = s + '.' + fraction e = repr(abs(expo)) e = '0'*(3-len(e)) + e if expo < 0: e = '-' + e else: e = '+' + e return s + 'e' + e def test(): """Interactive test run.""" try: while 1: x, digs = input('Enter (x, digs): ') print x, fix(x, digs), sci(x, digs) except (EOFError, KeyboardInterrupt): pass |