FastMathCalc.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      https://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. /*
  18.  * This is not the original file distributed by the Apache Software Foundation
  19.  * It has been modified by the Hipparchus project
  20.  */
  21. package org.hipparchus.util;

  22. import java.io.PrintStream;

  23. /**
  24.  * Class used to compute the classical functions tables.
  25.  */
  26. class FastMathCalc {

  27.     /**
  28.      * 0x40000000 - used to split a double into two parts, both with the low order bits cleared.
  29.      * Equivalent to 2^30.
  30.      */
  31.     private static final long HEX_40000000 = 0x40000000L; // 1073741824L

  32.     /** Factorial table, for Taylor series expansions. 0!, 1!, 2!, ... 19! */
  33.     private static final double[] FACT = {
  34.         +1.0d,                        // 0
  35.         +1.0d,                        // 1
  36.         +2.0d,                        // 2
  37.         +6.0d,                        // 3
  38.         +24.0d,                       // 4
  39.         +120.0d,                      // 5
  40.         +720.0d,                      // 6
  41.         +5040.0d,                     // 7
  42.         +40320.0d,                    // 8
  43.         +362880.0d,                   // 9
  44.         +3628800.0d,                  // 10
  45.         +39916800.0d,                 // 11
  46.         +479001600.0d,                // 12
  47.         +6227020800.0d,               // 13
  48.         +87178291200.0d,              // 14
  49.         +1307674368000.0d,            // 15
  50.         +20922789888000.0d,           // 16
  51.         +355687428096000.0d,          // 17
  52.         +6402373705728000.0d,         // 18
  53.         +121645100408832000.0d,       // 19
  54.         };

  55.     /** Coefficients for slowLog. */
  56.     private static final double[][] LN_SPLIT_COEF = {
  57.         {2.0, 0.0},
  58.         {0.6666666269302368, 3.9736429850260626E-8},
  59.         {0.3999999761581421, 2.3841857910019882E-8},
  60.         {0.2857142686843872, 1.7029898543501842E-8},
  61.         {0.2222222089767456, 1.3245471311735498E-8},
  62.         {0.1818181574344635, 2.4384203044354907E-8},
  63.         {0.1538461446762085, 9.140260083262505E-9},
  64.         {0.13333332538604736, 9.220590270857665E-9},
  65.         {0.11764700710773468, 1.2393345855018391E-8},
  66.         {0.10526403784751892, 8.251545029714408E-9},
  67.         {0.0952233225107193, 1.2675934823758863E-8},
  68.         {0.08713622391223907, 1.1430250008909141E-8},
  69.         {0.07842259109020233, 2.404307984052299E-9},
  70.         {0.08371849358081818, 1.176342548272881E-8},
  71.         {0.030589580535888672, 1.2958646899018938E-9},
  72.         {0.14982303977012634, 1.225743062930824E-8},
  73.     };

  74.     /** Table start declaration. */
  75.     private static final String TABLE_START_DECL = "    {";

  76.     /** Table end declaration. */
  77.     private static final String TABLE_END_DECL   = "    };";

  78.     /**
  79.      * Private Constructor.
  80.      */
  81.     private FastMathCalc() {
  82.     }

  83.     /** Build the sine and cosine tables.
  84.      * @param SINE_TABLE_A table of the most significant part of the sines
  85.      * @param SINE_TABLE_B table of the least significant part of the sines
  86.      * @param COSINE_TABLE_A table of the most significant part of the cosines
  87.      * @param COSINE_TABLE_B table of the most significant part of the cosines
  88.      * @param SINE_TABLE_LEN length of the tables
  89.      * @param TANGENT_TABLE_A table of the most significant part of the tangents
  90.      * @param TANGENT_TABLE_B table of the most significant part of the tangents
  91.      */
  92.     @SuppressWarnings("unused")
  93.     private static void buildSinCosTables(double[] SINE_TABLE_A, double[] SINE_TABLE_B,
  94.                                           double[] COSINE_TABLE_A, double[] COSINE_TABLE_B,
  95.                                           int SINE_TABLE_LEN, double[] TANGENT_TABLE_A, double[] TANGENT_TABLE_B) {
  96.         final double[] result = new double[2];

  97.         /* Use taylor series for 0 <= x <= 6/8 */
  98.         for (int i = 0; i < 7; i++) {
  99.             double x = i / 8.0;

  100.             slowSin(x, result);
  101.             SINE_TABLE_A[i] = result[0];
  102.             SINE_TABLE_B[i] = result[1];

  103.             slowCos(x, result);
  104.             COSINE_TABLE_A[i] = result[0];
  105.             COSINE_TABLE_B[i] = result[1];
  106.         }

  107.         /* Use angle addition formula to complete table to 13/8, just beyond pi/2 */
  108.         for (int i = 7; i < SINE_TABLE_LEN; i++) {
  109.             double[] xs = new double[2];
  110.             double[] ys = new double[2];
  111.             double[] as = new double[2];
  112.             double[] bs = new double[2];
  113.             double[] temps = new double[2];

  114.             if ( (i & 1) == 0) {
  115.                 // Even, use double angle
  116.                 xs[0] = SINE_TABLE_A[i/2];
  117.                 xs[1] = SINE_TABLE_B[i/2];
  118.                 ys[0] = COSINE_TABLE_A[i/2];
  119.                 ys[1] = COSINE_TABLE_B[i/2];

  120.                 /* compute sine */
  121.                 splitMult(xs, ys, result);
  122.                 SINE_TABLE_A[i] = result[0] * 2.0;
  123.                 SINE_TABLE_B[i] = result[1] * 2.0;

  124.                 /* Compute cosine */
  125.                 splitMult(ys, ys, as);
  126.                 splitMult(xs, xs, temps);
  127.                 temps[0] = -temps[0];
  128.                 temps[1] = -temps[1];
  129.                 splitAdd(as, temps, result);
  130.                 COSINE_TABLE_A[i] = result[0];
  131.                 COSINE_TABLE_B[i] = result[1];
  132.             } else {
  133.                 xs[0] = SINE_TABLE_A[i/2];
  134.                 xs[1] = SINE_TABLE_B[i/2];
  135.                 ys[0] = COSINE_TABLE_A[i/2];
  136.                 ys[1] = COSINE_TABLE_B[i/2];
  137.                 as[0] = SINE_TABLE_A[i/2+1];
  138.                 as[1] = SINE_TABLE_B[i/2+1];
  139.                 bs[0] = COSINE_TABLE_A[i/2+1];
  140.                 bs[1] = COSINE_TABLE_B[i/2+1];

  141.                 /* compute sine */
  142.                 splitMult(xs, bs, temps);
  143.                 splitMult(ys, as, result);
  144.                 splitAdd(result, temps, result);
  145.                 SINE_TABLE_A[i] = result[0];
  146.                 SINE_TABLE_B[i] = result[1];

  147.                 /* Compute cosine */
  148.                 splitMult(ys, bs, result);
  149.                 splitMult(xs, as, temps);
  150.                 temps[0] = -temps[0];
  151.                 temps[1] = -temps[1];
  152.                 splitAdd(result, temps, result);
  153.                 COSINE_TABLE_A[i] = result[0];
  154.                 COSINE_TABLE_B[i] = result[1];
  155.             }
  156.         }

  157.         /* Compute tangent = sine/cosine */
  158.         for (int i = 0; i < SINE_TABLE_LEN; i++) {
  159.             double[] xs = new double[2];
  160.             double[] ys = new double[2];
  161.             double[] as = new double[2];

  162.             as[0] = COSINE_TABLE_A[i];
  163.             as[1] = COSINE_TABLE_B[i];

  164.             splitReciprocal(as, ys);

  165.             xs[0] = SINE_TABLE_A[i];
  166.             xs[1] = SINE_TABLE_B[i];

  167.             splitMult(xs, ys, as);

  168.             TANGENT_TABLE_A[i] = as[0];
  169.             TANGENT_TABLE_B[i] = as[1];
  170.         }

  171.     }

  172.     /**
  173.      *  For x between 0 and pi/4 compute cosine using Talor series
  174.      *  cos(x) = 1 - x^2/2! + x^4/4! ...
  175.      * @param x number from which cosine is requested
  176.      * @param result placeholder where to put the result in extended precision
  177.      * (may be null)
  178.      * @return cos(x)
  179.      */
  180.     static double slowCos(final double x, final double[] result) {

  181.         final double[] xs = new double[2];
  182.         final double[] ys = new double[2];
  183.         final double[] facts = new double[2];
  184.         final double[] as = new double[2];
  185.         split(x, xs);
  186.         ys[0] = ys[1] = 0.0;

  187.         for (int i = FACT.length-1; i >= 0; i--) {
  188.             splitMult(xs, ys, as);
  189.             ys[0] = as[0]; ys[1] = as[1];

  190.             if ( (i & 1) != 0) { // skip odd entries
  191.                 continue;
  192.             }

  193.             split(FACT[i], as);
  194.             splitReciprocal(as, facts);

  195.             if ( (i & 2) != 0 ) { // alternate terms are negative
  196.                 facts[0] = -facts[0];
  197.                 facts[1] = -facts[1];
  198.             }

  199.             splitAdd(ys, facts, as);
  200.             ys[0] = as[0]; ys[1] = as[1];
  201.         }

  202.         if (result != null) {
  203.             result[0] = ys[0];
  204.             result[1] = ys[1];
  205.         }

  206.         return ys[0] + ys[1];
  207.     }

  208.     /**
  209.      * For x between 0 and pi/4 compute sine using Taylor expansion:
  210.      * sin(x) = x - x^3/3! + x^5/5! - x^7/7! ...
  211.      * @param x number from which sine is requested
  212.      * @param result placeholder where to put the result in extended precision
  213.      * (may be null)
  214.      * @return sin(x)
  215.      */
  216.     static double slowSin(final double x, final double[] result) {
  217.         final double[] xs = new double[2];
  218.         final double[] ys = new double[2];
  219.         final double[] facts = new double[2];
  220.         final double[] as = new double[2];
  221.         split(x, xs);
  222.         ys[0] = ys[1] = 0.0;

  223.         for (int i = FACT.length-1; i >= 0; i--) {
  224.             splitMult(xs, ys, as);
  225.             ys[0] = as[0]; ys[1] = as[1];

  226.             if ( (i & 1) == 0) { // Ignore even numbers
  227.                 continue;
  228.             }

  229.             split(FACT[i], as);
  230.             splitReciprocal(as, facts);

  231.             if ( (i & 2) != 0 ) { // alternate terms are negative
  232.                 facts[0] = -facts[0];
  233.                 facts[1] = -facts[1];
  234.             }

  235.             splitAdd(ys, facts, as);
  236.             ys[0] = as[0]; ys[1] = as[1];
  237.         }

  238.         if (result != null) {
  239.             result[0] = ys[0];
  240.             result[1] = ys[1];
  241.         }

  242.         return ys[0] + ys[1];
  243.     }


  244.     /**
  245.      *  For x between 0 and 1, returns exp(x), uses extended precision
  246.      *  @param x argument of exponential
  247.      *  @param result placeholder where to place exp(x) split in two terms
  248.      *  for extra precision (i.e. exp(x) = result[0] + result[1]
  249.      *  @return exp(x)
  250.      */
  251.     static double slowexp(final double x, final double[] result) {
  252.         final double[] xs = new double[2];
  253.         final double[] ys = new double[2];
  254.         final double[] facts = new double[2];
  255.         final double[] as = new double[2];
  256.         split(x, xs);
  257.         ys[0] = ys[1] = 0.0;

  258.         for (int i = FACT.length-1; i >= 0; i--) {
  259.             splitMult(xs, ys, as);
  260.             ys[0] = as[0];
  261.             ys[1] = as[1];

  262.             split(FACT[i], as);
  263.             splitReciprocal(as, facts);

  264.             splitAdd(ys, facts, as);
  265.             ys[0] = as[0];
  266.             ys[1] = as[1];
  267.         }

  268.         if (result != null) {
  269.             result[0] = ys[0];
  270.             result[1] = ys[1];
  271.         }

  272.         return ys[0] + ys[1];
  273.     }

  274.     /** Compute split[0], split[1] such that their sum is equal to d,
  275.      * and split[0] has its 30 least significant bits as zero.
  276.      * @param d number to split
  277.      * @param split placeholder where to place the result
  278.      */
  279.     private static void split(final double d, final double[] split) {
  280.         if (d < 8e298 && d > -8e298) {
  281.             final double a = d * HEX_40000000;
  282.             split[0] = (d + a) - a;
  283.             split[1] = d - split[0];
  284.         } else {
  285.             final double a = d * 9.31322574615478515625E-10;
  286.             split[0] = (d + a - d) * HEX_40000000;
  287.             split[1] = d - split[0];
  288.         }
  289.     }

  290.     /** Recompute a split.
  291.      * @param a input/out array containing the split, changed
  292.      * on output
  293.      */
  294.     private static void resplit(final double[] a) {
  295.         final double c = a[0] + a[1];
  296.         final double d = -(c - a[0] - a[1]);

  297.         if (c < 8e298 && c > -8e298) { // MAGIC NUMBER
  298.             double z = c * HEX_40000000;
  299.             a[0] = (c + z) - z;
  300.             a[1] = c - a[0] + d;
  301.         } else {
  302.             double z = c * 9.31322574615478515625E-10;
  303.             a[0] = (c + z - c) * HEX_40000000;
  304.             a[1] = c - a[0] + d;
  305.         }
  306.     }

  307.     /** Multiply two numbers in split form.
  308.      * @param a first term of multiplication
  309.      * @param b second term of multiplication
  310.      * @param ans placeholder where to put the result
  311.      */
  312.     private static void splitMult(double[] a, double[] b, double[] ans) {
  313.         ans[0] = a[0] * b[0];
  314.         ans[1] = a[0] * b[1] + a[1] * b[0] + a[1] * b[1];

  315.         /* Resplit */
  316.         resplit(ans);
  317.     }

  318.     /** Add two numbers in split form.
  319.      * @param a first term of addition
  320.      * @param b second term of addition
  321.      * @param ans placeholder where to put the result
  322.      */
  323.     private static void splitAdd(final double[] a, final double[] b, final double[] ans) {
  324.         ans[0] = a[0] + b[0];
  325.         ans[1] = a[1] + b[1];

  326.         resplit(ans);
  327.     }

  328.     /** Compute the reciprocal of in.  Use the following algorithm.
  329.      *  in = c + d.
  330.      *  want to find x + y such that x+y = 1/(c+d) and x is much
  331.      *  larger than y and x has several zero bits on the right.
  332.      *
  333.      *  Set b = 1/(2^22),  a = 1 - b.  Thus (a+b) = 1.
  334.      *  Use following identity to compute (a+b)/(c+d)
  335.      *
  336.      *  (a+b)/(c+d)  =   a/c   +    (bc - ad) / (c^2 + cd)
  337.      *  set x = a/c  and y = (bc - ad) / (c^2 + cd)
  338.      *  This will be close to the right answer, but there will be
  339.      *  some rounding in the calculation of X.  So by carefully
  340.      *  computing 1 - (c+d)(x+y) we can compute an error and
  341.      *  add that back in.   This is done carefully so that terms
  342.      *  of similar size are subtracted first.
  343.      *  @param in initial number, in split form
  344.      *  @param result placeholder where to put the result
  345.      */
  346.     static void splitReciprocal(final double[] in, final double[] result) {
  347.         final double b = 1.0/4194304.0;
  348.         final double a = 1.0 - b;

  349.         if (in[0] == 0.0) {
  350.             in[0] = in[1];
  351.             in[1] = 0.0;
  352.         }

  353.         result[0] = a / in[0];
  354.         result[1] = (b*in[0]-a*in[1]) / (in[0]*in[0] + in[0]*in[1]);

  355.         if (result[1] != result[1]) { // can happen if result[1] is NAN
  356.             result[1] = 0.0;
  357.         }

  358.         /* Resplit */
  359.         resplit(result);

  360.         for (int i = 0; i < 2; i++) {
  361.             /* this may be overkill, probably once is enough */
  362.             double err = 1.0 - result[0] * in[0] - result[0] * in[1] -
  363.             result[1] * in[0] - result[1] * in[1];
  364.             /*err = 1.0 - err; */
  365.             err *= result[0] + result[1];
  366.             /*printf("err = %16e\n", err); */
  367.             result[1] += err;
  368.         }
  369.     }

  370.     /** Compute (a[0] + a[1]) * (b[0] + b[1]) in extended precision.
  371.      * @param a first term of the multiplication
  372.      * @param b second term of the multiplication
  373.      * @param result placeholder where to put the result
  374.      */
  375.     private static void quadMult(final double[] a, final double[] b, final double[] result) {
  376.         final double[] xs = new double[2];
  377.         final double[] ys = new double[2];
  378.         final double[] zs = new double[2];

  379.         /* a[0] * b[0] */
  380.         split(a[0], xs);
  381.         split(b[0], ys);
  382.         splitMult(xs, ys, zs);

  383.         result[0] = zs[0];
  384.         result[1] = zs[1];

  385.         /* a[0] * b[1] */
  386.         split(b[1], ys);
  387.         splitMult(xs, ys, zs);

  388.         double tmp = result[0] + zs[0];
  389.         result[1] -= tmp - result[0] - zs[0];
  390.         result[0] = tmp;
  391.         tmp = result[0] + zs[1];
  392.         result[1] -= tmp - result[0] - zs[1];
  393.         result[0] = tmp;

  394.         /* a[1] * b[0] */
  395.         split(a[1], xs);
  396.         split(b[0], ys);
  397.         splitMult(xs, ys, zs);

  398.         tmp = result[0] + zs[0];
  399.         result[1] -= tmp - result[0] - zs[0];
  400.         result[0] = tmp;
  401.         tmp = result[0] + zs[1];
  402.         result[1] -= tmp - result[0] - zs[1];
  403.         result[0] = tmp;

  404.         /* a[1] * b[0] */
  405.         split(a[1], xs);
  406.         split(b[1], ys);
  407.         splitMult(xs, ys, zs);

  408.         tmp = result[0] + zs[0];
  409.         result[1] -= tmp - result[0] - zs[0];
  410.         result[0] = tmp;
  411.         tmp = result[0] + zs[1];
  412.         result[1] -= tmp - result[0] - zs[1];
  413.         result[0] = tmp;
  414.     }

  415.     /** Compute exp(p) for a integer p in extended precision.
  416.      * @param p integer whose exponential is requested
  417.      * @param result placeholder where to put the result in extended precision
  418.      * @return exp(p) in standard precision (equal to result[0] + result[1])
  419.      */
  420.     static double expint(int p, final double[] result) {
  421.         //double x = M_E;
  422.         final double[] xs = new double[2];
  423.         final double[] as = new double[2];
  424.         final double[] ys = new double[2];
  425.         //split(x, xs);
  426.         //xs[1] = (double)(2.7182818284590452353602874713526625L - xs[0]);
  427.         //xs[0] = 2.71827697753906250000;
  428.         //xs[1] = 4.85091998273542816811e-06;
  429.         //xs[0] = Double.longBitsToDouble(0x4005bf0800000000L);
  430.         //xs[1] = Double.longBitsToDouble(0x3ed458a2bb4a9b00L);

  431.         /* E */
  432.         xs[0] = 2.718281828459045;
  433.         xs[1] = 1.4456468917292502E-16;

  434.         split(1.0, ys);

  435.         while (p > 0) {
  436.             if ((p & 1) != 0) {
  437.                 quadMult(ys, xs, as);
  438.                 ys[0] = as[0]; ys[1] = as[1];
  439.             }

  440.             quadMult(xs, xs, as);
  441.             xs[0] = as[0]; xs[1] = as[1];

  442.             p >>= 1;
  443.         }

  444.         if (result != null) {
  445.             result[0] = ys[0];
  446.             result[1] = ys[1];

  447.             resplit(result);
  448.         }

  449.         return ys[0] + ys[1];
  450.     }
  451.     /** xi in the range of [1, 2].
  452.      *                                3        5        7
  453.      *      x+1           /          x        x        x          \
  454.      *  ln ----- =   2 *  |  x  +   ----  +  ----  +  ---- + ...  |
  455.      *      1-x           \          3        5        7          /
  456.      *
  457.      * So, compute a Remez approximation of the following function
  458.      *
  459.      *  ln ((sqrt(x)+1)/(1-sqrt(x)))  /  x
  460.      *
  461.      * This will be an even function with only positive coefficents.
  462.      * x is in the range [0 - 1/3].
  463.      *
  464.      * Transform xi for input to the above function by setting
  465.      * x = (xi-1)/(xi+1).   Input to the polynomial is x^2, then
  466.      * the result is multiplied by x.
  467.      * @param xi number from which log is requested
  468.      * @return log(xi)
  469.      */
  470.     static double[] slowLog(double xi) {
  471.         double[] x = new double[2];
  472.         double[] x2 = new double[2];
  473.         double[] y = new double[2];
  474.         double[] a = new double[2];

  475.         split(xi, x);

  476.         /* Set X = (x-1)/(x+1) */
  477.         x[0] += 1.0;
  478.         resplit(x);
  479.         splitReciprocal(x, a);
  480.         x[0] -= 2.0;
  481.         resplit(x);
  482.         splitMult(x, a, y);
  483.         x[0] = y[0];
  484.         x[1] = y[1];

  485.         /* Square X -> X2*/
  486.         splitMult(x, x, x2);


  487.         //x[0] -= 1.0;
  488.         //resplit(x);

  489.         y[0] = LN_SPLIT_COEF[LN_SPLIT_COEF.length-1][0];
  490.         y[1] = LN_SPLIT_COEF[LN_SPLIT_COEF.length-1][1];

  491.         for (int i = LN_SPLIT_COEF.length-2; i >= 0; i--) {
  492.             splitMult(y, x2, a);
  493.             y[0] = a[0];
  494.             y[1] = a[1];
  495.             splitAdd(y, LN_SPLIT_COEF[i], a);
  496.             y[0] = a[0];
  497.             y[1] = a[1];
  498.         }

  499.         splitMult(y, x, a);
  500.         y[0] = a[0];
  501.         y[1] = a[1];

  502.         return y;
  503.     }


  504.     /**
  505.      * Print an array.
  506.      * @param out text output stream where output should be printed
  507.      * @param name array name
  508.      * @param expectedLen expected length of the array
  509.      * @param array2d array data
  510.      */
  511.     static void printarray(PrintStream out, String name, int expectedLen, double[][] array2d) {
  512.         out.println(name);
  513.         MathUtils.checkDimension(expectedLen, array2d.length);
  514.         out.println(TABLE_START_DECL + " ");
  515.         int i = 0;
  516.         for(double[] array : array2d) { // "double array[]" causes PMD parsing error
  517.             out.print("        {");
  518.             for(double d : array) { // assume inner array has very few entries
  519.                 out.printf("%-25.25s", format(d)); // multiple entries per line
  520.             }
  521.             out.println("}, // " + i++);
  522.         }
  523.         out.println(TABLE_END_DECL);
  524.     }

  525.     /**
  526.      * Print an array.
  527.      * @param out text output stream where output should be printed
  528.      * @param name array name
  529.      * @param expectedLen expected length of the array
  530.      * @param array array data
  531.      */
  532.     static void printarray(PrintStream out, String name, int expectedLen, double[] array) {
  533.         out.println(name + "=");
  534.         MathUtils.checkDimension(expectedLen, array.length);
  535.         out.println(TABLE_START_DECL);
  536.         for(double d : array){
  537.             out.printf("        %s%n", format(d)); // one entry per line
  538.         }
  539.         out.println(TABLE_END_DECL);
  540.     }

  541.     /** Format a double.
  542.      * @param d double number to format
  543.      * @return formatted number
  544.      */
  545.     static String format(double d) {
  546.         if (d != d) {
  547.             return "Double.NaN,";
  548.         } else {
  549.             return ((d >= 0) ? "+" : "") + d + "d,";
  550.         }
  551.     }

  552. }