Search code examples
javascriptnode.jsmathbignumdecimal.js

Precision issue with Decimal.js tan


I have been using Decimal.js to increase the precision of my function that calculates the mth positive root of a = tan(a) through trial and error. It works, however it returns a "Precision limit exceeded" error for nTan(504) (would return 4.4934... to 505 digits) and greater.

var Decimal = require("decimal.js");
var fs = require("fs");

function nTan (acc, m) {
    var test = [1], acc = (parseInt(acc) || 15) + 1;
    Decimal.set({precision: acc});
    var n = new Decimal(fs.readFileSync("result.txt", "utf-8") || 4.4).toString();

    while (n.length + test.length - 2 < acc) {
        var dec = (new Decimal(n + test.join("")));
        if (dec.tan().cmp(n + test.join("")) >= 0) {
            test[test.length - 1]--;
            test.push(1);
        } else test[test.length - 1]++;

        if (test[test.length - 1] == 10) { test[test.length - 1] = 9; test.push(1); }
    }

    return (new Decimal(n + test.slice(0, -1).join(""))).plus(Math.PI * (parseInt(m) || 0)).toString();
}

My question(s) are:

  1. Why won't Decimal.js calculate past 504 digits when it advertises the capacity for up to and including 1e+9 digits?
  2. Is there an alternative node or JS API that would support this program to a greater precision?

Solution

  • 1000000000 is the maximum permitted value for the decimal.js precision setting, but that does not mean that the trigonometric methods can return a result to that number of significant digits.

    The limit to the precision of the trigonometric methods is determined by the precision of the value of Pi in the source code. It is hard-coded in the decimal.js file as the string variable PI, and has a precision of 1025 digits.

    This means that the precision limit for the cos, sin and tan methods is up to about 1000 digits, but the actual figure depends on the precision of the argument passed to them. To calculate the actual figure use

    maximum_result_precision = 1000 - argument_precision

    For example, the following both work fine

    Decimal.set({precision: 991}).tan(123456789);
    
    Decimal.set({precision: 9}).tan(991_digit_number);
    

    as, for each, the result precision plus the argument precision, i.e. 991 + 9 and 9 + 991, is less than or equal to 1000.

    This is why your program fails when you try and calculate the tan of an argument with more than 500 digits to a precision of more than 500 digits.

    To do it would require Pi to a higher precision - and that can only be done, and can be done simply, by editing the value of PI in the source code, i.e. add more digits to it. The time taken by the methods will then be the limiting factor.

    I am the library's author and I need to add this to its documentation.