diff --git a/decimal.js b/decimal.js index aa6526a..8eabf43 100644 --- a/decimal.js +++ b/decimal.js @@ -744,14 +744,13 @@ Ctor.precision = pr + 6; Ctor.rounding = 1; - - x = x.asin(); - halfPi = getPi(Ctor, pr + 4, rm).times(0.5); + + x = (new Ctor(1)).minus(x).div(x.plus(1)).sqrt().atan(); Ctor.precision = pr; Ctor.rounding = rm; - return halfPi.minus(x); + return x.times(2); }; diff --git a/decimal.mjs b/decimal.mjs index 909300c..293ee8e 100644 --- a/decimal.mjs +++ b/decimal.mjs @@ -741,13 +741,12 @@ P.inverseCosine = P.acos = function () { Ctor.precision = pr + 6; Ctor.rounding = 1; - x = x.asin(); - halfPi = getPi(Ctor, pr + 4, rm).times(0.5); + x = (new Ctor(1)).minus(x).div(x.plus(1)).sqrt().atan(); Ctor.precision = pr; Ctor.rounding = rm; - return halfPi.minus(x); + return x.times(2); }; diff --git a/test/hypothesis/.gitignore b/test/hypothesis/.gitignore new file mode 100644 index 0000000..6694234 --- /dev/null +++ b/test/hypothesis/.gitignore @@ -0,0 +1,3 @@ +__pycache__ +.hypothesis +.ipynb_checkpoints diff --git a/test/hypothesis/error_hunt.py b/test/hypothesis/error_hunt.py new file mode 100644 index 0000000..af9df0b --- /dev/null +++ b/test/hypothesis/error_hunt.py @@ -0,0 +1,99 @@ +from decimal import Decimal, getcontext +import json +import subprocess +import hypothesis +from hypothesis import given, settings +from hypothesis.strategies import decimals, integers, tuples +from mpmath import mp +import pytest + +mp.prec = 500 +getcontext().prec = 14 + +node = subprocess.Popen( + ["node", "evaluate.mjs"], stdout=subprocess.PIPE, stdin=subprocess.PIPE +) + + +def get_decimal(func: str, args: list, config: dict): + arg = json.dumps({"func": func, "args": args, "config": config}).encode() + b"\r" + node.stdin.write(arg) + node.stdin.flush() + return Decimal(node.stdout.readline().strip().decode()) + + +def assert_matches(x, mpfunc, jsfunc=None): + if jsfunc is None: + jsfunc = mpfunc + y = Decimal(str(getattr(mp, mpfunc)(x))) * Decimal("1.0") + z = get_decimal(jsfunc, [str(x)], {"precision": 14}) + assert y == z + + +@pytest.mark.parametrize("fn", "sin cos tan atan asinh".split()) +@given( + x=tuples( + decimals( + allow_nan=False, allow_infinity=False, min_value=-1, max_value=1, places=14 + ), + integers(min_value=-99, max_value=99), + ).map(lambda tup: tup[0] * Decimal(10) ** tup[1]) +) +@settings(max_examples=100_000) +def test_matches(x, fn): + assert_matches(x, fn) + + +@pytest.mark.parametrize("fn", "ln log10 sqrt".split()) +@given( + x=tuples( + decimals( + allow_nan=False, + allow_infinity=False, + min_value=1e-13, + max_value=1, + places=14, + ), + integers(min_value=-99, max_value=99), + ).map(lambda tup: tup[0] * Decimal(10) ** tup[1]) +) +@settings(max_examples=100_000) +def test_positive_domain(x, fn): + assert_matches(x, fn) + + +@pytest.mark.parametrize("fn", "asin acos atanh".split()) +@given( + x=decimals( + allow_nan=False, allow_infinity=False, min_value=-1, max_value=1, places=14 + ) +) +@settings(max_examples=100_000) +def test_inverse_trig(x, fn): + assert_matches(x, fn) + + +@pytest.mark.parametrize("fn", "sinh cosh tanh exp".split()) +@given( + x=tuples( + decimals( + allow_nan=False, allow_infinity=False, min_value=-1, max_value=1, places=14 + ), + integers(min_value=-99, max_value=3), + ).map(lambda tup: tup[0] * Decimal(10) ** tup[1]) +) +@settings(max_examples=100_000) +def test_small_domain(x, fn): + assert_matches(x, fn) + +@given( + x=tuples( + decimals( + allow_nan=False, allow_infinity=False, min_value=1, max_value=10, places=14 + ), + integers(min_value=0, max_value=99), + ).map(lambda tup: tup[0] * Decimal(10) ** tup[1]) +) +@settings(max_examples=100_000) +def test_acosh(x): + assert_matches(x, 'acosh') \ No newline at end of file diff --git a/test/hypothesis/evaluate.mjs b/test/hypothesis/evaluate.mjs new file mode 100644 index 0000000..bf31f47 --- /dev/null +++ b/test/hypothesis/evaluate.mjs @@ -0,0 +1,26 @@ +// listen for test cases, and provide the results. +// give JSON of the test case, receive the result +// Example: +// > {"func":"tan", "args":["12.5"], "config":{"precision":8}} +// -0.066468242 + + +import {Decimal} from '../../decimal.mjs'; +import {createInterface} from 'readline'; + +const readline = createInterface({ + input: process.stdin, + output: process.stdout +}); + +readline.on("close", () => {console.log('\n'); process.exit(0);}); + +readline.on("line", (line) => { + if (line) { + const {func, args, config} = JSON.parse(line); + config.defaults = true; + Decimal.set(config); + const result = Decimal[func](...args); + console.log(result); + } +}); \ No newline at end of file diff --git a/test/hypothesis/requirements.txt b/test/hypothesis/requirements.txt new file mode 100644 index 0000000..ab25b9f --- /dev/null +++ b/test/hypothesis/requirements.txt @@ -0,0 +1,3 @@ +hypothesis +mpmath +node \ No newline at end of file diff --git a/test/modules/acos.js b/test/modules/acos.js index 66a9dbb..b2ef48c 100644 --- a/test/modules/acos.js +++ b/test/modules/acos.js @@ -68,6 +68,10 @@ T('acos', function () { t('0.41923186648524998285699814886092351368793359300547574', 42, 3, '1.13819724675000902666504291062053681280681'); t('-0.19508761025300975791021816036', 27, 1, '1.76714310275532020878366926'); t('0.0623252416', 19, 0, '1.508430664767542249'); + t('0.9999999297625', 14, 4, '0.00037479994883195'); + t('0.99999999467518', 14, 4, '0.0001031970930281'); + t('0.9999999999999999995', 25, 4, '0.000000001000000000000000000041667'); + t('0.99999999999999999999995', 30, 4, '0.0000000000100000000000000000000000416667'); /* t('0.95', 6, 5, '0.31756'); diff --git a/test/modules/sin.js b/test/modules/sin.js index d90ea99..05036fe 100644 --- a/test/modules/sin.js +++ b/test/modules/sin.js @@ -119,4 +119,5 @@ T('sin', function () { t('22011131111011111.111111611113111111111151111111', 143, 2, '0.82582504036277799386306063085803210583251158969990606609364360685569588545519071481543672724620118406694191888115120286393881609546697317692404'); t('996270725099515169352424536636186062915113219400094989.8763797268889422850038402633796294758036260533902551191769915343780424028900449342752548782035', 46, 2, '0.6613706114081017074779805460666900787572253475'); t('0.780360750628373', 37, 5, '0.7035358359376557390803090830882458906'); + t('5900', 14, 4, '0.088879123681079'); });