930_dmath.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. """Python bindings for the new per-function dmath corpus.
  2. Run from the repository root: main tests/930_dmath.py [function]
  3. C-only functions (exp2, exp10, sincos, isnormal, fmin, fmax) have their own
  4. groups in test_dmath. The same frozen cases are used here, without decimal
  5. parsing or signed-integer overflow during test setup.
  6. """
  7. import math
  8. import stdc
  9. import sys
  10. _byte_order_probe = stdc.UInt(1)
  11. _little_endian = stdc.read_bytes(stdc.addressof(_byte_order_probe), 4)[0] == 1
  12. _hex_digits = '0123456789abcdef'
  13. def from_bits(text):
  14. raw = bytes([int(text[i:i + 2], 16) for i in range(0, 16, 2)])
  15. if _little_endian:
  16. raw = raw[::-1]
  17. cell = stdc.Double(0.0)
  18. stdc.memcpy(stdc.addressof(cell), raw, 8)
  19. return cell.value
  20. def to_bits(x):
  21. cell = stdc.Double(x)
  22. raw = stdc.read_bytes(stdc.addressof(cell), 8)
  23. if _little_endian:
  24. raw = raw[::-1]
  25. return ''.join([_hex_digits[b >> 4] + _hex_digits[b & 15] for b in raw])
  26. def check_result(actual, expected, context):
  27. if expected == 'nan':
  28. assert math.isnan(actual), context
  29. elif expected == '+inf' or expected == '-inf':
  30. assert math.isinf(actual), context
  31. assert (actual > 0.0) == (expected == '+inf'), context
  32. else:
  33. assert to_bits(actual) == expected, (context, to_bits(actual), expected)
  34. def raises_type_error(function, *args):
  35. try:
  36. function(*args)
  37. except TypeError:
  38. return
  39. raise AssertionError('expected TypeError')
  40. def check_cases(name, function, arity=1, kind='float'):
  41. with open('tests/dmath/cases/' + name + '.txt', 'rt') as handle:
  42. lines = handle.read().split('\n')
  43. count = 0
  44. for line in lines:
  45. if not line or line.startswith('#'):
  46. continue
  47. fields = line.split()
  48. label, x_word, y_word, expected, auxiliary = fields[:5]
  49. assert expected != 'PENDING', (name, label, 'unreviewed case')
  50. x, y = from_bits(x_word), from_bits(y_word)
  51. result = function(x) if arity == 1 else function(x, y)
  52. context = (name, label, x_word, y_word)
  53. if kind == 'bool':
  54. assert type(result) is bool, context
  55. assert result == bool(int(expected, 16)), context
  56. elif kind == 'rounding':
  57. want = from_bits(expected) if len(expected) == 16 else math.nan
  58. if want >= from_bits('c3e0000000000000') and want < from_bits('43e0000000000000'):
  59. assert type(result) is int, context
  60. assert result == int(want), context
  61. else:
  62. assert type(result) is float, context
  63. check_result(result, expected, context)
  64. elif kind == 'pair':
  65. assert type(result) is tuple and len(result) == 2, context
  66. check_result(result[0], expected, context)
  67. check_result(result[1], auxiliary, context)
  68. else:
  69. assert type(result) is float, context
  70. check_result(result, expected, context)
  71. count += 1
  72. assert count > 0, name
  73. raises_type_error(function)
  74. raises_type_error(function, 'not a number')
  75. raises_type_error(function, 1.0, 2.0, 3.0)
  76. if arity == 2:
  77. raises_type_error(function, 1.0, 'not a number')
  78. print('PASS math.' + name + ': ' + str(count) + ' named cases')
  79. # Classification.
  80. def test_isfinite():
  81. check_cases('isfinite', math.isfinite, kind='bool')
  82. def test_isinf():
  83. check_cases('isinf', math.isinf, kind='bool')
  84. def test_isnan():
  85. check_cases('isnan', math.isnan, kind='bool')
  86. # Sign operations require exact finite results; NaNs are checked by classification.
  87. def test_fabs():
  88. check_cases('fabs', math.fabs)
  89. def test_copysign():
  90. check_cases('copysign', math.copysign, 2)
  91. # Integral return types and exact int64 arguments belong to the binding layer.
  92. def check_integer_arguments(function):
  93. for x in [9007199254741027, -9007199254741027, 9223372036854775793,
  94. -9223372036854775807 - 1]:
  95. assert type(function(x)) is int
  96. assert function(x) == x
  97. def test_ceil():
  98. check_cases('ceil', math.ceil, kind='rounding')
  99. check_integer_arguments(math.ceil)
  100. def test_floor():
  101. check_cases('floor', math.floor, kind='rounding')
  102. check_integer_arguments(math.floor)
  103. def test_trunc():
  104. check_cases('trunc', math.trunc, kind='rounding')
  105. check_integer_arguments(math.trunc)
  106. def test_modf():
  107. check_cases('modf', math.modf, kind='pair')
  108. assert math.modf(83) == (0.0, 83.0)
  109. def test_fmod():
  110. check_cases('fmod', math.fmod, 2)
  111. # Roots.
  112. def test_sqrt():
  113. check_cases('sqrt', math.sqrt)
  114. def test_cbrt():
  115. check_cases('cbrt', math.cbrt)
  116. # Exponential and logarithmic functions.
  117. def test_exp():
  118. check_cases('exp', math.exp)
  119. def test_pow():
  120. check_cases('pow', math.pow, 2)
  121. def test_log():
  122. check_cases('log', math.log)
  123. def test_log2():
  124. check_cases('log2', math.log2)
  125. def test_log10():
  126. check_cases('log10', math.log10)
  127. def test_log_base():
  128. check_cases('log_base', math.log, 2)
  129. # Trigonometry.
  130. def test_sin():
  131. check_cases('sin', math.sin)
  132. def test_cos():
  133. check_cases('cos', math.cos)
  134. def test_tan():
  135. check_cases('tan', math.tan)
  136. # Inverse trigonometry.
  137. def test_asin():
  138. check_cases('asin', math.asin)
  139. def test_acos():
  140. check_cases('acos', math.acos)
  141. def test_atan():
  142. check_cases('atan', math.atan)
  143. def test_atan2():
  144. check_cases('atan2', math.atan2, 2)
  145. groups = [
  146. ('classification', [test_isfinite, test_isinf, test_isnan]),
  147. ('sign', [test_fabs, test_copysign]),
  148. ('rounding_and_remainder', [test_ceil, test_floor, test_trunc, test_modf, test_fmod]),
  149. ('roots', [test_sqrt, test_cbrt]),
  150. ('exponentials', [test_exp, test_pow]),
  151. ('logarithms', [test_log, test_log2, test_log10, test_log_base]),
  152. ('trigonometry', [test_sin, test_cos, test_tan]),
  153. ('inverse_trigonometry', [test_asin, test_acos, test_atan, test_atan2]),
  154. ]
  155. selected = sys.argv[1] if len(sys.argv) > 1 else None
  156. ran = 0
  157. for category, functions in groups:
  158. for function in functions:
  159. if selected is None or function.__name__ == 'test_' + selected:
  160. function()
  161. ran += 1
  162. assert ran > 0, selected