
org.elasticsearch.h3.FastMath Maven / Gradle / Ivy
/*
* @notice
* Copyright 2012 Jeff Hain
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* =============================================================================
* Notice of fdlibm package this program is partially derived from:
*
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
*
* Developed at SunSoft, a Sun Microsystems, Inc. business.
* Permission to use, copy, modify, and distribute this
* software is freely granted, provided that this notice
* is preserved.
* =============================================================================
*
* This code sourced from:
* https://github.com/jeffhain/jafama/blob/d7d2a7659e96e148d827acc24cf385b872cda365/src/main/java/net/jafama/FastMath.java
*/
package org.elasticsearch.h3;
/**
* This file is forked from https://github.com/jeffhain/jafama. In particular, it forks the following file:
* https://github.com/jeffhain/jafama/blob/master/src/main/java/net/jafama/FastMath.java
*
* It modifies the original implementation by removing not needed methods leaving the following trigonometric function:
*
* - {@link #cos(double)}
* - {@link #sin(double)}
* - {@link #tan(double)}
* - {@link #acos(double)}
* - {@link #asin(double)}
* - {@link #atan(double)}
* - {@link #atan2(double, double)}
*
*/
final class FastMath {
/*
* For trigonometric functions, use of look-up tables and Taylor-Lagrange formula
* with 4 derivatives (more take longer to compute and don't add much accuracy,
* less require larger tables (which use more memory, take more time to initialize,
* and are slower to access (at least on the machine they were developed on))).
*
* For angles reduction of cos/sin/tan functions:
* - for small values, instead of reducing angles, and then computing the best index
* for look-up tables, we compute this index right away, and use it for reduction,
* - for large values, treatments derived from fdlibm package are used, as done in
* java.lang.Math. They are faster but still "slow", so if you work with
* large numbers and need speed over accuracy for them, you might want to use
* normalizeXXXFast treatments before your function, or modify cos/sin/tan
* so that they call the fast normalization treatments instead of the accurate ones.
* NB: If an angle is huge (like PI*1e20), in double precision format its last digits
* are zeros, which most likely is not the case for the intended value, and doing
* an accurate reduction on a very inaccurate value is most likely pointless.
* But it gives some sort of coherence that could be needed in some cases.
*
* Multiplication on double appears to be about as fast (or not much slower) than call
* to [], and regrouping some doubles in a private class, to use
* index only once, does not seem to speed things up, so:
* - for uniformly tabulated values, to retrieve the parameter corresponding to
* an index, we recompute it rather than using an array to store it,
* - for cos/sin, we recompute derivatives divided by (multiplied by inverse of)
* factorial each time, rather than storing them in arrays.
*
* Lengths of look-up tables are usually of the form 2^n+1, for their values to be
* of the form ( * k/2^n, k in 0 .. 2^n), so that particular values
* (PI/2, etc.) are "exactly" computed, as well as for other reasons.
*
* Most math treatments I could find on the web, including "fast" ones,
* usually take care of special cases (NaN, etc.) at the beginning, and
* then deal with the general case, which adds a useless overhead for the
* general (and common) case. In this class, special cases are only dealt
* with when needed, and if the general case does not already handle them.
*/
// --------------------------------------------------------------------------
// GENERAL CONSTANTS
// --------------------------------------------------------------------------
private static final double ONE_DIV_F2 = 1 / 2.0;
private static final double ONE_DIV_F3 = 1 / 6.0;
private static final double ONE_DIV_F4 = 1 / 24.0;
private static final double TWO_POW_24 = Double.longBitsToDouble(0x4170000000000000L);
private static final double TWO_POW_N24 = Double.longBitsToDouble(0x3E70000000000000L);
private static final double TWO_POW_66 = Double.longBitsToDouble(0x4410000000000000L);
private static final int MIN_DOUBLE_EXPONENT = -1074;
private static final int MAX_DOUBLE_EXPONENT = 1023;
// --------------------------------------------------------------------------
// CONSTANTS FOR NORMALIZATIONS
// --------------------------------------------------------------------------
/*
* Table of constants for 1/(2*PI), 282 Hex digits (enough for normalizing doubles).
* 1/(2*PI) approximation = sum of ONE_OVER_TWOPI_TAB[i]*2^(-24*(i+1)).
*/
private static final double[] ONE_OVER_TWOPI_TAB = {
0x28BE60,
0xDB9391,
0x054A7F,
0x09D5F4,
0x7D4D37,
0x7036D8,
0xA5664F,
0x10E410,
0x7F9458,
0xEAF7AE,
0xF1586D,
0xC91B8E,
0x909374,
0xB80192,
0x4BBA82,
0x746487,
0x3F877A,
0xC72C4A,
0x69CFBA,
0x208D7D,
0x4BAED1,
0x213A67,
0x1C09AD,
0x17DF90,
0x4E6475,
0x8E60D4,
0xCE7D27,
0x2117E2,
0xEF7E4A,
0x0EC7FE,
0x25FFF7,
0x816603,
0xFBCBC4,
0x62D682,
0x9B47DB,
0x4D9FB3,
0xC9F2C2,
0x6DD3D1,
0x8FD9A7,
0x97FA8B,
0x5D49EE,
0xB1FAF9,
0x7C5ECF,
0x41CE7D,
0xE294A4,
0xBA9AFE,
0xD7EC47 };
/*
* Constants for 2*PI. Only the 23 most significant bits of each mantissa are used.
* 2*PI approximation = sum of TWOPI_TAB.
*/
private static final double TWOPI_TAB0 = Double.longBitsToDouble(0x401921FB40000000L);
private static final double TWOPI_TAB1 = Double.longBitsToDouble(0x3E94442D00000000L);
private static final double TWOPI_TAB2 = Double.longBitsToDouble(0x3D18469880000000L);
private static final double TWOPI_TAB3 = Double.longBitsToDouble(0x3B98CC5160000000L);
private static final double TWOPI_TAB4 = Double.longBitsToDouble(0x3A101B8380000000L);
private static final double INVPIO2 = Double.longBitsToDouble(0x3FE45F306DC9C883L); // 6.36619772367581382433e-01 53 bits of 2/pi
private static final double PIO2_HI = Double.longBitsToDouble(0x3FF921FB54400000L); // 1.57079632673412561417e+00 first 33 bits of pi/2
private static final double PIO2_LO = Double.longBitsToDouble(0x3DD0B4611A626331L); // 6.07710050650619224932e-11 pi/2 - PIO2_HI
private static final double INVTWOPI = INVPIO2 / 4;
private static final double TWOPI_HI = 4 * PIO2_HI;
private static final double TWOPI_LO = 4 * PIO2_LO;
// fdlibm uses 2^19*PI/2 here, but we normalize with % 2*PI instead of % PI/2,
// and we can bear some more error.
private static final double NORMALIZE_ANGLE_MAX_MEDIUM_DOUBLE = StrictMath.pow(2, 20) * (2 * Math.PI);
// --------------------------------------------------------------------------
// CONSTANTS AND TABLES FOR COS, SIN
// --------------------------------------------------------------------------
private static final int SIN_COS_TABS_SIZE = (1 << getTabSizePower(11)) + 1;
private static final double SIN_COS_DELTA_HI = TWOPI_HI / (SIN_COS_TABS_SIZE - 1);
private static final double SIN_COS_DELTA_LO = TWOPI_LO / (SIN_COS_TABS_SIZE - 1);
private static final double SIN_COS_INDEXER = 1 / (SIN_COS_DELTA_HI + SIN_COS_DELTA_LO);
private static final double[] sinTab = new double[SIN_COS_TABS_SIZE];
private static final double[] cosTab = new double[SIN_COS_TABS_SIZE];
// Max abs value for fast modulo, above which we use regular angle normalization.
// This value must be < (Integer.MAX_VALUE / SIN_COS_INDEXER), to stay in range of int type.
// The higher it is, the higher the error, but also the faster it is for lower values.
// If you set it to ((Integer.MAX_VALUE / SIN_COS_INDEXER) * 0.99), worse accuracy on double range is about 1e-10.
private static final double SIN_COS_MAX_VALUE_FOR_INT_MODULO = ((Integer.MAX_VALUE >> 9) / SIN_COS_INDEXER) * 0.99;
// --------------------------------------------------------------------------
// CONSTANTS AND TABLES FOR TAN
// --------------------------------------------------------------------------
// We use the following formula:
// 1) tan(-x) = -tan(x)
// 2) tan(x) = 1/tan(PI/2-x)
// ---> we only have to compute tan(x) on [0,A] with PI/4<=A= 45deg, and supposed to be >= 51.4deg, as fdlibm code is not
// supposed to work with values inferior to that (51.4deg is about
// (PI/2-Double.longBitsToDouble(0x3FE5942800000000L))).
private static final double TAN_MAX_VALUE_FOR_TABS = Math.toRadians(77.0);
private static final int TAN_TABS_SIZE = (int) ((TAN_MAX_VALUE_FOR_TABS / (Math.PI / 2)) * (TAN_VIRTUAL_TABS_SIZE - 1)) + 1;
private static final double TAN_DELTA_HI = PIO2_HI / (TAN_VIRTUAL_TABS_SIZE - 1);
private static final double TAN_DELTA_LO = PIO2_LO / (TAN_VIRTUAL_TABS_SIZE - 1);
private static final double TAN_INDEXER = 1 / (TAN_DELTA_HI + TAN_DELTA_LO);
private static final double[] tanTab = new double[TAN_TABS_SIZE];
private static final double[] tanDer1DivF1Tab = new double[TAN_TABS_SIZE];
private static final double[] tanDer2DivF2Tab = new double[TAN_TABS_SIZE];
private static final double[] tanDer3DivF3Tab = new double[TAN_TABS_SIZE];
private static final double[] tanDer4DivF4Tab = new double[TAN_TABS_SIZE];
// Max abs value for fast modulo, above which we use regular angle normalization.
// This value must be < (Integer.MAX_VALUE / TAN_INDEXER), to stay in range of int type.
// The higher it is, the higher the error, but also the faster it is for lower values.
private static final double TAN_MAX_VALUE_FOR_INT_MODULO = (((Integer.MAX_VALUE >> 9) / TAN_INDEXER) * 0.99);
// --------------------------------------------------------------------------
// CONSTANTS AND TABLES FOR ACOS, ASIN
// --------------------------------------------------------------------------
// We use the following formula:
// 1) acos(x) = PI/2 - asin(x)
// 2) asin(-x) = -asin(x)
// ---> we only have to compute asin(x) on [0,1].
// For values not close to +-1, we use look-up tables;
// for values near +-1, we use code derived from fdlibm.
// Supposed to be >= sin(77.2deg), as fdlibm code is supposed to work with values > 0.975,
// but seems to work well enough as long as value >= sin(25deg).
private static final double ASIN_MAX_VALUE_FOR_TABS = StrictMath.sin(Math.toRadians(73.0));
private static final int ASIN_TABS_SIZE = (1 << getTabSizePower(13)) + 1;
private static final double ASIN_DELTA = ASIN_MAX_VALUE_FOR_TABS / (ASIN_TABS_SIZE - 1);
private static final double ASIN_INDEXER = 1 / ASIN_DELTA;
private static final double[] asinTab = new double[ASIN_TABS_SIZE];
private static final double[] asinDer1DivF1Tab = new double[ASIN_TABS_SIZE];
private static final double[] asinDer2DivF2Tab = new double[ASIN_TABS_SIZE];
private static final double[] asinDer3DivF3Tab = new double[ASIN_TABS_SIZE];
private static final double[] asinDer4DivF4Tab = new double[ASIN_TABS_SIZE];
private static final double ASIN_MAX_VALUE_FOR_POWTABS = StrictMath.sin(Math.toRadians(88.6));
private static final int ASIN_POWTABS_POWER = 84;
private static final double ASIN_POWTABS_ONE_DIV_MAX_VALUE = 1 / ASIN_MAX_VALUE_FOR_POWTABS;
private static final int ASIN_POWTABS_SIZE = (1 << getTabSizePower(12)) + 1;
private static final int ASIN_POWTABS_SIZE_MINUS_ONE = ASIN_POWTABS_SIZE - 1;
private static final double[] asinParamPowTab = new double[ASIN_POWTABS_SIZE];
private static final double[] asinPowTab = new double[ASIN_POWTABS_SIZE];
private static final double[] asinDer1DivF1PowTab = new double[ASIN_POWTABS_SIZE];
private static final double[] asinDer2DivF2PowTab = new double[ASIN_POWTABS_SIZE];
private static final double[] asinDer3DivF3PowTab = new double[ASIN_POWTABS_SIZE];
private static final double[] asinDer4DivF4PowTab = new double[ASIN_POWTABS_SIZE];
private static final double ASIN_PIO2_HI = Double.longBitsToDouble(0x3FF921FB54442D18L); // 1.57079632679489655800e+00
private static final double ASIN_PIO2_LO = Double.longBitsToDouble(0x3C91A62633145C07L); // 6.12323399573676603587e-17
private static final double ASIN_PS0 = Double.longBitsToDouble(0x3fc5555555555555L); // 1.66666666666666657415e-01
private static final double ASIN_PS1 = Double.longBitsToDouble(0xbfd4d61203eb6f7dL); // -3.25565818622400915405e-01
private static final double ASIN_PS2 = Double.longBitsToDouble(0x3fc9c1550e884455L); // 2.01212532134862925881e-01
private static final double ASIN_PS3 = Double.longBitsToDouble(0xbfa48228b5688f3bL); // -4.00555345006794114027e-02
private static final double ASIN_PS4 = Double.longBitsToDouble(0x3f49efe07501b288L); // 7.91534994289814532176e-04
private static final double ASIN_PS5 = Double.longBitsToDouble(0x3f023de10dfdf709L); // 3.47933107596021167570e-05
private static final double ASIN_QS1 = Double.longBitsToDouble(0xc0033a271c8a2d4bL); // -2.40339491173441421878e+00
private static final double ASIN_QS2 = Double.longBitsToDouble(0x40002ae59c598ac8L); // 2.02094576023350569471e+00
private static final double ASIN_QS3 = Double.longBitsToDouble(0xbfe6066c1b8d0159L); // -6.88283971605453293030e-01
private static final double ASIN_QS4 = Double.longBitsToDouble(0x3fb3b8c5b12e9282L); // 7.70381505559019352791e-02
// --------------------------------------------------------------------------
// CONSTANTS AND TABLES FOR ATAN
// --------------------------------------------------------------------------
// We use the formula atan(-x) = -atan(x)
// ---> we only have to compute atan(x) on [0,+infinity[.
// For values corresponding to angles not close to +-PI/2, we use look-up tables;
// for values corresponding to angles near +-PI/2, we use code derived from fdlibm.
// Supposed to be >= tan(67.7deg), as fdlibm code is supposed to work with values > 2.4375.
private static final double ATAN_MAX_VALUE_FOR_TABS = StrictMath.tan(Math.toRadians(74.0));
private static final int ATAN_TABS_SIZE = (1 << getTabSizePower(12)) + 1;
private static final double ATAN_DELTA = ATAN_MAX_VALUE_FOR_TABS / (ATAN_TABS_SIZE - 1);
private static final double ATAN_INDEXER = 1 / ATAN_DELTA;
private static final double[] atanTab = new double[ATAN_TABS_SIZE];
private static final double[] atanDer1DivF1Tab = new double[ATAN_TABS_SIZE];
private static final double[] atanDer2DivF2Tab = new double[ATAN_TABS_SIZE];
private static final double[] atanDer3DivF3Tab = new double[ATAN_TABS_SIZE];
private static final double[] atanDer4DivF4Tab = new double[ATAN_TABS_SIZE];
private static final double ATAN_HI3 = Double.longBitsToDouble(0x3ff921fb54442d18L); // 1.57079632679489655800e+00 atan(inf)hi
private static final double ATAN_LO3 = Double.longBitsToDouble(0x3c91a62633145c07L); // 6.12323399573676603587e-17 atan(inf)lo
private static final double ATAN_AT0 = Double.longBitsToDouble(0x3fd555555555550dL); // 3.33333333333329318027e-01
private static final double ATAN_AT1 = Double.longBitsToDouble(0xbfc999999998ebc4L); // -1.99999999998764832476e-01
private static final double ATAN_AT2 = Double.longBitsToDouble(0x3fc24924920083ffL); // 1.42857142725034663711e-01
private static final double ATAN_AT3 = Double.longBitsToDouble(0xbfbc71c6fe231671L); // -1.11111104054623557880e-01
private static final double ATAN_AT4 = Double.longBitsToDouble(0x3fb745cdc54c206eL); // 9.09088713343650656196e-02
private static final double ATAN_AT5 = Double.longBitsToDouble(0xbfb3b0f2af749a6dL); // -7.69187620504482999495e-02
private static final double ATAN_AT6 = Double.longBitsToDouble(0x3fb10d66a0d03d51L); // 6.66107313738753120669e-02
private static final double ATAN_AT7 = Double.longBitsToDouble(0xbfadde2d52defd9aL); // -5.83357013379057348645e-02
private static final double ATAN_AT8 = Double.longBitsToDouble(0x3fa97b4b24760debL); // 4.97687799461593236017e-02
private static final double ATAN_AT9 = Double.longBitsToDouble(0xbfa2b4442c6a6c2fL); // -3.65315727442169155270e-02
private static final double ATAN_AT10 = Double.longBitsToDouble(0x3f90ad3ae322da11L); // 1.62858201153657823623e-02
// --------------------------------------------------------------------------
// TABLE FOR POWERS OF TWO
// --------------------------------------------------------------------------
private static final double[] twoPowTab = new double[(MAX_DOUBLE_EXPONENT - MIN_DOUBLE_EXPONENT) + 1];
// --------------------------------------------------------------------------
// PUBLIC TREATMENTS
// --------------------------------------------------------------------------
/**
* @param angle Angle in radians.
* @return Angle cosine.
*/
public static double cos(double angle) {
angle = Math.abs(angle);
if (angle > SIN_COS_MAX_VALUE_FOR_INT_MODULO) {
// Faster than using normalizeZeroTwoPi.
angle = remainderTwoPi(angle);
if (angle < 0.0) {
angle += 2 * Math.PI;
}
}
// index: possibly outside tables range.
int index = (int) (angle * SIN_COS_INDEXER + 0.5);
double delta = (angle - index * SIN_COS_DELTA_HI) - index * SIN_COS_DELTA_LO;
// Making sure index is within tables range.
// Last value of each table is the same than first, so we ignore it (tabs size minus one) for modulo.
index &= (SIN_COS_TABS_SIZE - 2); // index % (SIN_COS_TABS_SIZE-1)
double indexCos = cosTab[index];
double indexSin = sinTab[index];
return indexCos + delta * (-indexSin + delta * (-indexCos * ONE_DIV_F2 + delta * (indexSin * ONE_DIV_F3 + delta * indexCos
* ONE_DIV_F4)));
}
/**
* @param angle Angle in radians.
* @return Angle sine.
*/
public static double sin(double angle) {
boolean negateResult;
if (angle < 0.0) {
angle = -angle;
negateResult = true;
} else {
negateResult = false;
}
if (angle > SIN_COS_MAX_VALUE_FOR_INT_MODULO) {
// Faster than using normalizeZeroTwoPi.
angle = remainderTwoPi(angle);
if (angle < 0.0) {
angle += 2 * Math.PI;
}
}
int index = (int) (angle * SIN_COS_INDEXER + 0.5);
double delta = (angle - index * SIN_COS_DELTA_HI) - index * SIN_COS_DELTA_LO;
index &= (SIN_COS_TABS_SIZE - 2); // index % (SIN_COS_TABS_SIZE-1)
double indexSin = sinTab[index];
double indexCos = cosTab[index];
double result = indexSin + delta * (indexCos + delta * (-indexSin * ONE_DIV_F2 + delta * (-indexCos * ONE_DIV_F3 + delta * indexSin
* ONE_DIV_F4)));
return negateResult ? -result : result;
}
/**
* @param angle Angle in radians.
* @return Angle tangent.
*/
public static double tan(double angle) {
if (Math.abs(angle) > TAN_MAX_VALUE_FOR_INT_MODULO) {
// Faster than using normalizeMinusHalfPiHalfPi.
angle = remainderTwoPi(angle);
if (angle < -Math.PI / 2) {
angle += Math.PI;
} else if (angle > Math.PI / 2) {
angle -= Math.PI;
}
}
boolean negateResult;
if (angle < 0.0) {
angle = -angle;
negateResult = true;
} else {
negateResult = false;
}
int index = (int) (angle * TAN_INDEXER + 0.5);
double delta = (angle - index * TAN_DELTA_HI) - index * TAN_DELTA_LO;
// index modulo PI, i.e. 2*(virtual tab size minus one).
index &= (2 * (TAN_VIRTUAL_TABS_SIZE - 1) - 1); // index % (2*(TAN_VIRTUAL_TABS_SIZE-1))
// Here, index is in [0,2*(TAN_VIRTUAL_TABS_SIZE-1)-1], i.e. indicates an angle in [0,PI[.
if (index > (TAN_VIRTUAL_TABS_SIZE - 1)) {
index = (2 * (TAN_VIRTUAL_TABS_SIZE - 1)) - index;
delta = -delta;
negateResult = negateResult == false;
}
double result;
if (index < TAN_TABS_SIZE) {
result = tanTab[index] + delta * (tanDer1DivF1Tab[index] + delta * (tanDer2DivF2Tab[index] + delta * (tanDer3DivF3Tab[index]
+ delta * tanDer4DivF4Tab[index])));
} else { // angle in ]TAN_MAX_VALUE_FOR_TABS,TAN_MAX_VALUE_FOR_INT_MODULO], or angle is NaN
// Using tan(angle) == 1/tan(PI/2-angle) formula: changing angle (index and delta), and inverting.
index = (TAN_VIRTUAL_TABS_SIZE - 1) - index;
result = 1 / (tanTab[index] - delta * (tanDer1DivF1Tab[index] - delta * (tanDer2DivF2Tab[index] - delta
* (tanDer3DivF3Tab[index] - delta * tanDer4DivF4Tab[index]))));
}
return negateResult ? -result : result;
}
/**
* @param value Value in [-1,1].
* @return Value arccosine, in radians, in [0,PI].
*/
public static double acos(double value) {
return Math.PI / 2 - FastMath.asin(value);
}
/**
* @param value Value in [-1,1].
* @return Value arcsine, in radians, in [-PI/2,PI/2].
*/
public static double asin(double value) {
boolean negateResult;
if (value < 0.0) {
value = -value;
negateResult = true;
} else {
negateResult = false;
}
if (value <= ASIN_MAX_VALUE_FOR_TABS) {
int index = (int) (value * ASIN_INDEXER + 0.5);
double delta = value - index * ASIN_DELTA;
double result = asinTab[index] + delta * (asinDer1DivF1Tab[index] + delta * (asinDer2DivF2Tab[index] + delta
* (asinDer3DivF3Tab[index] + delta * asinDer4DivF4Tab[index])));
return negateResult ? -result : result;
} else if (value <= ASIN_MAX_VALUE_FOR_POWTABS) {
int index = (int) (FastMath.powFast(value * ASIN_POWTABS_ONE_DIV_MAX_VALUE, ASIN_POWTABS_POWER) * ASIN_POWTABS_SIZE_MINUS_ONE
+ 0.5);
double delta = value - asinParamPowTab[index];
double result = asinPowTab[index] + delta * (asinDer1DivF1PowTab[index] + delta * (asinDer2DivF2PowTab[index] + delta
* (asinDer3DivF3PowTab[index] + delta * asinDer4DivF4PowTab[index])));
return negateResult ? -result : result;
} else { // value > ASIN_MAX_VALUE_FOR_TABS, or value is NaN
// This part is derived from fdlibm.
if (value < 1.0) {
double t = (1.0 - value) * 0.5;
double p = t * (ASIN_PS0 + t * (ASIN_PS1 + t * (ASIN_PS2 + t * (ASIN_PS3 + t * (ASIN_PS4 + t * ASIN_PS5)))));
double q = 1.0 + t * (ASIN_QS1 + t * (ASIN_QS2 + t * (ASIN_QS3 + t * ASIN_QS4)));
double s = Math.sqrt(t);
double z = s + s * (p / q);
double result = ASIN_PIO2_HI - ((z + z) - ASIN_PIO2_LO);
return negateResult ? -result : result;
} else { // value >= 1.0, or value is NaN
if (value == 1.0) {
return negateResult ? -Math.PI / 2 : Math.PI / 2;
} else {
return Double.NaN;
}
}
}
}
/**
* @param value A double value.
* @return Value arctangent, in radians, in [-PI/2,PI/2].
*/
public static double atan(double value) {
boolean negateResult;
if (value < 0.0) {
value = -value;
negateResult = true;
} else {
negateResult = false;
}
if (value == 1.0) {
// We want "exact" result for 1.0.
return negateResult ? -Math.PI / 4 : Math.PI / 4;
} else if (value <= ATAN_MAX_VALUE_FOR_TABS) {
int index = (int) (value * ATAN_INDEXER + 0.5);
double delta = value - index * ATAN_DELTA;
double result = atanTab[index] + delta * (atanDer1DivF1Tab[index] + delta * (atanDer2DivF2Tab[index] + delta
* (atanDer3DivF3Tab[index] + delta * atanDer4DivF4Tab[index])));
return negateResult ? -result : result;
} else { // value > ATAN_MAX_VALUE_FOR_TABS, or value is NaN
// This part is derived from fdlibm.
if (value < TWO_POW_66) {
double x = -1 / value;
double x2 = x * x;
double x4 = x2 * x2;
double s1 = x2 * (ATAN_AT0 + x4 * (ATAN_AT2 + x4 * (ATAN_AT4 + x4 * (ATAN_AT6 + x4 * (ATAN_AT8 + x4 * ATAN_AT10)))));
double s2 = x4 * (ATAN_AT1 + x4 * (ATAN_AT3 + x4 * (ATAN_AT5 + x4 * (ATAN_AT7 + x4 * ATAN_AT9))));
double result = ATAN_HI3 - ((x * (s1 + s2) - ATAN_LO3) - x);
return negateResult ? -result : result;
} else { // value >= 2^66, or value is NaN
if (Double.isNaN(value)) {
return Double.NaN;
} else {
return negateResult ? -Math.PI / 2 : Math.PI / 2;
}
}
}
}
/**
* For special values for which multiple conventions could be adopted, behaves like Math.atan2(double,double).
*
* @param y Coordinate on y axis.
* @param x Coordinate on x axis.
* @return Angle from x axis positive side to (x,y) position, in radians, in [-PI,PI].
* Angle measure is positive when going from x axis to y axis (positive sides).
*/
public static double atan2(double y, double x) {
if (x > 0.0) {
if (y == 0.0) {
return (1 / y == Double.NEGATIVE_INFINITY) ? -0.0 : 0.0;
}
if (x == Double.POSITIVE_INFINITY) {
if (y == Double.POSITIVE_INFINITY) {
return Math.PI / 4;
} else if (y == Double.NEGATIVE_INFINITY) {
return -Math.PI / 4;
} else if (y > 0.0) {
return 0.0;
} else if (y < 0.0) {
return -0.0;
} else {
return Double.NaN;
}
} else {
return FastMath.atan(y / x);
}
} else if (x < 0.0) {
if (y == 0.0) {
return (1 / y == Double.NEGATIVE_INFINITY) ? -Math.PI : Math.PI;
}
if (x == Double.NEGATIVE_INFINITY) {
if (y == Double.POSITIVE_INFINITY) {
return 3 * Math.PI / 4;
} else if (y == Double.NEGATIVE_INFINITY) {
return -3 * Math.PI / 4;
} else if (y > 0.0) {
return Math.PI;
} else if (y < 0.0) {
return -Math.PI;
} else {
return Double.NaN;
}
} else if (y > 0.0) {
return Math.PI / 2 + FastMath.atan(-x / y);
} else if (y < 0.0) {
return -Math.PI / 2 - FastMath.atan(x / y);
} else {
return Double.NaN;
}
} else if (x == 0.0) {
if (y == 0.0) {
if (1 / x == Double.NEGATIVE_INFINITY) {
return (1 / y == Double.NEGATIVE_INFINITY) ? -Math.PI : Math.PI;
} else {
return (1 / y == Double.NEGATIVE_INFINITY) ? -0.0 : 0.0;
}
}
if (y > 0.0) {
return Math.PI / 2;
} else if (y < 0.0) {
return -Math.PI / 2;
} else {
return Double.NaN;
}
} else {
return Double.NaN;
}
}
/**
* This treatment is somehow accurate for low values of |power|,
* and for |power*getExponent(value)| < 1023 or so (to stay away
* from double extreme magnitudes (large and small)).
*
* @param value A double value.
* @param power A power.
* @return value^power.
*/
private static double powFast(double value, int power) {
if (power > 5) { // Most common case first.
double oddRemains = 1.0;
do {
// Test if power is odd.
if ((power & 1) != 0) {
oddRemains *= value;
}
value *= value;
power >>= 1; // power = power / 2
} while (power > 5);
// Here, power is in [3,5]: faster to finish outside the loop.
if (power == 3) {
return oddRemains * value * value * value;
} else {
double v2 = value * value;
if (power == 4) {
return oddRemains * v2 * v2;
} else { // power == 5
return oddRemains * v2 * v2 * value;
}
}
} else if (power >= 0) { // power in [0,5]
if (power < 3) { // power in [0,2]
if (power == 2) { // Most common case first.
return value * value;
} else if (power != 0) { // faster than == 1
return value;
} else { // power == 0
return 1.0;
}
} else { // power in [3,5]
if (power == 3) {
return value * value * value;
} else { // power in [4,5]
double v2 = value * value;
if (power == 4) {
return v2 * v2;
} else { // power == 5
return v2 * v2 * value;
}
}
}
} else { // power < 0
// Opposite of Integer.MIN_VALUE does not exist as int.
if (power == Integer.MIN_VALUE) {
// Integer.MAX_VALUE = -(power+1)
return 1.0 / (FastMath.powFast(value, Integer.MAX_VALUE) * value);
} else {
return 1.0 / FastMath.powFast(value, -power);
}
}
}
// --------------------------------------------------------------------------
// PRIVATE TREATMENTS
// --------------------------------------------------------------------------
/**
* FastMath is non-instantiable.
*/
private FastMath() {}
/**
* Use look-up tables size power through this method,
* to make sure is it small in case java.lang.Math
* is directly used.
*/
private static int getTabSizePower(int tabSizePower) {
return tabSizePower;
}
/**
* Remainder using an accurate definition of PI.
* Derived from a fdlibm treatment called __ieee754_rem_pio2.
*
* This method can return values slightly (like one ULP or so) outside [-Math.PI,Math.PI] range.
*
* @param angle Angle in radians.
* @return Remainder of (angle % (2*PI)), which is in [-PI,PI] range.
*/
private static double remainderTwoPi(double angle) {
boolean negateResult;
if (angle < 0.0) {
negateResult = true;
angle = -angle;
} else {
negateResult = false;
}
if (angle <= NORMALIZE_ANGLE_MAX_MEDIUM_DOUBLE) {
double fn = (double) (int) (angle * INVTWOPI + 0.5);
double result = (angle - fn * TWOPI_HI) - fn * TWOPI_LO;
return negateResult ? -result : result;
} else if (angle < Double.POSITIVE_INFINITY) {
// Reworking exponent to have a value < 2^24.
long lx = Double.doubleToRawLongBits(angle);
long exp = ((lx >> 52) & 0x7FF) - 1046;
double z = Double.longBitsToDouble(lx - (exp << 52));
double x0 = (double) ((int) z);
z = (z - x0) * TWO_POW_24;
double x1 = (double) ((int) z);
double x2 = (z - x1) * TWO_POW_24;
double result = subRemainderTwoPi(x0, x1, x2, (int) exp, (x2 == 0) ? 2 : 3);
return negateResult ? -result : result;
} else { // angle is +infinity or NaN
return Double.NaN;
}
}
/**
* Remainder using an accurate definition of PI.
* Derived from a fdlibm treatment called __kernel_rem_pio2.
*
* @param x0 Most significant part of the value, as an integer < 2^24, in double precision format. Must be >= 0.
* @param x1 Following significant part of the value, as an integer < 2^24, in double precision format.
* @param x2 Least significant part of the value, as an integer < 2^24, in double precision format.
* @param e0 Exponent of x0 (value is (2^e0)*(x0+(2^-24)*(x1+(2^-24)*x2))). Must be ≥ -20.
* @param nx Number of significant parts to take into account. Must be 2 or 3.
* @return Remainder of (value % (2*PI)), which is in [-PI,PI] range.
*/
private static double subRemainderTwoPi(double x0, double x1, double x2, int e0, int nx) {
int ih;
double z, fw;
double f0, f1, f2, f3, f4, f5, f6 = 0.0, f7;
double q0, q1, q2, q3, q4, q5;
int iq0, iq1, iq2, iq3, iq4;
final int jx = nx - 1; // jx in [1,2] (nx in [2,3])
// Could use a table to avoid division, but the gain isn't worth it most likely...
final int jv = (e0 - 3) / 24; // We do not handle the case (e0-3 < -23).
int q = e0 - ((jv << 4) + (jv << 3)) - 24; // e0-24*(jv+1)
final int j = jv + 4;
if (jx == 1) {
f5 = (j >= 0) ? ONE_OVER_TWOPI_TAB[j] : 0.0;
f4 = (j >= 1) ? ONE_OVER_TWOPI_TAB[j - 1] : 0.0;
f3 = (j >= 2) ? ONE_OVER_TWOPI_TAB[j - 2] : 0.0;
f2 = (j >= 3) ? ONE_OVER_TWOPI_TAB[j - 3] : 0.0;
f1 = (j >= 4) ? ONE_OVER_TWOPI_TAB[j - 4] : 0.0;
f0 = (j >= 5) ? ONE_OVER_TWOPI_TAB[j - 5] : 0.0;
q0 = x0 * f1 + x1 * f0;
q1 = x0 * f2 + x1 * f1;
q2 = x0 * f3 + x1 * f2;
q3 = x0 * f4 + x1 * f3;
q4 = x0 * f5 + x1 * f4;
} else { // jx == 2
f6 = (j >= 0) ? ONE_OVER_TWOPI_TAB[j] : 0.0;
f5 = (j >= 1) ? ONE_OVER_TWOPI_TAB[j - 1] : 0.0;
f4 = (j >= 2) ? ONE_OVER_TWOPI_TAB[j - 2] : 0.0;
f3 = (j >= 3) ? ONE_OVER_TWOPI_TAB[j - 3] : 0.0;
f2 = (j >= 4) ? ONE_OVER_TWOPI_TAB[j - 4] : 0.0;
f1 = (j >= 5) ? ONE_OVER_TWOPI_TAB[j - 5] : 0.0;
f0 = (j >= 6) ? ONE_OVER_TWOPI_TAB[j - 6] : 0.0;
q0 = x0 * f2 + x1 * f1 + x2 * f0;
q1 = x0 * f3 + x1 * f2 + x2 * f1;
q2 = x0 * f4 + x1 * f3 + x2 * f2;
q3 = x0 * f5 + x1 * f4 + x2 * f3;
q4 = x0 * f6 + x1 * f5 + x2 * f4;
}
z = q4;
fw = (double) ((int) (TWO_POW_N24 * z));
iq0 = (int) (z - TWO_POW_24 * fw);
z = q3 + fw;
fw = (double) ((int) (TWO_POW_N24 * z));
iq1 = (int) (z - TWO_POW_24 * fw);
z = q2 + fw;
fw = (double) ((int) (TWO_POW_N24 * z));
iq2 = (int) (z - TWO_POW_24 * fw);
z = q1 + fw;
fw = (double) ((int) (TWO_POW_N24 * z));
iq3 = (int) (z - TWO_POW_24 * fw);
z = q0 + fw;
// Here, q is in [-25,2] range or so, so we can use the table right away.
double twoPowQ = twoPowTab[q - MIN_DOUBLE_EXPONENT];
z = (z * twoPowQ) % 8.0;
z -= (double) ((int) z);
if (q > 0) {
iq3 &= 0xFFFFFF >> q;
ih = iq3 >> (23 - q);
} else if (q == 0) {
ih = iq3 >> 23;
} else if (z >= 0.5) {
ih = 2;
} else {
ih = 0;
}
if (ih > 0) {
int carry;
if (iq0 != 0) {
carry = 1;
iq0 = 0x1000000 - iq0;
iq1 = 0x0FFFFFF - iq1;
iq2 = 0x0FFFFFF - iq2;
iq3 = 0x0FFFFFF - iq3;
} else {
if (iq1 != 0) {
carry = 1;
iq1 = 0x1000000 - iq1;
iq2 = 0x0FFFFFF - iq2;
iq3 = 0x0FFFFFF - iq3;
} else {
if (iq2 != 0) {
carry = 1;
iq2 = 0x1000000 - iq2;
iq3 = 0x0FFFFFF - iq3;
} else {
if (iq3 != 0) {
carry = 1;
iq3 = 0x1000000 - iq3;
} else {
carry = 0;
}
}
}
}
if (q > 0) {
switch (q) {
case 1 -> iq3 &= 0x7FFFFF;
case 2 -> iq3 &= 0x3FFFFF;
}
}
if (ih == 2) {
z = 1.0 - z;
if (carry != 0) {
z -= twoPowQ;
}
}
}
if (z == 0.0) {
if (jx == 1) {
f6 = ONE_OVER_TWOPI_TAB[jv + 5];
q5 = x0 * f6 + x1 * f5;
} else { // jx == 2
f7 = ONE_OVER_TWOPI_TAB[jv + 5];
q5 = x0 * f7 + x1 * f6 + x2 * f5;
}
z = q5;
fw = (double) ((int) (TWO_POW_N24 * z));
iq0 = (int) (z - TWO_POW_24 * fw);
z = q4 + fw;
fw = (double) ((int) (TWO_POW_N24 * z));
iq1 = (int) (z - TWO_POW_24 * fw);
z = q3 + fw;
fw = (double) ((int) (TWO_POW_N24 * z));
iq2 = (int) (z - TWO_POW_24 * fw);
z = q2 + fw;
fw = (double) ((int) (TWO_POW_N24 * z));
iq3 = (int) (z - TWO_POW_24 * fw);
z = q1 + fw;
fw = (double) ((int) (TWO_POW_N24 * z));
iq4 = (int) (z - TWO_POW_24 * fw);
z = q0 + fw;
z = (z * twoPowQ) % 8.0;
z -= (double) ((int) z);
if (q > 0) {
// some parentheses for Eclipse formatter's weaknesses with bits shifts
iq4 &= (0xFFFFFF >> q);
ih = (iq4 >> (23 - q));
} else if (q == 0) {
ih = iq4 >> 23;
} else if (z >= 0.5) {
ih = 2;
} else {
ih = 0;
}
if (ih > 0) {
if (iq0 != 0) {
iq0 = 0x1000000 - iq0;
iq1 = 0x0FFFFFF - iq1;
iq2 = 0x0FFFFFF - iq2;
iq3 = 0x0FFFFFF - iq3;
iq4 = 0x0FFFFFF - iq4;
} else {
if (iq1 != 0) {
iq1 = 0x1000000 - iq1;
iq2 = 0x0FFFFFF - iq2;
iq3 = 0x0FFFFFF - iq3;
iq4 = 0x0FFFFFF - iq4;
} else {
if (iq2 != 0) {
iq2 = 0x1000000 - iq2;
iq3 = 0x0FFFFFF - iq3;
iq4 = 0x0FFFFFF - iq4;
} else {
if (iq3 != 0) {
iq3 = 0x1000000 - iq3;
iq4 = 0x0FFFFFF - iq4;
} else {
if (iq4 != 0) {
iq4 = 0x1000000 - iq4;
}
}
}
}
}
if (q > 0) {
switch (q) {
case 1 -> iq4 &= 0x7FFFFF;
case 2 -> iq4 &= 0x3FFFFF;
}
}
}
fw = twoPowQ * TWO_POW_N24; // q -= 24, so initializing fw with ((2^q)*(2^-24)=2^(q-24))
} else {
// Here, q is in [-25,-2] range or so, so we could use twoPow's table right away with
// iq4 = (int)(z*twoPowTab[-q-TWO_POW_TAB_MIN_POW]);
// but tests show using division is faster...
iq4 = (int) (z / twoPowQ);
fw = twoPowQ;
}
q4 = fw * (double) iq4;
fw *= TWO_POW_N24;
q3 = fw * (double) iq3;
fw *= TWO_POW_N24;
q2 = fw * (double) iq2;
fw *= TWO_POW_N24;
q1 = fw * (double) iq1;
fw *= TWO_POW_N24;
q0 = fw * (double) iq0;
fw *= TWO_POW_N24;
fw = TWOPI_TAB0 * q4;
fw += TWOPI_TAB0 * q3 + TWOPI_TAB1 * q4;
fw += TWOPI_TAB0 * q2 + TWOPI_TAB1 * q3 + TWOPI_TAB2 * q4;
fw += TWOPI_TAB0 * q1 + TWOPI_TAB1 * q2 + TWOPI_TAB2 * q3 + TWOPI_TAB3 * q4;
fw += TWOPI_TAB0 * q0 + TWOPI_TAB1 * q1 + TWOPI_TAB2 * q2 + TWOPI_TAB3 * q3 + TWOPI_TAB4 * q4;
return (ih == 0) ? fw : -fw;
}
// --------------------------------------------------------------------------
// STATIC INITIALIZATIONS
// --------------------------------------------------------------------------
/**
* Initializes look-up tables.
*
* Might use some FastMath methods in there, not to spend
* an hour in it, but must take care not to use methods
* which look-up tables have not yet been initialized,
* or that are not accurate enough.
*/
static {
// sin and cos
final int SIN_COS_PI_INDEX = (SIN_COS_TABS_SIZE - 1) / 2;
final int SIN_COS_PI_MUL_2_INDEX = 2 * SIN_COS_PI_INDEX;
final int SIN_COS_PI_MUL_0_5_INDEX = SIN_COS_PI_INDEX / 2;
final int SIN_COS_PI_MUL_1_5_INDEX = 3 * SIN_COS_PI_INDEX / 2;
for (int i = 0; i < SIN_COS_TABS_SIZE; i++) {
// angle: in [0,2*PI].
double angle = i * SIN_COS_DELTA_HI + i * SIN_COS_DELTA_LO;
double sinAngle = StrictMath.sin(angle);
double cosAngle = StrictMath.cos(angle);
// For indexes corresponding to null cosine or sine, we make sure the value is zero
// and not an epsilon. This allows for a much better accuracy for results close to zero.
if (i == SIN_COS_PI_INDEX) {
sinAngle = 0.0;
} else if (i == SIN_COS_PI_MUL_2_INDEX) {
sinAngle = 0.0;
} else if (i == SIN_COS_PI_MUL_0_5_INDEX) {
cosAngle = 0.0;
} else if (i == SIN_COS_PI_MUL_1_5_INDEX) {
cosAngle = 0.0;
}
sinTab[i] = sinAngle;
cosTab[i] = cosAngle;
}
// tan
for (int i = 0; i < TAN_TABS_SIZE; i++) {
// angle: in [0,TAN_MAX_VALUE_FOR_TABS].
double angle = i * TAN_DELTA_HI + i * TAN_DELTA_LO;
tanTab[i] = StrictMath.tan(angle);
double cosAngle = StrictMath.cos(angle);
double sinAngle = StrictMath.sin(angle);
double cosAngleInv = 1 / cosAngle;
double cosAngleInv2 = cosAngleInv * cosAngleInv;
double cosAngleInv3 = cosAngleInv2 * cosAngleInv;
double cosAngleInv4 = cosAngleInv2 * cosAngleInv2;
double cosAngleInv5 = cosAngleInv3 * cosAngleInv2;
tanDer1DivF1Tab[i] = cosAngleInv2;
tanDer2DivF2Tab[i] = ((2 * sinAngle) * cosAngleInv3) * ONE_DIV_F2;
tanDer3DivF3Tab[i] = ((2 * (1 + 2 * sinAngle * sinAngle)) * cosAngleInv4) * ONE_DIV_F3;
tanDer4DivF4Tab[i] = ((8 * sinAngle * (2 + sinAngle * sinAngle)) * cosAngleInv5) * ONE_DIV_F4;
}
// asin
for (int i = 0; i < ASIN_TABS_SIZE; i++) {
// x: in [0,ASIN_MAX_VALUE_FOR_TABS].
double x = i * ASIN_DELTA;
asinTab[i] = StrictMath.asin(x);
double oneMinusXSqInv = 1.0 / (1 - x * x);
double oneMinusXSqInv0_5 = StrictMath.sqrt(oneMinusXSqInv);
double oneMinusXSqInv1_5 = oneMinusXSqInv0_5 * oneMinusXSqInv;
double oneMinusXSqInv2_5 = oneMinusXSqInv1_5 * oneMinusXSqInv;
double oneMinusXSqInv3_5 = oneMinusXSqInv2_5 * oneMinusXSqInv;
asinDer1DivF1Tab[i] = oneMinusXSqInv0_5;
asinDer2DivF2Tab[i] = (x * oneMinusXSqInv1_5) * ONE_DIV_F2;
asinDer3DivF3Tab[i] = ((1 + 2 * x * x) * oneMinusXSqInv2_5) * ONE_DIV_F3;
asinDer4DivF4Tab[i] = ((5 + 2 * x * (2 + x * (5 - 2 * x))) * oneMinusXSqInv3_5) * ONE_DIV_F4;
}
for (int i = 0; i < ASIN_POWTABS_SIZE; i++) {
// x: in [0,ASIN_MAX_VALUE_FOR_POWTABS].
double x = StrictMath.pow(i * (1.0 / ASIN_POWTABS_SIZE_MINUS_ONE), 1.0 / ASIN_POWTABS_POWER) * ASIN_MAX_VALUE_FOR_POWTABS;
asinParamPowTab[i] = x;
asinPowTab[i] = StrictMath.asin(x);
double oneMinusXSqInv = 1.0 / (1 - x * x);
double oneMinusXSqInv0_5 = StrictMath.sqrt(oneMinusXSqInv);
double oneMinusXSqInv1_5 = oneMinusXSqInv0_5 * oneMinusXSqInv;
double oneMinusXSqInv2_5 = oneMinusXSqInv1_5 * oneMinusXSqInv;
double oneMinusXSqInv3_5 = oneMinusXSqInv2_5 * oneMinusXSqInv;
asinDer1DivF1PowTab[i] = oneMinusXSqInv0_5;
asinDer2DivF2PowTab[i] = (x * oneMinusXSqInv1_5) * ONE_DIV_F2;
asinDer3DivF3PowTab[i] = ((1 + 2 * x * x) * oneMinusXSqInv2_5) * ONE_DIV_F3;
asinDer4DivF4PowTab[i] = ((5 + 2 * x * (2 + x * (5 - 2 * x))) * oneMinusXSqInv3_5) * ONE_DIV_F4;
}
// atan
for (int i = 0; i < ATAN_TABS_SIZE; i++) {
// x: in [0,ATAN_MAX_VALUE_FOR_TABS].
double x = i * ATAN_DELTA;
double onePlusXSqInv = 1.0 / (1 + x * x);
double onePlusXSqInv2 = onePlusXSqInv * onePlusXSqInv;
double onePlusXSqInv3 = onePlusXSqInv2 * onePlusXSqInv;
double onePlusXSqInv4 = onePlusXSqInv2 * onePlusXSqInv2;
atanTab[i] = StrictMath.atan(x);
atanDer1DivF1Tab[i] = onePlusXSqInv;
atanDer2DivF2Tab[i] = (-2 * x * onePlusXSqInv2) * ONE_DIV_F2;
atanDer3DivF3Tab[i] = ((-2 + 6 * x * x) * onePlusXSqInv3) * ONE_DIV_F3;
atanDer4DivF4Tab[i] = ((24 * x * (1 - x * x)) * onePlusXSqInv4) * ONE_DIV_F4;
}
// twoPow
for (int i = MIN_DOUBLE_EXPONENT; i <= MAX_DOUBLE_EXPONENT; i++) {
twoPowTab[i - MIN_DOUBLE_EXPONENT] = StrictMath.pow(2.0, i);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy