| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229 |
- """Python bindings for the new per-function dmath corpus.
- Run from the repository root: main tests/930_dmath.py [function]
- C-only functions (exp2, exp10, sincos, isnormal, fmin, fmax) have their own
- groups in test_dmath. The same frozen cases are used here, without decimal
- parsing or signed-integer overflow during test setup.
- """
- import math
- import stdc
- import sys
- _byte_order_probe = stdc.UInt(1)
- _little_endian = stdc.read_bytes(stdc.addressof(_byte_order_probe), 4)[0] == 1
- _hex_digits = '0123456789abcdef'
- def from_bits(text):
- raw = bytes([int(text[i:i + 2], 16) for i in range(0, 16, 2)])
- if _little_endian:
- raw = raw[::-1]
- cell = stdc.Double(0.0)
- stdc.memcpy(stdc.addressof(cell), raw, 8)
- return cell.value
- def to_bits(x):
- cell = stdc.Double(x)
- raw = stdc.read_bytes(stdc.addressof(cell), 8)
- if _little_endian:
- raw = raw[::-1]
- return ''.join([_hex_digits[b >> 4] + _hex_digits[b & 15] for b in raw])
- def check_result(actual, expected, context):
- if expected == 'nan':
- assert math.isnan(actual), context
- elif expected == '+inf' or expected == '-inf':
- assert math.isinf(actual), context
- assert (actual > 0.0) == (expected == '+inf'), context
- else:
- assert to_bits(actual) == expected, (context, to_bits(actual), expected)
- def raises_type_error(function, *args):
- try:
- function(*args)
- except TypeError:
- return
- raise AssertionError('expected TypeError')
- def check_cases(name, function, arity=1, kind='float'):
- with open('tests/dmath/cases/' + name + '.txt', 'rt') as handle:
- lines = handle.read().split('\n')
- count = 0
- for line in lines:
- if not line or line.startswith('#'):
- continue
- fields = line.split()
- label, x_word, y_word, expected, auxiliary = fields[:5]
- assert expected != 'PENDING', (name, label, 'unreviewed case')
- x, y = from_bits(x_word), from_bits(y_word)
- result = function(x) if arity == 1 else function(x, y)
- context = (name, label, x_word, y_word)
- if kind == 'bool':
- assert type(result) is bool, context
- assert result == bool(int(expected, 16)), context
- elif kind == 'rounding':
- want = from_bits(expected) if len(expected) == 16 else math.nan
- if want >= from_bits('c3e0000000000000') and want < from_bits('43e0000000000000'):
- assert type(result) is int, context
- assert result == int(want), context
- else:
- assert type(result) is float, context
- check_result(result, expected, context)
- elif kind == 'pair':
- assert type(result) is tuple and len(result) == 2, context
- check_result(result[0], expected, context)
- check_result(result[1], auxiliary, context)
- else:
- assert type(result) is float, context
- check_result(result, expected, context)
- count += 1
- assert count > 0, name
- raises_type_error(function)
- raises_type_error(function, 'not a number')
- raises_type_error(function, 1.0, 2.0, 3.0)
- if arity == 2:
- raises_type_error(function, 1.0, 'not a number')
- print('PASS math.' + name + ': ' + str(count) + ' named cases')
- # Classification.
- def test_isfinite():
- check_cases('isfinite', math.isfinite, kind='bool')
- def test_isinf():
- check_cases('isinf', math.isinf, kind='bool')
- def test_isnan():
- check_cases('isnan', math.isnan, kind='bool')
- # Sign operations require exact finite results; NaNs are checked by classification.
- def test_fabs():
- check_cases('fabs', math.fabs)
- def test_copysign():
- check_cases('copysign', math.copysign, 2)
- # Integral return types and exact int64 arguments belong to the binding layer.
- def check_integer_arguments(function):
- for x in [9007199254741027, -9007199254741027, 9223372036854775793,
- -9223372036854775807 - 1]:
- assert type(function(x)) is int
- assert function(x) == x
- def test_ceil():
- check_cases('ceil', math.ceil, kind='rounding')
- check_integer_arguments(math.ceil)
- def test_floor():
- check_cases('floor', math.floor, kind='rounding')
- check_integer_arguments(math.floor)
- def test_trunc():
- check_cases('trunc', math.trunc, kind='rounding')
- check_integer_arguments(math.trunc)
- def test_modf():
- check_cases('modf', math.modf, kind='pair')
- assert math.modf(83) == (0.0, 83.0)
- def test_fmod():
- check_cases('fmod', math.fmod, 2)
- # Roots.
- def test_sqrt():
- check_cases('sqrt', math.sqrt)
- def test_cbrt():
- check_cases('cbrt', math.cbrt)
- # Exponential and logarithmic functions.
- def test_exp():
- check_cases('exp', math.exp)
- def test_pow():
- check_cases('pow', math.pow, 2)
- def test_log():
- check_cases('log', math.log)
- def test_log2():
- check_cases('log2', math.log2)
- def test_log10():
- check_cases('log10', math.log10)
- def test_log_base():
- check_cases('log_base', math.log, 2)
- # Trigonometry.
- def test_sin():
- check_cases('sin', math.sin)
- def test_cos():
- check_cases('cos', math.cos)
- def test_tan():
- check_cases('tan', math.tan)
- # Inverse trigonometry.
- def test_asin():
- check_cases('asin', math.asin)
- def test_acos():
- check_cases('acos', math.acos)
- def test_atan():
- check_cases('atan', math.atan)
- def test_atan2():
- check_cases('atan2', math.atan2, 2)
- groups = [
- ('classification', [test_isfinite, test_isinf, test_isnan]),
- ('sign', [test_fabs, test_copysign]),
- ('rounding_and_remainder', [test_ceil, test_floor, test_trunc, test_modf, test_fmod]),
- ('roots', [test_sqrt, test_cbrt]),
- ('exponentials', [test_exp, test_pow]),
- ('logarithms', [test_log, test_log2, test_log10, test_log_base]),
- ('trigonometry', [test_sin, test_cos, test_tan]),
- ('inverse_trigonometry', [test_asin, test_acos, test_atan, test_atan2]),
- ]
- selected = sys.argv[1] if len(sys.argv) > 1 else None
- ran = 0
- for category, functions in groups:
- for function in functions:
- if selected is None or function.__name__ == 'test_' + selected:
- function()
- ran += 1
- assert ran > 0, selected
|