python-2.5.2/win32/Lib/fpformat.py
changeset 0 ae805ac0140d
equal deleted inserted replaced
-1:000000000000 0:ae805ac0140d
       
     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 = repr(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 = repr(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 = repr(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