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