0001 """General floating point formatting functions. 0002 0003 Functions: 0004 fix(x, digits_behind) 0005 sci(x, digits_behind) 0006 0007 Each takes a number or a string and a number of digits as arguments. 0008 0009 Parameters: 0010 x: number to be formatted; or a string resembling a number 0011 digits_behind: number of digits behind the decimal point 0012 """ 0013 0014 import re 0015 0016 __all__ = ["fix","sci","NotANumber"] 0017 0018 # Compiled regular expression to "decode" a number 0019 decoder = re.compile(r'^([-+]?)0*(\d*)((?:\.\d*)?)(([eE][-+]?\d+)?)$') 0020 # \0 the whole thing 0021 # \1 leading sign or empty 0022 # \2 digits left of decimal point 0023 # \3 fraction (empty or begins with point) 0024 # \4 exponent part (empty or begins with 'e' or 'E') 0025 0026 try: 0027 class NotANumber(ValueError): 0028 pass 0029 except TypeError: 0030 NotANumber = 'fpformat.NotANumber' 0031 0032 def extract(s): 0033 """Return (sign, intpart, fraction, expo) or raise an exception: 0034 sign is '+' or '-' 0035 intpart is 0 or more digits beginning with a nonzero 0036 fraction is 0 or more digits 0037 expo is an integer""" 0038 res = decoder.match(s) 0039 if res is None: raise NotANumber, s 0040 sign, intpart, fraction, exppart = res.group(1,2,3,4) 0041 if sign == '+': sign = '' 0042 if fraction: fraction = fraction[1:] 0043 if exppart: expo = int(exppart[1:]) 0044 else: expo = 0 0045 return sign, intpart, fraction, expo 0046 0047 def unexpo(intpart, fraction, expo): 0048 """Remove the exponent by changing intpart and fraction.""" 0049 if expo > 0: # Move the point left 0050 f = len(fraction) 0051 intpart, fraction = intpart + fraction[:expo], fraction[expo:] 0052 if expo > f: 0053 intpart = intpart + '0'*(expo-f) 0054 elif expo < 0: # Move the point right 0055 i = len(intpart) 0056 intpart, fraction = intpart[:expo], intpart[expo:] + fraction 0057 if expo < -i: 0058 fraction = '0'*(-expo-i) + fraction 0059 return intpart, fraction 0060 0061 def roundfrac(intpart, fraction, digs): 0062 """Round or extend the fraction to size digs.""" 0063 f = len(fraction) 0064 if f <= digs: 0065 return intpart, fraction + '0'*(digs-f) 0066 i = len(intpart) 0067 if i+digs < 0: 0068 return '0'*-digs, '' 0069 total = intpart + fraction 0070 nextdigit = total[i+digs] 0071 if nextdigit >= '5': # Hard case: increment last digit, may have carry! 0072 n = i + digs - 1 0073 while n >= 0: 0074 if total[n] != '9': break 0075 n = n-1 0076 else: 0077 total = '0' + total 0078 i = i+1 0079 n = 0 0080 total = total[:n] + chr(ord(total[n]) + 1) + '0'*(len(total)-n-1) 0081 intpart, fraction = total[:i], total[i:] 0082 if digs >= 0: 0083 return intpart, fraction[:digs] 0084 else: 0085 return intpart[:digs] + '0'*-digs, '' 0086 0087 def fix(x, digs): 0088 """Format x as [-]ddd.ddd with 'digs' digits after the point 0089 and at least one digit before. 0090 If digs <= 0, the point is suppressed.""" 0091 if type(x) != type(''): x = repr(x) 0092 try: 0093 sign, intpart, fraction, expo = extract(x) 0094 except NotANumber: 0095 return x 0096 intpart, fraction = unexpo(intpart, fraction, expo) 0097 intpart, fraction = roundfrac(intpart, fraction, digs) 0098 while intpart and intpart[0] == '0': intpart = intpart[1:] 0099 if intpart == '': intpart = '0' 0100 if digs > 0: return sign + intpart + '.' + fraction 0101 else: return sign + intpart 0102 0103 def sci(x, digs): 0104 """Format x as [-]d.dddE[+-]ddd with 'digs' digits after the point 0105 and exactly one digit before. 0106 If digs is <= 0, one digit is kept and the point is suppressed.""" 0107 if type(x) != type(''): x = repr(x) 0108 sign, intpart, fraction, expo = extract(x) 0109 if not intpart: 0110 while fraction and fraction[0] == '0': 0111 fraction = fraction[1:] 0112 expo = expo - 1 0113 if fraction: 0114 intpart, fraction = fraction[0], fraction[1:] 0115 expo = expo - 1 0116 else: 0117 intpart = '0' 0118 else: 0119 expo = expo + len(intpart) - 1 0120 intpart, fraction = intpart[0], intpart[1:] + fraction 0121 digs = max(0, digs) 0122 intpart, fraction = roundfrac(intpart, fraction, digs) 0123 if len(intpart) > 1: 0124 intpart, fraction, expo = \ 0125 intpart[0], intpart[1:] + fraction[:-1], \ 0126 expo + len(intpart) - 1 0127 s = sign + intpart 0128 if digs > 0: s = s + '.' + fraction 0129 e = repr(abs(expo)) 0130 e = '0'*(3-len(e)) + e 0131 if expo < 0: e = '-' + e 0132 else: e = '+' + e 0133 return s + 'e' + e 0134 0135 def test(): 0136 """Interactive test run.""" 0137 try: 0138 while 1: 0139 x, digs = input('Enter (x, digs): ') 0140 print x, fix(x, digs), sci(x, digs) 0141 except (EOFError, KeyboardInterrupt): 0142 pass 0143
Generated by PyXR 0.9.4