You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

145 lines
4.6 KiB

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