diff --git a/kmath-jafama/build.gradle.kts b/kmath-jafama/build.gradle.kts new file mode 100644 index 000000000..22d50f89c --- /dev/null +++ b/kmath-jafama/build.gradle.kts @@ -0,0 +1,21 @@ +plugins { + id("ru.mipt.npm.gradle.jvm") +} + +dependencies { + api(project(":kmath-ast")) + api(project(":kmath-complex")) + api(project(":kmath-for-real")) +} + +kscience{ + useHtml() +} + +readme { + maturity = ru.mipt.npm.gradle.Maturity.PROTOTYPE +} + +kotlin.sourceSets.all { + languageSettings.useExperimentalAnnotation("space.kscience.kmath.misc.UnstableKMathAPI") +} diff --git a/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/CmnFastMath.java b/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/CmnFastMath.java new file mode 100644 index 000000000..0abc5d95d --- /dev/null +++ b/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/CmnFastMath.java @@ -0,0 +1,2112 @@ +/* + * Copyright 2018-2021 KMath contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package space.kscience.kmath.jafama; + +/** + * Stuffs for FastMath and StrictFastMath. + */ +abstract class CmnFastMath { + + /* + * 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. + * + * Tables are put in specific inner classes, to be lazily initialized. + * Always doing strict tables initialization, even if StrictFastMath delegates + * to StrictMath and doesn't use tables, which makes tables initialization a bit + * slower but code simpler. + * Using redefined pure Java treatments during tables initialization, + * instead of Math or StrictMath ones (even asin(double)), can be very slow, + * because class loading is likely not to be optimized. + * + * 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. + */ + + /* + * Regarding strictfp-ness: + * + * Switching from/to strictfp has some overhead, so we try to only + * strictfp-ize when needed (or when clueless). + * Compile-time constants are computed in a FP-strict way, so no need + * to make this whole class strictfp. + */ + + //-------------------------------------------------------------------------- + // CONFIGURATION + //-------------------------------------------------------------------------- + + /* + * FastMath + */ + + static final boolean FM_USE_JDK_MATH = getBooleanProperty("jafama.usejdk", false); + + /** + * Used for both FastMath.log(double) and FastMath.log10(double). + */ + static final boolean FM_USE_REDEFINED_LOG = getBooleanProperty("jafama.fastlog", false); + + static final boolean FM_USE_REDEFINED_SQRT = getBooleanProperty("jafama.fastsqrt", false); + + /** + * Set it to true if FastMath.sqrt(double) is slow + * (more tables, but less calls to FastMath.sqrt(double)). + */ + static final boolean FM_USE_POWTABS_FOR_ASIN = false; + + /* + * StrictFastMath + */ + + static final boolean SFM_USE_JDK_MATH = getBooleanProperty("jafama.strict.usejdk", false); + + /** + * Used for both StrictFastMath.log(double) and StrictFastMath.log10(double). + * True by default because the StrictMath implementations can be slow. + */ + static final boolean SFM_USE_REDEFINED_LOG = getBooleanProperty("jafama.strict.fastlog", true); + + static final boolean SFM_USE_REDEFINED_SQRT = getBooleanProperty("jafama.strict.fastsqrt", false); + + /** + * Set it to true if StrictFastMath.sqrt(double) is slow + * (more tables, but less calls to StrictFastMath.sqrt(double)). + */ + static final boolean SFM_USE_POWTABS_FOR_ASIN = false; + + /* + * Common to FastMath and StrictFastMath. + */ + + /** + * Using two pow tab can just make things barely faster, + * and could relatively hurt in case of cache-misses, + * especially for methods that otherwise wouldn't rely + * on any tab, so we don't use it. + */ + static final boolean USE_TWO_POW_TAB = false; + + /** + * Because on some architectures, some casts can be slow, + * especially for large values. + * Might make things a bit slower for latest architectures, + * but not as much as it makes them faster for older ones. + */ + static final boolean ANTI_SLOW_CASTS = true; + + /** + * If some methods get JIT-optimized, they might crash + * if they contain "(var == xxx)" with var being NaN + * (can happen with Java 6u29). + * + * The crash does not happen if we replace "==" with "<" or ">". + * + * Only the code that has been observed to trigger the bug + * has been modified. + */ + static final boolean ANTI_JIT_OPTIM_CRASH_ON_NAN = true; + + //-------------------------------------------------------------------------- + // GENERAL CONSTANTS + //-------------------------------------------------------------------------- + + /** + * Closest double approximation of e. + */ + public static final double E = Math.E; + + /** + * Closest double approximation of pi, which is inferior to mathematical pi: + * pi ~= 3.14159265358979323846... + * PI ~= 3.141592653589793 + */ + public static final double PI = Math.PI; + + /** + * High double approximation of pi, which is further from pi + * than the low approximation PI: + * pi ~= 3.14159265358979323846... + * PI ~= 3.141592653589793 + * PI_SUP ~= 3.1415926535897936 + */ + public static final double PI_SUP = Double.longBitsToDouble(Double.doubleToRawLongBits(Math.PI)+1); + + static final double ONE_DIV_F2 = 1/2.0; + static final double ONE_DIV_F3 = 1/6.0; + static final double ONE_DIV_F4 = 1/24.0; + + static final float TWO_POW_23_F = (float)NumbersUtils.twoPow(23); + + static final double TWO_POW_24 = NumbersUtils.twoPow(24); + private static final double TWO_POW_N24 = NumbersUtils.twoPow(-24); + + static final double TWO_POW_26 = NumbersUtils.twoPow(26); + static final double TWO_POW_N26 = NumbersUtils.twoPow(-26); + + // First double value (from zero) such as (value+-1/value == value). + static final double TWO_POW_27 = NumbersUtils.twoPow(27); + static final double TWO_POW_N27 = NumbersUtils.twoPow(-27); + + static final double TWO_POW_N28 = NumbersUtils.twoPow(-28); + + static final double TWO_POW_52 = NumbersUtils.twoPow(52); + + static final double TWO_POW_N55 = NumbersUtils.twoPow(-55); + + static final double TWO_POW_66 = NumbersUtils.twoPow(66); + + static final double TWO_POW_512 = NumbersUtils.twoPow(512); + static final double TWO_POW_N512 = NumbersUtils.twoPow(-512); + + /** + * Double.MIN_NORMAL since Java 6. + */ + static final double DOUBLE_MIN_NORMAL = Double.longBitsToDouble(0x0010000000000000L); // 2.2250738585072014E-308 + + // Not storing float/double mantissa size in constants, + // for 23 and 52 are shorter to read and more + // bitwise-explicit than some constant's name. + + static final int MIN_DOUBLE_EXPONENT = -1074; + static final int MIN_DOUBLE_NORMAL_EXPONENT = -1022; + static final int MAX_DOUBLE_EXPONENT = 1023; + + static final int MIN_FLOAT_NORMAL_EXPONENT = -126; + static final int MAX_FLOAT_EXPONENT = 127; + + private static final double SQRT_2 = StrictMath.sqrt(2.0); + + static final double LOG_2 = StrictMath.log(2.0); + static final double LOG_TWO_POW_27 = StrictMath.log(TWO_POW_27); + static final double LOG_DOUBLE_MAX_VALUE = StrictMath.log(Double.MAX_VALUE); + + static final double INV_LOG_10 = 1.0/StrictMath.log(10.0); + + static final double DOUBLE_BEFORE_60 = Double.longBitsToDouble(Double.doubleToRawLongBits(60.0)-1); + + //-------------------------------------------------------------------------- + // CONSTANTS FOR NORMALIZATIONS + //-------------------------------------------------------------------------- + + /** + * Table of constants for 1/(PI/2), 282 Hex digits (enough for normalizing doubles). + * 1/(PI/2) approximation = sum of TWO_OVER_PI_TAB[i]*2^(-24*(i+1)). + * + * double and not int, to avoid int-to-double cast during computations. + */ + private static final double TWO_OVER_PI_TAB[] = { + 0xA2F983, 0x6E4E44, 0x1529FC, 0x2757D1, 0xF534DD, 0xC0DB62, + 0x95993C, 0x439041, 0xFE5163, 0xABDEBB, 0xC561B7, 0x246E3A, + 0x424DD2, 0xe00649, 0x2EEA09, 0xD1921C, 0xFE1DEB, 0x1CB129, + 0xA73EE8, 0x8235F5, 0x2EBB44, 0x84E99C, 0x7026B4, 0x5F7E41, + 0x3991d6, 0x398353, 0x39F49C, 0x845F8B, 0xBDF928, 0x3B1FF8, + 0x97FFDE, 0x05980F, 0xEF2F11, 0x8B5A0A, 0x6D1F6D, 0x367ECF, + 0x27CB09, 0xB74F46, 0x3F669E, 0x5FEA2D, 0x7527BA, 0xC7EBE5, + 0xF17B3D, 0x0739F7, 0x8A5292, 0xEA6BFB, 0x5FB11F, 0x8D5D08, + 0x560330, 0x46FC7B, 0x6BABF0, 0xCFBC20, 0x9AF436, 0x1DA9E3, + 0x91615E, 0xE61B08, 0x659985, 0x5F14A0, 0x68408D, 0xFFD880, + 0x4D7327, 0x310606, 0x1556CA, 0x73A8C9, 0x60E27B, 0xC08C6B}; + + /* + * Constants for PI/2. Only the 23 most significant bits of each mantissa are used. + * 2*PI approximation = sum of TWOPI_TAB. + */ + private static final double PIO2_TAB0 = Double.longBitsToDouble(0x3FF921FB40000000L); + private static final double PIO2_TAB1 = Double.longBitsToDouble(0x3E74442D00000000L); + private static final double PIO2_TAB2 = Double.longBitsToDouble(0x3CF8469880000000L); + private static final double PIO2_TAB3 = Double.longBitsToDouble(0x3B78CC5160000000L); + private static final double PIO2_TAB4 = Double.longBitsToDouble(0x39F01B8380000000L); + private static final double PIO2_TAB5 = Double.longBitsToDouble(0x387A252040000000L); + + static final double PIO2_INV = Double.longBitsToDouble(0x3FE45F306DC9C883L); // 6.36619772367581382433e-01 53 bits of 2/pi + static final double PIO2_HI = Double.longBitsToDouble(0x3FF921FB54400000L); // 1.57079632673412561417e+00 first 33 bits of pi/2 + static final double PIO2_LO = Double.longBitsToDouble(0x3DD0B4611A626331L); // 6.07710050650619224932e-11 pi/2 - PIO2_HI + static final double PI_INV = PIO2_INV/2; + static final double PI_HI = 2*PIO2_HI; + static final double PI_LO = 2*PIO2_LO; + static final double TWOPI_INV = PIO2_INV/4; + static final double TWOPI_HI = 4*PIO2_HI; + static final double TWOPI_LO = 4*PIO2_LO; + + /** + * Bit = 0 where quadrant is encoded in remainder bits. + */ + private static final long QUADRANT_BITS_0_MASK = 0xCFFFFFFFFFFFFFFFL; + + /** + * Remainder bits where quadrant is encoded, 0 elsewhere. + */ + private static final long QUADRANT_PLACE_BITS = 0x3000000000000000L; + + /** + * fdlibm uses 2^19*PI/2 here. + * With 2^18*PI/2 we would be more accurate, for example when normalizing + * 822245.903631403, which is close to 2^19*PI/2, but we are still in + * our accuracy tolerance with fdlibm's value (but not 2^20*PI/2) so we + * stick to it, to help being faster than (Strict)Math for values in + * [2^18*PI/2,2^19*PI/2]. + * + * For tests, can use a smaller value, for heavy remainder + * not to only be used with huge values. + */ + static final double NORMALIZE_ANGLE_MAX_MEDIUM_DOUBLE_PIO2 = StrictMath.pow(2.0,19.0)*(Math.PI/2); + + /** + * 2*Math.PI, normalized into [-PI,PI], as returned by + * StrictMath.asin(StrictMath.sin(2*Math.PI)) + * (asin behaves as identity for this). + * + * NB: NumbersUtils.minus2PI(2*Math.PI) returns -2.449293598153844E-16, + * which is different due to not using an accurate enough definition of PI. + */ + static final double TWO_MATH_PI_IN_MINUS_PI_PI = -2.4492935982947064E-16; + + //-------------------------------------------------------------------------- + // CONSTANTS AND TABLES FOR SIN AND COS + //-------------------------------------------------------------------------- + + static final int SIN_COS_TABS_SIZE = (1<>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))). + */ + static final double TAN_MAX_VALUE_FOR_TABS = StrictMath.toRadians(77.0); + + static final int TAN_TABS_SIZE = (int)((TAN_MAX_VALUE_FOR_TABS/(Math.PI/2)) * (TAN_VIRTUAL_TABS_SIZE-1)) + 1; + static final double TAN_DELTA_HI = PIO2_HI/(TAN_VIRTUAL_TABS_SIZE-1); + static final double TAN_DELTA_LO = PIO2_LO/(TAN_VIRTUAL_TABS_SIZE-1); + static final double TAN_INDEXER = 1/(TAN_DELTA_HI+TAN_DELTA_LO); + + static final class MyTTan { + static final double[] tanTab = new double[TAN_TABS_SIZE]; + static final double[] tanDer1DivF1Tab = new double[TAN_TABS_SIZE]; + static final double[] tanDer2DivF2Tab = new double[TAN_TABS_SIZE]; + static final double[] tanDer3DivF3Tab = new double[TAN_TABS_SIZE]; + static final double[] tanDer4DivF4Tab = new double[TAN_TABS_SIZE]; + static { + init(); + } + private static strictfp void init() { + for (int i=0;i>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). + */ + static final double ASIN_MAX_VALUE_FOR_TABS = StrictMath.sin(StrictMath.toRadians(73.0)); + + static final int ASIN_TABS_SIZE = (1< 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. + */ + static final double ATAN_MAX_VALUE_FOR_TABS = StrictMath.tan(StrictMath.toRadians(74.0)); + + static final int ATAN_TABS_SIZE = (1<>SQRT_LO_BITS)); + for (int i=1;i>CBRT_LO_BITS)); + for (int i=1;i= MIN_DOUBLE_EXPONENT) { + if (power <= MAX_DOUBLE_EXPONENT) { // Normal or subnormal. + return MyTTwoPow.twoPowTab[power-MIN_DOUBLE_EXPONENT]; + } else { // Overflow. + return Double.POSITIVE_INFINITY; + } + } else { // Underflow. + return 0.0; + } + } else { + return NumbersUtils.twoPow(power); + } + } + + /** + * @param value An int value. + * @return value*value. + */ + public static int pow2(int value) { + return value*value; + } + + /** + * @param value A long value. + * @return value*value. + */ + public static long pow2(long value) { + return value*value; + } + + /** + * @param value An int value. + * @return value*value*value. + */ + public static int pow3(int value) { + return value*value*value; + } + + /** + * @param value A long value. + * @return value*value*value. + */ + public static long pow3(long value) { + return value*value*value; + } + + /* + * absolute values + */ + + /** + * @param value An int value. + * @return The absolute value, except if value is Integer.MIN_VALUE, for which it returns Integer.MIN_VALUE. + */ + public static int abs(int value) { + if (FM_USE_JDK_MATH || SFM_USE_JDK_MATH) { + return Math.abs(value); + } + return NumbersUtils.abs(value); + } + + /** + * @param value A long value. + * @return The absolute value, except if value is Long.MIN_VALUE, for which it returns Long.MIN_VALUE. + */ + public static long abs(long value) { + if (FM_USE_JDK_MATH || SFM_USE_JDK_MATH) { + return Math.abs(value); + } + return NumbersUtils.abs(value); + } + + /* + * close values + */ + + /** + * @param value A long value. + * @return The specified value as int. + * @throws ArithmeticException if the specified value is not in [Integer.MIN_VALUE,Integer.MAX_VALUE] range. + */ + public static int toIntExact(long value) { + return NumbersUtils.asInt(value); + } + + /** + * @param value A long value. + * @return The closest int value in [Integer.MIN_VALUE,Integer.MAX_VALUE] range. + */ + public static int toInt(long value) { + return NumbersUtils.toInt(value); + } + + /* + * ranges + */ + + /** + * @param min An int value. + * @param max An int value. + * @param value An int value. + * @return minValue if value < minValue, maxValue if value > maxValue, value otherwise. + */ + public static int toRange(int min, int max, int value) { + return NumbersUtils.toRange(min, max, value); + } + + /** + * @param min A long value. + * @param max A long value. + * @param value A long value. + * @return min if value < min, max if value > max, value otherwise. + */ + public static long toRange(long min, long max, long value) { + return NumbersUtils.toRange(min, max, value); + } + + /* + * unary operators (increment,decrement,negate) + */ + + /** + * @param value An int value. + * @return The argument incremented by one. + * @throws ArithmeticException if the mathematical result + * is not in int range. + */ + public static int incrementExact(int value) { + if (value == Integer.MAX_VALUE) { + throw new ArithmeticException("integer overflow"); + } + return value + 1; + } + + /** + * @param value A long value. + * @return The argument incremented by one. + * @throws ArithmeticException if the mathematical result + * is not in long range. + */ + public static long incrementExact(long value) { + if (value == Long.MAX_VALUE) { + throw new ArithmeticException("long overflow"); + } + return value + 1L; + } + + /** + * @param value An int value. + * @return The argument incremented by one, or the argument + * if the mathematical result is not in int range. + */ + public static int incrementBounded(int value) { + if (value == Integer.MAX_VALUE) { + return value; + } + return value + 1; + } + + /** + * @param value A long value. + * @return The argument incremented by one, or the argument + * if the mathematical result is not in long range. + */ + public static long incrementBounded(long value) { + if (value == Long.MAX_VALUE) { + return value; + } + return value + 1L; + } + + /** + * @param value An int value. + * @return The argument decremented by one. + * @throws ArithmeticException if the mathematical result + * is not in int range. + */ + public static int decrementExact(int value) { + if (value == Integer.MIN_VALUE) { + throw new ArithmeticException("integer overflow"); + } + return value - 1; + } + + /** + * @param value A long value. + * @return The argument decremented by one. + * @throws ArithmeticException if the mathematical result + * is not in long range. + */ + public static long decrementExact(long value) { + if (value == Long.MIN_VALUE) { + throw new ArithmeticException("long overflow"); + } + return value - 1L; + } + + /** + * @param value An int value. + * @return The argument decremented by one, or the argument + * if the mathematical result is not in int range. + */ + public static int decrementBounded(int value) { + if (value == Integer.MIN_VALUE) { + return value; + } + return value - 1; + } + + /** + * @param value A long value. + * @return The argument decremented by one, or the argument + * if the mathematical result is not in long range. + */ + public static long decrementBounded(long value) { + if (value == Long.MIN_VALUE) { + return value; + } + return value - 1L; + } + + /** + * @param value An int value. + * @return The argument negated. + * @throws ArithmeticException if the mathematical result + * is not in int range. + */ + public static int negateExact(int value) { + if (value == Integer.MIN_VALUE) { + throw new ArithmeticException("integer overflow"); + } + return -value; + } + + /** + * @param value A long value. + * @return The argument negated. + * @throws ArithmeticException if the mathematical result + * is not in long range. + */ + public static long negateExact(long value) { + if (value == Long.MIN_VALUE) { + throw new ArithmeticException("long overflow"); + } + return -value; + } + + /** + * @param value An int value. + * @return The argument negated, or Integer.MAX_VALUE + * if the argument is Integer.MIN_VALUE. + */ + public static int negateBounded(int value) { + if (value == Integer.MIN_VALUE) { + return Integer.MAX_VALUE; + } + return -value; + } + + /** + * @param value A long value. + * @return The argument negated, or Long.MAX_VALUE + * if the argument is Long.MIN_VALUE. + */ + public static long negateBounded(long value) { + if (value == Long.MIN_VALUE) { + return Long.MAX_VALUE; + } + return -value; + } + + /* + * binary operators (+,-,*) + */ + + /** + * @param a An int value. + * @param b An int value. + * @return The mathematical result of a+b. + * @throws ArithmeticException if the mathematical result of a+b is not in [Integer.MIN_VALUE,Integer.MAX_VALUE] range. + */ + public static int addExact(int a, int b) { + return NumbersUtils.plusExact(a, b); + } + + /** + * @param a A long value. + * @param b A long value. + * @return The mathematical result of a+b. + * @throws ArithmeticException if the mathematical result of a+b is not in [Long.MIN_VALUE,Long.MAX_VALUE] range. + */ + public static long addExact(long a, long b) { + return NumbersUtils.plusExact(a, b); + } + + /** + * @param a An int value. + * @param b An int value. + * @return The int value of [Integer.MIN_VALUE,Integer.MAX_VALUE] range which is the closest to mathematical result of a+b. + */ + public static int addBounded(int a, int b) { + return NumbersUtils.plusBounded(a, b); + } + + /** + * @param a A long value. + * @param b A long value. + * @return The long value of [Long.MIN_VALUE,Long.MAX_VALUE] range which is the closest to mathematical result of a+b. + */ + public static long addBounded(long a, long b) { + return NumbersUtils.plusBounded(a, b); + } + + /** + * @param a An int value. + * @param b An int value. + * @return The mathematical result of a-b. + * @throws ArithmeticException if the mathematical result of a-b is not in [Integer.MIN_VALUE,Integer.MAX_VALUE] range. + */ + public static int subtractExact(int a, int b) { + return NumbersUtils.minusExact(a, b); + } + + /** + * @param a A long value. + * @param b A long value. + * @return The mathematical result of a-b. + * @throws ArithmeticException if the mathematical result of a-b is not in [Long.MIN_VALUE,Long.MAX_VALUE] range. + */ + public static long subtractExact(long a, long b) { + return NumbersUtils.minusExact(a, b); + } + + /** + * @param a An int value. + * @param b An int value. + * @return The int value of [Integer.MIN_VALUE,Integer.MAX_VALUE] range which is the closest to mathematical result of a-b. + */ + public static int subtractBounded(int a, int b) { + return NumbersUtils.minusBounded(a, b); + } + + /** + * @param a A long value. + * @param b A long value. + * @return The long value of [Long.MIN_VALUE,Long.MAX_VALUE] range which is the closest to mathematical result of a-b. + */ + public static long subtractBounded(long a, long b) { + return NumbersUtils.minusBounded(a, b); + } + + /** + * @param a An int value. + * @param b An int value. + * @return The mathematical result of a*b. + * @throws ArithmeticException if the mathematical result of a*b is not in [Integer.MIN_VALUE,Integer.MAX_VALUE] range. + */ + public static int multiplyExact(int a, int b) { + return NumbersUtils.timesExact(a, b); + } + + /** + * @param a A long value. + * @param b An int value. + * @return The mathematical result of a*b. + * @throws ArithmeticException if the mathematical result of a*b is not in [Long.MIN_VALUE,Long.MAX_VALUE] range. + */ + public static long multiplyExact(long a, int b) { + return NumbersUtils.timesExact(a, (long) b); + } + + /** + * @param a A long value. + * @param b A long value. + * @return The mathematical result of a*b. + * @throws ArithmeticException if the mathematical result of a*b is not in [Long.MIN_VALUE,Long.MAX_VALUE] range. + */ + public static long multiplyExact(long a, long b) { + return NumbersUtils.timesExact(a, b); + } + + /** + * @param a An int value. + * @param b An int value. + * @return The int value of [Integer.MIN_VALUE,Integer.MAX_VALUE] range which is the closest to mathematical result of a*b. + */ + public static int multiplyBounded(int a, int b) { + return NumbersUtils.timesBounded(a, b); + } + + /** + * @param a A long value. + * @param b An int value. + * @return The long value of [Long.MIN_VALUE,Long.MAX_VALUE] range which is the closest to mathematical result of a*b. + */ + public static long multiplyBounded(long a, int b) { + return NumbersUtils.timesBounded(a, (long) b); + } + + /** + * @param a A long value. + * @param b A long value. + * @return The long value of [Long.MIN_VALUE,Long.MAX_VALUE] range which is the closest to mathematical result of a*b. + */ + public static long multiplyBounded(long a, long b) { + return NumbersUtils.timesBounded(a, b); + } + + /** + * @param x An int value. + * @param y An int value. + * @return The mathematical product as a long. + */ + public static long multiplyFull(int x, int y) { + return ((long) x) * ((long) y); + } + + /** + * @param x A long value. + * @param y A long value. + * @return The most significant 64 bits of the 128-bit product of two 64-bit factors. + */ + public static long multiplyHigh(long x, long y) { + if ((x|y) < 0) { + // Use technique from section 8-2 of Henry S. Warren, Jr., + // Hacker's Delight (2nd ed.) (Addison Wesley, 2013), 173-174. + long x1 = (x >> 32); + long y1 = (y >> 32); + long x2 = (x & 0xFFFFFFFFL); + long y2 = (y & 0xFFFFFFFFL); + long z2 = x2 * y2; + long t = x1 * y2 + (z2 >>> 32); + long z1 = (t & 0xFFFFFFFFL) + x2 * y1; + long z0 = (t >> 32); + return x1 * y1 + z0 + (z1 >> 32); + } else { + // Use Karatsuba technique with two base 2^32 digits. + long x1 = (x >>> 32); + long y1 = (y >>> 32); + long x2 = (x & 0xFFFFFFFFL); + long y2 = (y & 0xFFFFFFFFL); + long A = x1 * y1; + long B = x2 * y2; + long C = (x1 + x2) * (y1 + y2); + long K = C - A - B; + return (((B >>> 32) + K) >>> 32) + A; + } + } + + /* + * binary operators (/,%) + */ + + /** + * Returns the largest int <= dividend/divisor. + * + * Unlike "/" operator, which rounds towards 0, this division + * rounds towards -Infinity (which give different result + * when the exact result is negative). + * + * @param x The dividend. + * @param y The divisor. + * @return The largest int <= dividend/divisor, unless dividend is + * Integer.MIN_VALUE and divisor is -1, in which case + * Integer.MIN_VALUE is returned. + * @throws ArithmeticException if the divisor is zero. + */ + public static int floorDiv(int x, int y) { + int r = x / y; + // If the signs are different and modulo not zero, rounding down. + if (((x ^ y) < 0) && ((r * y) != x)) { + r--; + } + return r; + } + + /** + * Returns the largest long <= dividend/divisor. + * + * Unlike "/" operator, which rounds towards 0, this division + * rounds towards -Infinity (which give different result + * when the exact result is negative). + * + * @param x The dividend. + * @param y The divisor. + * @return The largest long <= dividend/divisor, unless dividend is + * Long.MIN_VALUE and divisor is -1, in which case + * Long.MIN_VALUE is returned. + * @throws ArithmeticException if the divisor is zero. + */ + public static long floorDiv(long x, int y) { + return floorDiv(x, (long) y); + } + + /** + * Returns the largest long <= dividend/divisor. + * + * Unlike "/" operator, which rounds towards 0, this division + * rounds towards -Infinity (which give different result + * when the exact result is negative). + * + * @param x The dividend. + * @param y The divisor. + * @return The largest long <= dividend/divisor, unless dividend is + * Long.MIN_VALUE and divisor is -1, in which case + * Long.MIN_VALUE is returned. + * @throws ArithmeticException if the divisor is zero. + */ + public static long floorDiv(long x, long y) { + long r = x / y; + // If the signs are different and modulo not zero, rounding down. + if (((x ^ y) < 0) && ((r * y) != x)) { + r--; + } + return r; + } + + /** + * Returns the floor modulus, which is "x - floorDiv(x,y) * y", + * has the same sign as y, and is in ]-abs(y),abs(y)[. + * + * The relationship between floorMod and floorDiv is the same + * than between "%" and "/". + * + * @param x The dividend. + * @param y The divisor. + * @return The floor modulus, i.e. "x - (floorDiv(x, y) * y)". + * @throws ArithmeticException if the divisor is zero. + */ + public static int floorMod(int x, int y) { + return x - floorDiv(x, y) * y; + } + + /** + * Returns the floor modulus, which is "x - floorDiv(x,y) * y", + * has the same sign as y, and is in ]-abs(y),abs(y)[. + * + * The relationship between floorMod and floorDiv is the same + * than between "%" and "/". + * + * @param x The dividend. + * @param y The divisor. + * @return The floor modulus, i.e. "x - (floorDiv(x, y) * y)". + * @throws ArithmeticException if the divisor is zero. + */ + public static int floorMod(long x, int y) { + // No overflow so can cast. + return (int) (x - floorDiv(x,y) * y); + } + + /** + * Returns the floor modulus, which is "x - floorDiv(x,y) * y", + * has the same sign as y, and is in ]-abs(y),abs(y)[. + * + * The relationship between floorMod and floorDiv is the same + * than between "%" and "/". + * + * @param x The dividend. + * @param y The divisor. + * @return The floor modulus, i.e. "x - (floorDiv(x, y) * y)". + * @throws ArithmeticException if the divisor is zero. + */ + public static long floorMod(long x, long y) { + return x - floorDiv(x, y) * y; + } + + /* + * Non-redefined Math public values and treatments. + */ + + public static int min(int a, int b) { + return Math.min(a,b); + } + + public static long min(long a, long b) { + return Math.min(a,b); + } + + public static int max(int a, int b) { + return Math.max(a,b); + } + + public static long max(long a, long b) { + return Math.max(a,b); + } + + //-------------------------------------------------------------------------- + // PACKAGE-PRIVATE METHODS + //-------------------------------------------------------------------------- + + /** + * @param power Must be in normal values range. + */ + static double twoPowNormal(int power) { + if (USE_TWO_POW_TAB) { + return MyTTwoPow.twoPowTab[power-MIN_DOUBLE_EXPONENT]; + } else { + return Double.longBitsToDouble(((long)(power+MAX_DOUBLE_EXPONENT))<<52); + } + } + + /** + * @param power Must be in normal or subnormal values range. + */ + static double twoPowNormalOrSubnormal(int power) { + if (USE_TWO_POW_TAB) { + return MyTTwoPow.twoPowTab[power-MIN_DOUBLE_EXPONENT]; + } else { + if (power <= -MAX_DOUBLE_EXPONENT) { // Not normal. + return Double.longBitsToDouble(0x0008000000000000L>>(-(power+MAX_DOUBLE_EXPONENT))); + } else { // Normal. + return Double.longBitsToDouble(((long)(power+MAX_DOUBLE_EXPONENT))<<52); + } + } + } + + static double atan2_pinf_yyy(double y) { + 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; + } + } + + static double atan2_ninf_yyy(double y) { + 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; + } + } + + static double atan2_yyy_zeroOrNaN(double y, double x) { + if (x == 0.0) { + if (y == 0.0) { + if (signFromBit_antiCyclic(x) < 0) { + // x is -0.0 + return signFromBit_antiCyclic(y) * Math.PI; + } else { + // +-0.0 + return y; + } + } + 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; + } + } + + /** + * At least one of the arguments must be NaN. + */ + static double hypot_NaN(double xAbs, double yAbs) { + if ((xAbs == Double.POSITIVE_INFINITY) || (yAbs == Double.POSITIVE_INFINITY)) { + return Double.POSITIVE_INFINITY; + } else { + return Double.NaN; + } + } + + /** + * At least one of the arguments must be NaN. + */ + static double hypot_NaN(double xAbs, double yAbs, double zAbs) { + if ((xAbs == Double.POSITIVE_INFINITY) || (yAbs == Double.POSITIVE_INFINITY) || (zAbs == Double.POSITIVE_INFINITY)) { + return Double.POSITIVE_INFINITY; + } else { + return Double.NaN; + } + } + + /* + * + */ + + /** + * @param remainder Must have 1 for 2nd and 3rd exponent bits, which is the + * case for heavyRemPiO2 remainders (their absolute values are >= + * Double.longBitsToDouble(0x3000000000000000L) + * = 1.727233711018889E-77, and even if they were not, turning these + * bits from 0 to 1 on decoding would not change the absolute error + * much), and also works for +-Infinity or NaN encoding. + * @param quadrant Must be in [0,3]. + * @return Bits holding remainder, and quadrant instead of + * reamainder's 2nd and 3rd exponent bits. + */ + static long encodeRemainderAndQuadrant(double remainder, int quadrant) { + final long bits = Double.doubleToRawLongBits(remainder); + return (bits&QUADRANT_BITS_0_MASK)|(((long)quadrant)<<60); + } + + static double decodeRemainder(long bits) { + return Double.longBitsToDouble((bits&QUADRANT_BITS_0_MASK)|QUADRANT_PLACE_BITS); + } + + static int decodeQuadrant(long bits) { + return ((int)(bits>>60))&3; + } + + /* + * JDK-based remainders. + * Since a strict one for (% (PI/2)) is needed for heavyRemainderPiO2, + * we need it in this class. + * Then, for homogeneity, we put them all in this class. + * Then, to avoid code duplication for these slow-anyway methods, + * we just stick with strict versions, for both FastMath and StrictFastMath. + */ + + /** + * @param angle Angle, in radians. + * @return Remainder of (angle % (2*PI)), in [-PI,PI]. + */ + static strictfp double jdkRemainderTwoPi(double angle) { + final double sin = StrictMath.sin(angle); + final double cos = StrictMath.cos(angle); + return StrictMath.atan2(sin, cos); + } + + /** + * @param angle Angle, in radians. + * @return Remainder of (angle % PI), in [-PI/2,PI/2]. + */ + static strictfp double jdkRemainderPi(double angle) { + final double sin = StrictMath.sin(angle); + final double cos = StrictMath.cos(angle); + /* + * Making sure atan2's result ends up in [-PI/2,PI/2], + * i.e. has maximum accuracy. + */ + return StrictMath.atan2(sin, Math.abs(cos)); + } + + /** + * @param angle Angle, in radians. + * @return Bits of double corresponding to remainder of (angle % (PI/2)), + * in [-PI/4,PI/4], with quadrant encoded in exponent bits. + */ + static strictfp long jdkRemainderPiO2(double angle, boolean negateRem) { + final double sin = StrictMath.sin(angle); + final double cos = StrictMath.cos(angle); + + /* + * Computing quadrant first, and then computing + * atan2, to make sure its result ends up in [-PI/4,PI/4], + * i.e. has maximum accuracy. + */ + + final int q; + final double sinForAtan2; + final double cosForAtan2; + if (cos >= (SQRT_2/2)) { + // [-PI/4,PI/4] + q = 0; + sinForAtan2 = sin; + cosForAtan2 = cos; + } else if (cos <= -(SQRT_2/2)) { + // [3*PI/4,5*PI/4] + q = 2; + sinForAtan2 = -sin; + cosForAtan2 = -cos; + } else if (sin > 0.0) { + // [PI/4,3*PI/4] + q = 1; + sinForAtan2 = -cos; + cosForAtan2 = sin; + } else { + // [5*PI/4,7*PI/4] + q = 3; + sinForAtan2 = cos; + cosForAtan2 = -sin; + } + + double fw = StrictMath.atan2(sinForAtan2, cosForAtan2); + + return encodeRemainderAndQuadrant(negateRem ? -fw : fw, q); + } + + /* + * Our remainders implementations. + */ + + /** + * @param angle Angle, in radians. Must not be NaN nor +-Infinity. + * @return Remainder of (angle % (2*PI)), in [-PI,PI]. + */ + static strictfp double heavyRemainderTwoPi(double angle) { + final long remAndQuad = heavyRemainderPiO2(angle, false); + final double rem = decodeRemainder(remAndQuad); + final int q = decodeQuadrant(remAndQuad); + if (q == 0) { + return rem; + } else if (q == 1) { + return (rem + PIO2_LO) + PIO2_HI; + } else if (q == 2) { + if (rem < 0.0) { + return (rem + PI_LO) + PI_HI; + } else { + return (rem - PI_LO) - PI_HI; + } + } else { + return (rem - PIO2_LO) - PIO2_HI; + } + } + + /** + * @param angle Angle, in radians. Must not be NaN nor +-Infinity. + * @return Remainder of (angle % PI), in [-PI/2,PI/2]. + */ + static strictfp double heavyRemainderPi(double angle) { + final long remAndQuad = heavyRemainderPiO2(angle, false); + final double rem = decodeRemainder(remAndQuad); + final int q = decodeQuadrant(remAndQuad); + if ((q&1) != 0) { + // q is 1 or 3 + if (rem < 0.0) { + return (rem + PIO2_LO) + PIO2_HI; + } else { + return (rem - PIO2_LO) - PIO2_HI; + } + } + return rem; + } + + /** + * Remainder using an accurate definition of PI. + * Derived from a fdlibm treatment called __kernel_rem_pio2. + * + * Not defining a non-strictfp version for FastMath, to avoid duplicating + * its long and messy code, and because it's slow anyway, and should be + * rarely used when speed matters. + * + * @param angle Angle, in radians. Must not be NaN nor +-Infinity. + * @param negateRem True if remainder must be negated before encoded into returned long. + * @return Bits of double corresponding to remainder of (angle % (PI/2)), + * in [-PI/4,PI/4], with quadrant encoded in exponent bits. + */ + static strictfp long heavyRemainderPiO2(double angle, boolean negateRem) { + + /* + * fdlibm treatments unrolled, to avoid garbage and be OOME-free, + * corresponding to: + * 1) initial jk = 4 (precision = 3 = 64 bits (extended)), + * which is more accurate than using precision = 2 + * (53 bits, double), even though we work with doubles + * and use strictfp! + * 2) max lengths of 8 for f[], 6 for q[], fq[] and iq[]. + * 3) at most one recomputation (one goto). + * These limitations were experimentally found to + * be sufficient for billions of random doubles + * of random magnitudes. + * For the rare cases that our unrolled treatments can't handle, + * we fall back to a JDK-based implementation. + */ + + int n,i,j,ih; + double fw; + + /* + * Turning angle into 24-bits integer chunks. + * Done outside __kernel_rem_pio2, but we factor it inside our method. + */ + + // Reworking exponent to have a value < 2^24. + final long lx = Double.doubleToRawLongBits(angle); + final long exp = ((lx>>52)&0x7FF) - (1023+23); + double z = Double.longBitsToDouble(lx - (exp<<52)); + + double x0 = (double)(int)z; + z = (z-x0)*TWO_POW_24; + double x1 = (double)(int)z; + z = (z-x1)*TWO_POW_24; + double x2 = (double)(int)z; + + final int e0 = (int)exp; + // in [1,3] + final int nx = (x2 == 0.0) ? ((x1 == 0.0) ? 1 : 2) : 3; + + /* + * + */ + + double f0,f1,f2,f3,f4,f5,f6,f7; + double q0,q1,q2,q3,q4,q5; + int iq0,iq1,iq2,iq3,iq4,iq5; + + int jk = 4; + + int jx = nx-1; + int jv = Math.max(0,(e0-3)/24); + // In fdlibm, this is q0, but we prefer to use q0 for q[0]. + int qZero = e0-24*(jv+1); + + j = jv-jx; + if (jx == 0) { + f6 = 0.0; + f5 = 0.0; + f4 = (j >= -4) ? TWO_OVER_PI_TAB[j+4] : 0.0; + f3 = (j >= -3) ? TWO_OVER_PI_TAB[j+3] : 0.0; + f2 = (j >= -2) ? TWO_OVER_PI_TAB[j+2] : 0.0; + f1 = (j >= -1) ? TWO_OVER_PI_TAB[j+1] : 0.0; + f0 = (j >= 0) ? TWO_OVER_PI_TAB[j] : 0.0; + + q0 = x0*f0; + q1 = x0*f1; + q2 = x0*f2; + q3 = x0*f3; + q4 = x0*f4; + } else if (jx == 1) { + f6 = 0.0; + f5 = (j >= -5) ? TWO_OVER_PI_TAB[j+5] : 0.0; + f4 = (j >= -4) ? TWO_OVER_PI_TAB[j+4] : 0.0; + f3 = (j >= -3) ? TWO_OVER_PI_TAB[j+3] : 0.0; + f2 = (j >= -2) ? TWO_OVER_PI_TAB[j+2] : 0.0; + f1 = (j >= -1) ? TWO_OVER_PI_TAB[j+1] : 0.0; + f0 = (j >= 0) ? TWO_OVER_PI_TAB[j] : 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 >= -6) ? TWO_OVER_PI_TAB[j+6] : 0.0; + f5 = (j >= -5) ? TWO_OVER_PI_TAB[j+5] : 0.0; + f4 = (j >= -4) ? TWO_OVER_PI_TAB[j+4] : 0.0; + f3 = (j >= -3) ? TWO_OVER_PI_TAB[j+3] : 0.0; + f2 = (j >= -2) ? TWO_OVER_PI_TAB[j+2] : 0.0; + f1 = (j >= -1) ? TWO_OVER_PI_TAB[j+1] : 0.0; + f0 = (j >= 0) ? TWO_OVER_PI_TAB[j] : 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; + } + + double twoPowQZero = twoPowNormal(qZero); + + int jz = jk; + + /* + * Unrolling of first round. + */ + + 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; + iq4 = 0; + iq5 = 0; + + z = (z*twoPowQZero) % 8.0; + n = (int)z; + z -= (double)n; + + ih = 0; + if (qZero > 0) { + // Parentheses against code formatter bug. + i = (iq3>>(24-qZero)); + n += i; + iq3 -= i<<(24-qZero); + ih = iq3>>(23-qZero); + } else if (qZero == 0) { + ih = iq3>>23; + } else if (z >= 0.5) { + ih = 2; + } + + if (ih > 0) { + n += 1; + // carry = 1 is common case, + // so using it as initial value. + int carry = 1; + if (iq0 != 0) { + iq0 = 0x1000000 - iq0; + iq1 = 0xFFFFFF - iq1; + iq2 = 0xFFFFFF - iq2; + iq3 = 0xFFFFFF - iq3; + } else if (iq1 != 0) { + iq1 = 0x1000000 - iq1; + iq2 = 0xFFFFFF - iq2; + iq3 = 0xFFFFFF - iq3; + } else if (iq2 != 0) { + iq2 = 0x1000000 - iq2; + iq3 = 0xFFFFFF - iq3; + } else if (iq3 != 0) { + iq3 = 0x1000000 - iq3; + } else { + carry = 0; + } + if (qZero > 0) { + if (qZero == 1) { + iq3 &= 0x7FFFFF; + } else if (qZero == 2) { + iq3 &= 0x3FFFFF; + } + } + if (ih == 2) { + z = 1.0 - z; + if (carry != 0) { + z -= twoPowQZero; + } + } + } + + if (z == 0.0) { + if (iq3 == 0) { + // With random values of random magnitude, + // probability for this to happen seems lower than 1e-6. + // jz would be more than just incremented by one, + // which our unrolling doesn't support. + return jdkRemainderPiO2(angle, negateRem); + } + if (jx == 0) { + f5 = TWO_OVER_PI_TAB[jv+5]; + q5 = x0*f5; + } else if (jx == 1) { + f6 = TWO_OVER_PI_TAB[jv+5]; + q5 = x0*f6 + x1*f5; + } else { // jx == 2 + f7 = TWO_OVER_PI_TAB[jv+5]; + q5 = x0*f7 + x1*f6 + x2*f5; + } + + jz++; + + /* + * Unrolling of second round. + */ + + 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; + iq5 = 0; + + z = (z*twoPowQZero) % 8.0; + n = (int)z; + z -= (double)n; + + ih = 0; + if (qZero > 0) { + // Parentheses against code formatter bug. + i = (iq4>>(24-qZero)); + n += i; + iq4 -= i<<(24-qZero); + ih = iq4>>(23-qZero); + } else if (qZero == 0) { + ih = iq4>>23; + } else if (z >= 0.5) { + ih = 2; + } + + if (ih > 0) { + n += 1; + // carry = 1 is common case, + // so using it as initial value. + int carry = 1; + if (iq0 != 0) { + iq0 = 0x1000000 - iq0; + iq1 = 0xFFFFFF - iq1; + iq2 = 0xFFFFFF - iq2; + iq3 = 0xFFFFFF - iq3; + iq4 = 0xFFFFFF - iq4; + } else if (iq1 != 0) { + iq1 = 0x1000000 - iq1; + iq2 = 0xFFFFFF - iq2; + iq3 = 0xFFFFFF - iq3; + iq4 = 0xFFFFFF - iq4; + } else if (iq2 != 0) { + iq2 = 0x1000000 - iq2; + iq3 = 0xFFFFFF - iq3; + iq4 = 0xFFFFFF - iq4; + } else if (iq3 != 0) { + iq3 = 0x1000000 - iq3; + iq4 = 0xFFFFFF - iq4; + } else if (iq4 != 0) { + iq4 = 0x1000000 - iq4; + } else { + carry = 0; + } + if (qZero > 0) { + if (qZero == 1) { + iq4 &= 0x7FFFFF; + } else if (qZero == 2) { + iq4 &= 0x3FFFFF; + } + } + if (ih == 2) { + z = 1.0 - z; + if (carry != 0) { + z -= twoPowQZero; + } + } + } + + if (z == 0.0) { + if (iq4 == 0) { + // Case not encountered in tests, but still handling it. + // Would require a third loop unrolling. + return jdkRemainderPiO2(angle, negateRem); + } else { + // z == 0.0, and iq4 != 0, + // so we remove 24 from qZero only once, + // but since we no longer use qZero, + // we just bother to multiply its 2-power + // by 2^-24. + jz--; + twoPowQZero *= TWO_POW_N24; + } + } else { + // z != 0.0 at end of second round. + } + } else { + // z != 0.0 at end of first round. + } + + /* + * After loop. + */ + + if (z != 0.0) { + z /= twoPowQZero; + if (z >= TWO_POW_24) { + fw = (double)(int)(TWO_POW_N24*z); + if (jz == jk) { + iq4 = (int)(z-TWO_POW_24*fw); + jz++; // jz to 5 + // Not using qZero anymore so not updating it. + twoPowQZero *= TWO_POW_24; + iq5 = (int)fw; + } else { // jz == jk+1 == 5 + // Case not encountered in tests, but still handling it. + // Would require use of iq6, with jz = 6. + return jdkRemainderPiO2(angle, negateRem); + } + } else { + if (jz == jk) { + iq4 = (int)z; + } else { // jz == jk+1 == 5 + // Case not encountered in tests, but still handling it. + iq5 = (int)z; + } + } + } + + fw = twoPowQZero; + + if (jz == 5) { + q5 = fw*(double)iq5; + fw *= TWO_POW_N24; + } else { + q5 = 0.0; + } + 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; + + /* + * We just use HI part of the result. + */ + + fw = PIO2_TAB0*q5; + fw += PIO2_TAB0*q4 + PIO2_TAB1*q5; + fw += PIO2_TAB0*q3 + PIO2_TAB1*q4 + PIO2_TAB2*q5; + fw += PIO2_TAB0*q2 + PIO2_TAB1*q3 + PIO2_TAB2*q4 + PIO2_TAB3*q5; + fw += PIO2_TAB0*q1 + PIO2_TAB1*q2 + PIO2_TAB2*q3 + PIO2_TAB3*q4 + PIO2_TAB4*q5; + fw += PIO2_TAB0*q0 + PIO2_TAB1*q1 + PIO2_TAB2*q2 + PIO2_TAB3*q3 + PIO2_TAB4*q4 + PIO2_TAB5*q5; + + if ((ih != 0) ^ negateRem) { + fw = -fw; + } + + return encodeRemainderAndQuadrant(fw, n&3); + } + + //-------------------------------------------------------------------------- + // PRIVATE METHODS + //-------------------------------------------------------------------------- + + /** + * Redefined here, to avoid cyclic dependency with (Strict)FastMath. + * + * @param value A double value. + * @return -1 if sign bit is 1, 1 if sign bit is 0. + */ + private static long signFromBit_antiCyclic(double value) { + // Returning a long, to avoid useless cast into int. + return ((Double.doubleToRawLongBits(value)>>62)|1); + } + + private static boolean getBooleanProperty( + final String key, + boolean defaultValue) { + final String tmp = System.getProperty(key); + if (tmp != null) { + return Boolean.parseBoolean(tmp); + } else { + return defaultValue; + } + } + + /** + * 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 (FM_USE_JDK_MATH && SFM_USE_JDK_MATH) ? Math.min(2, tabSizePower) : tabSizePower; + } +} diff --git a/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/DoubleWrapper.java b/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/DoubleWrapper.java new file mode 100644 index 000000000..e7adc8d59 --- /dev/null +++ b/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/DoubleWrapper.java @@ -0,0 +1,13 @@ +/* + * Copyright 2018-2021 KMath contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ +package space.kscience.kmath.jafama; + +public class DoubleWrapper { + public double value; + @Override + public String toString() { + return Double.toString(this.value); + } +} diff --git a/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/FastMath.java b/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/FastMath.java new file mode 100644 index 000000000..a83c01f7b --- /dev/null +++ b/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/FastMath.java @@ -0,0 +1,2986 @@ +/* + * Copyright 2018-2021 KMath contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package space.kscience.kmath.jafama; + +/** + * Faster (hopefully) versions of java.lang.Math methods, plus additional ones. + * Cf. README.txt for more info. + */ +public final class FastMath extends CmnFastMath { + + //-------------------------------------------------------------------------- + // CONFIGURATION + //-------------------------------------------------------------------------- + + private static final boolean USE_JDK_MATH = FM_USE_JDK_MATH; + + private static final boolean USE_REDEFINED_LOG = FM_USE_REDEFINED_LOG; + + private static final boolean USE_REDEFINED_SQRT = FM_USE_REDEFINED_SQRT; + + private static final boolean USE_POWTABS_FOR_ASIN = FM_USE_POWTABS_FOR_ASIN; + + //-------------------------------------------------------------------------- + // PUBLIC METHODS + //-------------------------------------------------------------------------- + + /* + * trigonometry + */ + + /** + * @param angle Angle in radians. + * @return Angle sine. + */ + public static double sin(double angle) { + if (USE_JDK_MATH) { + return Math.sin(angle); + } + boolean negateResult = false; + if (angle < 0.0) { + angle = -angle; + negateResult = true; + } + if (angle > SIN_COS_MAX_VALUE_FOR_INT_MODULO) { + if (false) { + // Can give very bad relative error near PI (mod 2*PI). + angle = remainderTwoPi(angle); + if (angle < 0.0) { + angle = -angle; + negateResult = !negateResult; + } + } else { + final long remAndQuad = remainderPiO2(angle); + angle = decodeRemainder(remAndQuad); + final double sin; + final int q = decodeQuadrant(remAndQuad); + if (q == 0) { + sin = sin(angle); + } else if (q == 1) { + sin = cos(angle); + } else if (q == 2) { + sin = -sin(angle); + } else { + sin = -cos(angle); + } + return (negateResult ? -sin : sin); + } + } + // 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 indexSin = MyTSinCos.sinTab[index]; + double indexCos = MyTSinCos.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; + } + + /** + * Quick sin, with accuracy of about 1.6e-3 (PI/) + * for |angle| < 6588395.0 (Integer.MAX_VALUE * (2*PI/) - 2) + * (- 2 due to removing PI/2 before using cosine tab), + * and no accuracy at all for larger values. + * + * @param angle Angle in radians. + * @return Angle sine. + */ + public static double sinQuick(double angle) { + if (USE_JDK_MATH) { + return Math.sin(angle); + } + return MyTSinCos.cosTab[((int)(Math.abs(angle-Math.PI/2) * SIN_COS_INDEXER + 0.5)) & (SIN_COS_TABS_SIZE-2)]; + } + + /** + * @param angle Angle in radians. + * @return Angle cosine. + */ + public static double cos(double angle) { + if (USE_JDK_MATH) { + return Math.cos(angle); + } + angle = Math.abs(angle); + if (angle > SIN_COS_MAX_VALUE_FOR_INT_MODULO) { + if (false) { + // Can give very bad relative error near PI (mod 2*PI). + angle = remainderTwoPi(angle); + if (angle < 0.0) { + angle = -angle; + } + } else { + final long remAndQuad = remainderPiO2(angle); + angle = decodeRemainder(remAndQuad); + final double cos; + final int q = decodeQuadrant(remAndQuad); + if (q == 0) { + cos = cos(angle); + } else if (q == 1) { + cos = -sin(angle); + } else if (q == 2) { + cos = -cos(angle); + } else { + cos = sin(angle); + } + return cos; + } + } + // 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 = MyTSinCos.cosTab[index]; + double indexSin = MyTSinCos.sinTab[index]; + return indexCos + delta * (-indexSin + delta * (-indexCos * ONE_DIV_F2 + delta * (indexSin * ONE_DIV_F3 + delta * indexCos * ONE_DIV_F4))); + } + + /** + * Quick cos, with accuracy of about 1.6e-3 (PI/) + * for |angle| < 6588397.0 (Integer.MAX_VALUE * (2*PI/)), + * and no accuracy at all for larger values. + * + * @param angle Angle in radians. + * @return Angle cosine. + */ + public static double cosQuick(double angle) { + if (USE_JDK_MATH) { + return Math.cos(angle); + } + return MyTSinCos.cosTab[((int)(Math.abs(angle) * SIN_COS_INDEXER + 0.5)) & (SIN_COS_TABS_SIZE-2)]; + } + + /** + * Computes sine and cosine together. + * + * @param angle Angle in radians. + * @param cosine (out) Angle cosine. + * @return Angle sine. + */ + public static double sinAndCos(double angle, DoubleWrapper cosine) { + if (USE_JDK_MATH) { + cosine.value = Math.cos(angle); + return Math.sin(angle); + } + // Using the same algorithm than sin(double) method, + // and computing also cosine at the end. + boolean negateResult = false; + if (angle < 0.0) { + angle = -angle; + negateResult = true; + } + if (angle > SIN_COS_MAX_VALUE_FOR_INT_MODULO) { + if (false) { + // Can give very bad relative error near PI (mod 2*PI). + angle = remainderTwoPi(angle); + if (angle < 0.0) { + angle = -angle; + negateResult = !negateResult; + } + } else { + final long remAndQuad = remainderPiO2(angle); + angle = decodeRemainder(remAndQuad); + final double sin; + final int q = decodeQuadrant(remAndQuad); + if (q == 0) { + sin = sin(angle); + cosine.value = cos(angle); + } else if (q == 1) { + sin = cos(angle); + cosine.value = -sin(angle); + } else if (q == 2) { + sin = -sin(angle); + cosine.value = -cos(angle); + } else { + sin = -cos(angle); + cosine.value = sin(angle); + } + return (negateResult ? -sin : sin); + } + } + 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 = MyTSinCos.sinTab[index]; + double indexCos = MyTSinCos.cosTab[index]; + // Could factor some multiplications (delta * factorials), but then is less accurate. + cosine.value = indexCos + delta * (-indexSin + delta * (-indexCos * ONE_DIV_F2 + delta * (indexSin * ONE_DIV_F3 + delta * indexCos * ONE_DIV_F4))); + double result = indexSin + delta * (indexCos + delta * (-indexSin * ONE_DIV_F2 + delta * (-indexCos * ONE_DIV_F3 + delta * indexSin * ONE_DIV_F4))); + return negateResult ? -result : result; + } + + /** + * Can have very bad relative error near +-PI/2, + * but of the same magnitude than the relative delta between + * StrictMath.tan(PI/2) and StrictMath.tan(nextDown(PI/2)). + * + * @param angle Angle in radians. + * @return Angle tangent. + */ + public static double tan(double angle) { + if (USE_JDK_MATH) { + return Math.tan(angle); + } + boolean negateResult = false; + if (angle < 0.0) { + angle = -angle; + negateResult = true; + } + if (angle > TAN_MAX_VALUE_FOR_INT_MODULO) { + angle = remainderPi(angle); + if (angle < 0.0) { + angle = -angle; + negateResult = !negateResult; + } + } + // index: possibly outside tables range. + int index = (int)(angle * TAN_INDEXER + 0.5); + double delta = (angle - index * TAN_DELTA_HI) - index * TAN_DELTA_LO; + // Making sure index is within tables range. + // 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; + } + double result; + if (index < TAN_TABS_SIZE) { + result = MyTTan.tanTab[index] + + delta * (MyTTan.tanDer1DivF1Tab[index] + + delta * (MyTTan.tanDer2DivF2Tab[index] + + delta * (MyTTan.tanDer3DivF3Tab[index] + + delta * MyTTan.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/(MyTTan.tanTab[index] + - delta * (MyTTan.tanDer1DivF1Tab[index] + - delta * (MyTTan.tanDer2DivF2Tab[index] + - delta * (MyTTan.tanDer3DivF3Tab[index] + - delta * MyTTan.tanDer4DivF4Tab[index])))); + } + return negateResult ? -result : result; + } + + /** + * @param value Value in [-1,1]. + * @return Value arcsine, in radians, in [-PI/2,PI/2]. + */ + public static double asin(double value) { + if (USE_JDK_MATH) { + return Math.asin(value); + } + boolean negateResult = false; + if (value < 0.0) { + value = -value; + negateResult = true; + } + if (value <= ASIN_MAX_VALUE_FOR_TABS) { + int index = (int)(value * ASIN_INDEXER + 0.5); + double delta = value - index * ASIN_DELTA; + double result = MyTAsin.asinTab[index] + + delta * (MyTAsin.asinDer1DivF1Tab[index] + + delta * (MyTAsin.asinDer2DivF2Tab[index] + + delta * (MyTAsin.asinDer3DivF3Tab[index] + + delta * MyTAsin.asinDer4DivF4Tab[index]))); + return negateResult ? -result : result; + } else if (USE_POWTABS_FOR_ASIN && (value <= ASIN_MAX_VALUE_FOR_POWTABS)) { + int index = (int)(powFast(value * ASIN_POWTABS_ONE_DIV_MAX_VALUE, ASIN_POWTABS_POWER) * ASIN_POWTABS_SIZE_MINUS_ONE + 0.5); + double delta = value - MyTAsinPow.asinParamPowTab[index]; + double result = MyTAsinPow.asinPowTab[index] + + delta * (MyTAsinPow.asinDer1DivF1PowTab[index] + + delta * (MyTAsinPow.asinDer2DivF2PowTab[index] + + delta * (MyTAsinPow.asinDer3DivF3PowTab[index] + + delta * MyTAsinPow.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 = 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; + } + } + } + } + + /** + * If value is not NaN and is outside [-1,1] range, closest value in this range is used. + * + * @param value Value in [-1,1]. + * @return Value arcsine, in radians, in [-PI/2,PI/2]. + */ + public static double asinInRange(double value) { + if (value <= -1.0) { + return -Math.PI/2; + } else if (value >= 1.0) { + return Math.PI/2; + } else { + return asin(value); + } + } + + /** + * @param value Value in [-1,1]. + * @return Value arccosine, in radians, in [0,PI]. + */ + public static double acos(double value) { + if (USE_JDK_MATH) { + return Math.acos(value); + } + return Math.PI/2 - asin(value); + } + + /** + * If value is not NaN and is outside [-1,1] range, + * closest value in this range is used. + * + * @param value Value in [-1,1]. + * @return Value arccosine, in radians, in [0,PI]. + */ + public static double acosInRange(double value) { + if (value <= -1.0) { + return Math.PI; + } else if (value >= 1.0) { + return 0.0; + } else { + return acos(value); + } + } + + /** + * @param value A double value. + * @return Value arctangent, in radians, in [-PI/2,PI/2]. + */ + public static double atan(double value) { + if (USE_JDK_MATH) { + return Math.atan(value); + } + boolean negateResult = false; + if (value < 0.0) { + value = -value; + negateResult = true; + } + 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 = MyTAtan.atanTab[index] + + delta * (MyTAtan.atanDer1DivF1Tab[index] + + delta * (MyTAtan.atanDer2DivF2Tab[index] + + delta * (MyTAtan.atanDer3DivF3Tab[index] + + delta * MyTAtan.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 (value != 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 (USE_JDK_MATH) { + return Math.atan2(y,x); + } + /* + * Using sub-methods, to make method lighter for general case, + * and to avoid JIT-optimization crash on NaN. + */ + if (x > 0.0) { + if (y == 0.0) { + // +-0.0 + return y; + } + if (x == Double.POSITIVE_INFINITY) { + return atan2_pinf_yyy(y); + } else { + return atan(y/x); + } + } else if (x < 0.0) { + if (y == 0.0) { + return signFromBit(y) * Math.PI; + } + if (x == Double.NEGATIVE_INFINITY) { + return atan2_ninf_yyy(y); + } else if (y > 0.0) { + return Math.PI/2 - atan(x/y); + } else if (y < 0.0) { + return -Math.PI/2 - atan(x/y); + } else { + return Double.NaN; + } + } else { + return atan2_yyy_zeroOrNaN(y, x); + } + } + + /** + * Gives same result as Math.toRadians for some particular values + * like 90.0, 180.0 or 360.0, but is faster (no division). + * + * @param angdeg Angle value in degrees. + * @return Angle value in radians. + */ + public static double toRadians(double angdeg) { + if (USE_JDK_MATH) { + return Math.toRadians(angdeg); + } + return angdeg * (Math.PI/180); + } + + /** + * Gives same result as Math.toDegrees for some particular values + * like Math.PI/2, Math.PI or 2*Math.PI, but is faster (no division). + * + * @param angrad Angle value in radians. + * @return Angle value in degrees. + */ + public static double toDegrees(double angrad) { + if (USE_JDK_MATH) { + return Math.toDegrees(angrad); + } + return angrad * (180/Math.PI); + } + + /** + * @param sign Sign of the angle: true for positive, false for negative. + * @param degrees Degrees, in [0,180]. + * @param minutes Minutes, in [0,59]. + * @param seconds Seconds, in [0.0,60.0[. + * @return Angle in radians. + */ + public static double toRadians(boolean sign, int degrees, int minutes, double seconds) { + return toRadians(toDegrees(sign, degrees, minutes, seconds)); + } + + /** + * @param sign Sign of the angle: true for positive, false for negative. + * @param degrees Degrees, in [0,180]. + * @param minutes Minutes, in [0,59]. + * @param seconds Seconds, in [0.0,60.0[. + * @return Angle in degrees. + */ + public static double toDegrees(boolean sign, int degrees, int minutes, double seconds) { + double signFactor = sign ? 1.0 : -1.0; + return signFactor * (degrees + (1.0/60)*(minutes + (1.0/60)*seconds)); + } + + /** + * @param angrad Angle in radians. + * @param degrees (out) Degrees, in [0,180]. + * @param minutes (out) Minutes, in [0,59]. + * @param seconds (out) Seconds, in [0.0,60.0[. + * @return true if the resulting angle in [-180deg,180deg] is positive, false if it is negative. + */ + public static boolean toDMS(double angrad, IntWrapper degrees, IntWrapper minutes, DoubleWrapper seconds) { + // Computing longitude DMS. + double tmp = toDegrees(normalizeMinusPiPi(angrad)); + boolean isNeg = (tmp < 0.0); + if (isNeg) { + tmp = -tmp; + } + degrees.value = (int)tmp; + tmp = (tmp-degrees.value)*60.0; + minutes.value = (int)tmp; + seconds.value = Math.min((tmp-minutes.value)*60.0,DOUBLE_BEFORE_60); + return !isNeg; + } + + /** + * NB: Since 2*Math.PI < 2*PI, a span of 2*Math.PI does not mean full angular range. + * ex.: isInClockwiseDomain(0.0, 2*Math.PI, -1e-20) returns false. + * ---> For full angular range, use a span > 2*Math.PI, like 2*PI_SUP constant of this class. + * + * @param startAngRad An angle, in radians. + * @param angSpanRad An angular span, >= 0.0, in radians. + * @param angRad An angle, in radians. + * @return true if angRad is in the clockwise angular domain going from startAngRad, over angSpanRad, + * extremities included, false otherwise. + */ + public static boolean isInClockwiseDomain(double startAngRad, double angSpanRad, double angRad) { + if (Math.abs(angRad) < -TWO_MATH_PI_IN_MINUS_PI_PI) { + // special case for angular values of small magnitude + if (angSpanRad <= 2*Math.PI) { + if (angSpanRad < 0.0) { + // empty domain + return false; + } + // angSpanRad is in [0,2*PI] + startAngRad = normalizeMinusPiPi(startAngRad); + double endAngRad = normalizeMinusPiPi(startAngRad + angSpanRad); + if (startAngRad <= endAngRad) { + return (angRad >= startAngRad) && (angRad <= endAngRad); + } else { + return (angRad >= startAngRad) || (angRad <= endAngRad); + } + } else { // angSpanRad > 2*Math.PI, or is NaN + return (angSpanRad == angSpanRad); + } + } else { + // general case + return (normalizeZeroTwoPi(angRad - startAngRad) <= angSpanRad); + } + } + + /* + * hyperbolic trigonometry + */ + + /** + * Some properties of sinh(x) = (exp(x)-exp(-x))/2: + * 1) defined on ]-Infinity,+Infinity[ + * 2) result in ]-Infinity,+Infinity[ + * 3) sinh(x) = -sinh(-x) (implies sinh(0) = 0) + * 4) sinh(epsilon) ~= epsilon + * 5) lim(sinh(x),x->+Infinity) = +Infinity + * (y increasing exponentially faster than x) + * 6) reaches +Infinity (double overflow) for x >= 710.475860073944, + * i.e. a bit further than exp(x) + * + * @param value A double value. + * @return Value hyperbolic sine. + */ + public static double sinh(double value) { + if (USE_JDK_MATH) { + return Math.sinh(value); + } + // sinh(x) = (exp(x)-exp(-x))/2 + double h; + if (value < 0.0) { + value = -value; + h = -0.5; + } else { + h = 0.5; + } + if (value < 22.0) { + if (value < TWO_POW_N28) { + return (h < 0.0) ? -value : value; + } else { + // sinh(x) + // = (exp(x)-exp(-x))/2 + // = (exp(x)-1/exp(x))/2 + // = (expm1(x) + 1 - 1/(expm1(x)+1))/2 + // = (expm1(x) + (expm1(x)+1)/(expm1(x)+1) - 1/(expm1(x)+1))/2 + // = (expm1(x) + expm1(x)/(expm1(x)+1))/2 + double t = expm1(value); + // Might be more accurate, if value < 1: return h*((t+t)-t*t/(t+1.0)). + return h * (t + t/(t+1.0)); + } + } else if (value < LOG_DOUBLE_MAX_VALUE) { + return h * exp(value); + } else { + double t = exp(value*0.5); + return (h*t)*t; + } + } + + /** + * Some properties of cosh(x) = (exp(x)+exp(-x))/2: + * 1) defined on ]-Infinity,+Infinity[ + * 2) result in [1,+Infinity[ + * 3) cosh(0) = 1 + * 4) cosh(x) = cosh(-x) + * 5) lim(cosh(x),x->+Infinity) = +Infinity + * (y increasing exponentially faster than x) + * 6) reaches +Infinity (double overflow) for x >= 710.475860073944, + * i.e. a bit further than exp(x) + * + * @param value A double value. + * @return Value hyperbolic cosine. + */ + public static double cosh(double value) { + if (USE_JDK_MATH) { + return Math.cosh(value); + } + // cosh(x) = (exp(x)+exp(-x))/2 + if (value < 0.0) { + value = -value; + } + if (value < LOG_TWO_POW_27) { + if (value < TWO_POW_N27) { + // cosh(x) + // = (exp(x)+exp(-x))/2 + // = ((1+x+x^2/2!+...) + (1-x+x^2/2!-...))/2 + // = 1+x^2/2!+x^4/4!+... + // For value of x small in magnitude, the sum of the terms does not add to 1. + return 1; + } else { + // cosh(x) + // = (exp(x)+exp(-x))/2 + // = (exp(x)+1/exp(x))/2 + double t = exp(value); + return 0.5 * (t+1/t); + } + } else if (value < LOG_DOUBLE_MAX_VALUE) { + return 0.5 * exp(value); + } else { + double t = exp(value*0.5); + return (0.5*t)*t; + } + } + + /** + * Much more accurate than cosh(value)-1, + * for arguments (and results) close to zero. + * + * coshm1(-0.0) = -0.0, for homogeneity with + * acosh1p(-0.0) = -0.0. + * + * @param value A double value. + * @return Value hyperbolic cosine, minus 1. + */ + public static double coshm1(double value) { + // cosh(x)-1 = (exp(x)+exp(-x))/2 - 1 + if (value < 0.0) { + value = -value; + } + if (value < LOG_TWO_POW_27) { + if (value < TWO_POW_N27) { + if (value == 0.0) { + // +-0.0 + return value; + } + // Using (expm1(x)+expm1(-x))/2 + // is not accurate for tiny values, + // for expm1 results are of higher + // magnitude than the result and + // of different signs, such as their + // sum is not accurate. + // cosh(x) - 1 + // = (exp(x)+exp(-x))/2 - 1 + // = ((1+x+x^2/2!+...) + (1-x+x^2/2!-...))/2 - 1 + // = x^2/2!+x^4/4!+... + // ~= x^2 * (1/2 + x^2 * 1/24) + // = x^2 * 0.5 (since x < 2^-27) + return 0.5 * value*value; + } else { + // cosh(x) - 1 + // = (exp(x)+exp(-x))/2 - 1 + // = (exp(x)-1+exp(-x)-1)/2 + // = (expm1(x)+expm1(-x))/2 + return 0.5 * (expm1(value)+expm1(-value)); + } + } else if (value < LOG_DOUBLE_MAX_VALUE) { + return 0.5 * exp(value) - 1.0; + } else { + // No need to subtract 1 from result. + double t = exp(value*0.5); + return (0.5*t)*t; + } + } + + /** + * Computes hyperbolic sine and hyperbolic cosine together. + * + * @param value A double value. + * @param hcosine (out) Value hyperbolic cosine. + * @return Value hyperbolic sine. + */ + public static double sinhAndCosh(double value, DoubleWrapper hcosine) { + if (USE_JDK_MATH) { + hcosine.value = Math.cosh(value); + return Math.sinh(value); + } + // Mixup of sinh and cosh treatments: if you modify them, + // you might want to also modify this. + double h; + if (value < 0.0) { + value = -value; + h = -0.5; + } else { + h = 0.5; + } + final double hsine; + // LOG_TWO_POW_27 = 18.714973875118524 + if (value < LOG_TWO_POW_27) { // test from cosh + // sinh + if (value < TWO_POW_N28) { + hsine = (h < 0.0) ? -value : value; + } else { + double t = expm1(value); + hsine = h * (t + t/(t+1.0)); + } + // cosh + if (value < TWO_POW_N27) { + hcosine.value = 1; + } else { + double t = exp(value); + hcosine.value = 0.5 * (t+1/t); + } + } else if (value < 22.0) { // test from sinh + // Here, value is in [18.714973875118524,22.0[. + double t = expm1(value); + hsine = h * (t + t/(t+1.0)); + hcosine.value = 0.5 * (t+1.0); + } else { + if (value < LOG_DOUBLE_MAX_VALUE) { + hsine = h * exp(value); + } else { + double t = exp(value*0.5); + hsine = (h*t)*t; + } + hcosine.value = Math.abs(hsine); + } + return hsine; + } + + /** + * Some properties of tanh(x) = sinh(x)/cosh(x) = (exp(2*x)-1)/(exp(2*x)+1): + * 1) defined on ]-Infinity,+Infinity[ + * 2) result in ]-1,1[ + * 3) tanh(x) = -tanh(-x) (implies tanh(0) = 0) + * 4) tanh(epsilon) ~= epsilon + * 5) lim(tanh(x),x->+Infinity) = 1 + * 6) reaches 1 (double loss of precision) for x = 19.061547465398498 + * + * @param value A double value. + * @return Value hyperbolic tangent. + */ + public static double tanh(double value) { + if (USE_JDK_MATH) { + return Math.tanh(value); + } + // tanh(x) = sinh(x)/cosh(x) + // = (exp(x)-exp(-x))/(exp(x)+exp(-x)) + // = (exp(2*x)-1)/(exp(2*x)+1) + boolean negateResult = false; + if (value < 0.0) { + value = -value; + negateResult = true; + } + double z; + if (value < TANH_1_THRESHOLD) { + if (value < TWO_POW_N55) { + return negateResult ? -value*(1.0-value) : value*(1.0+value); + } else if (value >= 1) { + z = 1.0-2.0/(expm1(value+value)+2.0); + } else { + double t = expm1(-(value+value)); + z = -t/(t+2.0); + } + } else { + z = (value != value) ? Double.NaN : 1.0; + } + return negateResult ? -z : z; + } + + /** + * Some properties of asinh(x) = log(x + sqrt(x^2 + 1)) + * 1) defined on ]-Infinity,+Infinity[ + * 2) result in ]-Infinity,+Infinity[ + * 3) asinh(x) = -asinh(-x) (implies asinh(0) = 0) + * 4) asinh(epsilon) ~= epsilon + * 5) lim(asinh(x),x->+Infinity) = +Infinity + * (y increasing logarithmically slower than x) + * + * @param value A double value. + * @return Value hyperbolic arcsine. + */ + public static double asinh(double value) { + // asinh(x) = log(x + sqrt(x^2 + 1)) + boolean negateResult = false; + if (value < 0.0) { + value = -value; + negateResult = true; + } + double result; + // (about) smallest possible for + // non-log1p case to be accurate. + if (value < ASINH_LOG1P_THRESHOLD) { + // Around this range, FDLIBM uses + // log1p(value+value*value/(1+sqrt(value*value+1))), + // but it's slower, so we don't use it. + /* + * If x is close to zero, log argument is close to 1, + * so to avoid precision loss we use log1p(double), + * with + * (1+x)^p = 1 + p * x + (p*(p-1))/2! * x^2 + (p*(p-1)*(p-2))/3! * x^3 + ... + * (1+x)^p = 1 + p * x * (1 + (p-1)/2 * x * (1 + (p-2)/3 * x + ...) + * (1+x)^0.5 = 1 + 0.5 * x * (1 + (0.5-1)/2 * x * (1 + (0.5-2)/3 * x + ...) + * (1+x^2)^0.5 = 1 + 0.5 * x^2 * (1 + (0.5-1)/2 * x^2 * (1 + (0.5-2)/3 * x^2 + ...) + * x + (1+x^2)^0.5 = 1 + x * (1 + 0.5 * x * (1 + (0.5-1)/2 * x^2 * (1 + (0.5-2)/3 * x^2 + ...)) + * so + * asinh(x) = log1p(x * (1 + 0.5 * x * (1 + (0.5-1)/2 * x^2 * (1 + (0.5-2)/3 * x^2 + ...))) + */ + final double x = value; + final double x2 = x*x; + // Enough terms for good accuracy, + // given our threshold. + final double argLog1p = (x * + (1 + 0.5 * x + * (1 + (0.5-1)/2 * x2 + * (1 + (0.5-2)/3 * x2 + * (1 + (0.5-3)/4 * x2 + * (1 + (0.5-4)/5 * x2 + )))))); + result = log1p(argLog1p); + } else if (value < ASINH_ACOSH_SQRT_ELISION_THRESHOLD) { + // Around this range, FDLIBM uses + // log(2*value+1/(value+sqrt(value*value+1))), + // but it involves an additional division + // so we don't use it. + result = log(value + sqrt(value*value + 1.0)); + } else { + // log(2*value) would overflow for value > Double.MAX_VALUE/2, + // so we compute otherwise. + result = LOG_2 + log(value); + } + return negateResult ? -result : result; + } + + /** + * Some properties of acosh(x) = log(x + sqrt(x^2 - 1)): + * 1) defined on [1,+Infinity[ + * 2) result in ]0,+Infinity[ (by convention, since cosh(x) = cosh(-x)) + * 3) acosh(1) = 0 + * 4) acosh(1+epsilon) ~= log(1 + sqrt(2*epsilon)) ~= sqrt(2*epsilon) + * 5) lim(acosh(x),x->+Infinity) = +Infinity + * (y increasing logarithmically slower than x) + * + * @param value A double value. + * @return Value hyperbolic arccosine. + */ + public static double acosh(double value) { + if (!(value > 1.0)) { + // NaN, or value <= 1 + if (ANTI_JIT_OPTIM_CRASH_ON_NAN) { + return (value < 1.0) ? Double.NaN : value - 1.0; + } else { + return (value == 1.0) ? 0.0 : Double.NaN; + } + } + double result; + if (value < ASINH_ACOSH_SQRT_ELISION_THRESHOLD) { + // Around this range, FDLIBM uses + // log(2*value-1/(value+sqrt(value*value-1))), + // but it involves an additional division + // so we don't use it. + result = log(value + sqrt(value*value - 1.0)); + } else { + // log(2*value) would overflow for value > Double.MAX_VALUE/2, + // so we compute otherwise. + result = LOG_2 + log(value); + } + return result; + } + + /** + * Much more accurate than acosh(1+value), + * for arguments (and results) close to zero. + * + * acosh1p(-0.0) = -0.0, for homogeneity with + * sqrt(-0.0) = -0.0, which looks about the same + * near 0. + * + * @param value A double value. + * @return Hyperbolic arccosine of (1+value). + */ + public static double acosh1p(double value) { + if (!(value > 0.0)) { + // NaN, or value <= 0. + // If value is -0.0, returning -0.0. + if (ANTI_JIT_OPTIM_CRASH_ON_NAN) { + return (value < 0.0) ? Double.NaN : value; + } else { + return (value == 0.0) ? value : Double.NaN; + } + } + double result; + if (value < (ASINH_ACOSH_SQRT_ELISION_THRESHOLD-1)) { + // acosh(1+x) + // = log((1+x) + sqrt((1+x)^2 - 1)) + // = log(1 + x + sqrt(1 + 2*x + x^2 - 1)) + // = log1p(x + sqrt(2*x + x^2)) + // = log1p(x + sqrt(x * (2 + x)) + result = log1p(value + sqrt(value * (2 + value))); + } else { + result = LOG_2 + log(1+value); + } + return result; + } + + /** + * Some properties of atanh(x) = log((1+x)/(1-x))/2: + * 1) defined on ]-1,1[ + * 2) result in ]-Infinity,+Infinity[ + * 3) atanh(-1) = -Infinity (by continuity) + * 4) atanh(1) = +Infinity (by continuity) + * 5) atanh(epsilon) ~= epsilon + * 6) lim(atanh(x),x->1) = +Infinity + * + * @param value A double value. + * @return Value hyperbolic arctangent. + */ + public static double atanh(double value) { + boolean negateResult = false; + if (value < 0.0) { + value = -value; + negateResult = true; + } + double result; + if (!(value < 1.0)) { + // NaN, or value >= 1 + if (ANTI_JIT_OPTIM_CRASH_ON_NAN) { + result = (value > 1.0) ? Double.NaN : Double.POSITIVE_INFINITY + value; + } else { + result = (value == 1.0) ? Double.POSITIVE_INFINITY : Double.NaN; + } + } else { + // For value < 0.5, FDLIBM uses + // 0.5 * log1p((value+value) + (value+value)*value/(1-value)), + // instead, but this is good enough for us. + // atanh(x) + // = log((1+x)/(1-x))/2 + // = log((1-x+2x)/(1-x))/2 + // = log1p(2x/(1-x))/2 + result = 0.5 * log1p((value+value)/(1.0-value)); + } + return negateResult ? -result : result; + } + + /* + * exponentials + */ + + /** + * @param value A double value. + * @return e^value. + */ + public static double exp(double value) { + if (USE_JDK_MATH) { + return Math.exp(value); + } + // exp(x) = exp([x])*exp(y) + // with [x] the integer part of x, and y = x-[x] + // ===> + // We find an approximation of y, called z. + // ===> + // exp(x) = exp([x])*(exp(z)*exp(epsilon)) + // with epsilon = y - z + // ===> + // We have exp([x]) and exp(z) pre-computed in tables, we "just" have to compute exp(epsilon). + // + // We use the same indexing (cast to int) to compute x integer part and the + // table index corresponding to z, to avoid two int casts. + // Also, to optimize index multiplication and division, we use powers of two, + // so that we can do it with bits shifts. + + if (value > EXP_OVERFLOW_LIMIT) { + return Double.POSITIVE_INFINITY; + } else if (!(value >= EXP_UNDERFLOW_LIMIT)) { + return (value != value) ? Double.NaN : 0.0; + } + + final int indexes = (int)(value*EXP_LO_INDEXING); + + final int valueInt; + if (indexes >= 0) { + valueInt = (indexes>>EXP_LO_INDEXING_DIV_SHIFT); + } else { + valueInt = -((-indexes)>>EXP_LO_INDEXING_DIV_SHIFT); + } + final double hiTerm = MyTExp.expHiTab[valueInt-(int)EXP_UNDERFLOW_LIMIT]; + + final int zIndex = indexes - (valueInt< 0.0) { + if (value == Double.POSITIVE_INFINITY) { + return Double.POSITIVE_INFINITY; + } + + // For normal values not close to 1.0, we use the following formula: + // log(value) + // = log(2^exponent*1.mantissa) + // = log(2^exponent) + log(1.mantissa) + // = exponent * log(2) + log(1.mantissa) + // = exponent * log(2) + log(1.mantissaApprox) + log(1.mantissa/1.mantissaApprox) + // = exponent * log(2) + log(1.mantissaApprox) + log(1+epsilon) + // = exponent * log(2) + log(1.mantissaApprox) + epsilon-epsilon^2/2+epsilon^3/3-epsilon^4/4+... + // with: + // 1.mantissaApprox <= 1.mantissa, + // log(1.mantissaApprox) in table, + // epsilon = (1.mantissa/1.mantissaApprox)-1 + // + // To avoid bad relative error for small results, + // values close to 1.0 are treated aside, with the formula: + // log(x) = z*(2+z^2*((2.0/3)+z^2*((2.0/5))+z^2*((2.0/7))+...))) + // with z=(x-1)/(x+1) + + double h; + if (value > 0.95) { + if (value < 1.14) { + double z = (value-1.0)/(value+1.0); + double z2 = z*z; + return z*(2+z2*((2.0/3)+z2*((2.0/5)+z2*((2.0/7)+z2*((2.0/9)+z2*((2.0/11))))))); + } + h = 0.0; + } else if (value < DOUBLE_MIN_NORMAL) { + // Ensuring value is normal. + value *= TWO_POW_52; + // log(x*2^52) + // = log(x)-ln(2^52) + // = log(x)-52*ln(2) + h = -52*LOG_2; + } else { + h = 0.0; + } + + int valueBitsHi = (int)(Double.doubleToRawLongBits(value)>>32); + int valueExp = (valueBitsHi>>20)-MAX_DOUBLE_EXPONENT; + // Getting the first LOG_BITS bits of the mantissa. + int xIndex = ((valueBitsHi<<12)>>>(32-LOG_BITS)); + + // 1.mantissa/1.mantissaApprox - 1 + double z = (value * twoPowNormalOrSubnormal(-valueExp)) * MyTLog.logXInvTab[xIndex] - 1; + + z *= (1-z*((1.0/2)-z*((1.0/3)))); + + return h + valueExp * LOG_2 + (MyTLog.logXLogTab[xIndex] + z); + + } else if (value == 0.0) { + return Double.NEGATIVE_INFINITY; + } else { // value < 0.0, or value is NaN + return Double.NaN; + } + } + + /** + * Quick log, with a max relative error of about 1.9e-3 + * for values in ]Double.MIN_NORMAL,+Infinity[, and + * worse accuracy outside this range. + * + * @param value A double value, in ]0,+Infinity[ (strictly positive and finite). + * @return Value logarithm (base e). + */ + public static double logQuick(double value) { + if (USE_JDK_MATH) { + return Math.log(value); + } + /* + * Inverse of Schraudolph's method for exp, is very inaccurate near 1, + * and not that fast (even using floats), especially with added if's + * to deal with values near 1, so we don't use it, and use a simplified + * version of our log's redefined algorithm. + */ + + // Simplified version of log's redefined algorithm: + // log(value) ~= exponent * log(2) + log(1.mantissaApprox) + + double h; + if (value > 0.87) { + if (value < 1.16) { + return 2.0 * (value-1.0)/(value+1.0); + } + h = 0.0; + } else if (value < DOUBLE_MIN_NORMAL) { + value *= TWO_POW_52; + h = -52*LOG_2; + } else { + h = 0.0; + } + + int valueBitsHi = (int)(Double.doubleToRawLongBits(value)>>32); + int valueExp = (valueBitsHi>>20)-MAX_DOUBLE_EXPONENT; + int xIndex = ((valueBitsHi<<12)>>>(32-LOG_BITS)); + + return h + valueExp * LOG_2 + MyTLog.logXLogTab[xIndex]; + } + + /** + * @param value A double value. + * @return Value logarithm (base 10). + */ + public static double log10(double value) { + if (USE_JDK_MATH || (!USE_REDEFINED_LOG)) { + return Math.log10(value); + } + // INV_LOG_10 is < 1, but there is no risk of log(double) + // overflow (positive or negative) while the end result shouldn't, + // since log(Double.MIN_VALUE) and log(Double.MAX_VALUE) have + // magnitudes of just a few hundreds. + return log(value) * INV_LOG_10; + } + + /** + * Much more accurate than log(1+value), + * for arguments (and results) close to zero. + * + * @param value A double value. + * @return Logarithm (base e) of (1+value). + */ + public static double log1p(double value) { + if (USE_JDK_MATH) { + return Math.log1p(value); + } + if (false) { + // This also works. Simpler but a bit slower. + if (value == Double.POSITIVE_INFINITY) { + return Double.POSITIVE_INFINITY; + } + double valuePlusOne = 1+value; + if (valuePlusOne == 1.0) { + return value; + } else { + return log(valuePlusOne)*(value/(valuePlusOne-1.0)); + } + } + if (value > -1.0) { + if (value == Double.POSITIVE_INFINITY) { + return Double.POSITIVE_INFINITY; + } + + // ln'(x) = 1/x + // so + // log(x+epsilon) ~= log(x) + epsilon/x + // + // Let u be 1+value rounded: + // 1+value = u+epsilon + // + // log(1+value) + // = log(u+epsilon) + // ~= log(u) + epsilon/value + // We compute log(u) as done in log(double), and then add the corrective term. + + double valuePlusOne = 1.0+value; + if (valuePlusOne == 1.0) { + return value; + } else if (Math.abs(value) < 0.15) { + double z = value/(value+2.0); + double z2 = z*z; + return z*(2+z2*((2.0/3)+z2*((2.0/5)+z2*((2.0/7)+z2*((2.0/9)+z2*((2.0/11))))))); + } + + int valuePlusOneBitsHi = (int)(Double.doubleToRawLongBits(valuePlusOne)>>32) & 0x7FFFFFFF; + int valuePlusOneExp = (valuePlusOneBitsHi>>20)-MAX_DOUBLE_EXPONENT; + // Getting the first LOG_BITS bits of the mantissa. + int xIndex = ((valuePlusOneBitsHi<<12)>>>(32-LOG_BITS)); + + // 1.mantissa/1.mantissaApprox - 1 + double z = (valuePlusOne * twoPowNormalOrSubnormal(-valuePlusOneExp)) * MyTLog.logXInvTab[xIndex] - 1; + + z *= (1-z*((1.0/2)-z*(1.0/3))); + + // Adding epsilon/valuePlusOne to z, + // with + // epsilon = value - (valuePlusOne-1) + // (valuePlusOne + epsilon ~= 1+value (not rounded)) + + return valuePlusOneExp * LOG_2 + MyTLog.logXLogTab[xIndex] + (z + (value - (valuePlusOne-1))/valuePlusOne); + } else if (value == -1.0) { + return Double.NEGATIVE_INFINITY; + } else { // value < -1.0, or value is NaN + return Double.NaN; + } + } + + /* + * powers + */ + + /** + * 1e-13ish accuracy or better on whole double range. + * + * @param value A double value. + * @param power A power. + * @return value^power. + */ + public static double pow(double value, double power) { + if (USE_JDK_MATH) { + return Math.pow(value,power); + } + if (power == 0.0) { + return 1.0; + } else if (power == 1.0) { + return value; + } + if (value <= 0.0) { + // powerInfo: 0 if not integer, 1 if even integer, -1 if odd integer + int powerInfo; + if (Math.abs(power) >= (TWO_POW_52*2)) { + // The binary digit just before comma is outside mantissa, + // thus it is always 0: power is an even integer. + powerInfo = 1; + } else { + // If power's magnitude permits, we cast into int instead of into long, + // as it is faster. + if (Math.abs(power) <= (double)Integer.MAX_VALUE) { + int powerAsInt = (int)power; + if (power == (double)powerAsInt) { + powerInfo = ((powerAsInt & 1) == 0) ? 1 : -1; + } else { // power is not an integer (and not NaN, due to test against Integer.MAX_VALUE) + powerInfo = 0; + } + } else { + long powerAsLong = (long)power; + if (power == (double)powerAsLong) { + powerInfo = ((powerAsLong & 1) == 0) ? 1 : -1; + } else { // power is not an integer, or is NaN + if (power != power) { + return Double.NaN; + } + powerInfo = 0; + } + } + } + + if (value == 0.0) { + if (power < 0.0) { + return (powerInfo < 0) ? 1/value : Double.POSITIVE_INFINITY; + } else { // power > 0.0 (0 and NaN cases already treated) + return (powerInfo < 0) ? value : 0.0; + } + } else { // value < 0.0 + if (value == Double.NEGATIVE_INFINITY) { + if (powerInfo < 0) { // power odd integer + return (power < 0.0) ? -0.0 : Double.NEGATIVE_INFINITY; + } else { // power even integer, or not an integer + return (power < 0.0) ? 0.0 : Double.POSITIVE_INFINITY; + } + } else { + return (powerInfo == 0) ? Double.NaN : powerInfo * exp(power*log(-value)); + } + } + } else { // value > 0.0, or value is NaN + return exp(power*log(value)); + } + } + + /** + * Quick pow, with a max relative error of about 1e-2 + * for value >= Double.MIN_NORMAL and 1e-10 < |value^power| < 1e10, + * of about 6e-2 for value >= Double.MIN_NORMAL and 1e-40 < |value^power| < 1e40, + * and worse accuracy otherwise. + * + * @param value A double value, in ]0,+Infinity[ (strictly positive and finite). + * @param power A double value. + * @return value^power. + */ + public static double powQuick(double value, double power) { + if (USE_JDK_MATH) { + return Math.pow(value,power); + } + return exp(power*logQuick(value)); + } + + /** + * 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. + */ + public static double powFast(double value, int power) { + if (USE_JDK_MATH) { + return Math.pow(value,power); + } + if (power < 3) { + if (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/(powFast(value,Integer.MAX_VALUE) * value); + } else { + return 1.0/powFast(value,-power); + } + } else { + // Here, power is in [0,2]. + if (power == 2) { // Most common case first. + return value * value; + } else if (power == 0) { + return 1.0; + } else { // power == 1 + return value; + } + } + } else { // power >= 4 + double oddRemains = 1.0; + // If power <= 5, faster to finish outside the loop. + while (power > 5) { + // Test if power is odd. + if ((power & 1) != 0) { + oddRemains *= value; + } + value *= value; + power >>= 1; // power = power / 2 + } + // Here, power is in [3,5]. + if (power == 3) { + return oddRemains * value * value * value; + } else { // power in [4,5]. + double v2 = value * value; + if (power == 4) { + return oddRemains * v2 * v2; + } else { // power == 5 + return oddRemains * v2 * v2 * value; + } + } + } + } + + /** + * @param value A float value. + * @return value*value. + */ + public static float pow2(float value) { + return value*value; + } + + /** + * @param value A double value. + * @return value*value. + */ + public static double pow2(double value) { + return value*value; + } + + /** + * @param value A float value. + * @return value*value*value. + */ + public static float pow3(float value) { + return value*value*value; + } + + /** + * @param value A double value. + * @return value*value*value. + */ + public static double pow3(double value) { + return value*value*value; + } + + /* + * roots + */ + + /** + * @param value A double value. + * @return Value square root. + */ + public static double sqrt(double value) { + if (USE_JDK_MATH || (!USE_REDEFINED_SQRT)) { + return Math.sqrt(value); + } + // See cbrt for comments, sqrt uses the same ideas. + + if (!(value > 0.0)) { // value <= 0.0, or value is NaN + if (ANTI_JIT_OPTIM_CRASH_ON_NAN) { + return (value < 0.0) ? Double.NaN : value; + } else { + return (value == 0.0) ? value : Double.NaN; + } + } else if (value == Double.POSITIVE_INFINITY) { + return Double.POSITIVE_INFINITY; + } + + double h; + if (value < DOUBLE_MIN_NORMAL) { + value *= TWO_POW_52; + h = 2*TWO_POW_N26; + } else { + h = 2.0; + } + + int valueBitsHi = (int)(Double.doubleToRawLongBits(value)>>32); + int valueExponentIndex = (valueBitsHi>>20)+(-MAX_DOUBLE_EXPONENT-MIN_DOUBLE_EXPONENT); + int xIndex = ((valueBitsHi<<12)>>>(32-SQRT_LO_BITS)); + + double result = MyTSqrt.sqrtXSqrtHiTab[valueExponentIndex] * MyTSqrt.sqrtXSqrtLoTab[xIndex]; + double slope = MyTSqrt.sqrtSlopeHiTab[valueExponentIndex] * MyTSqrt.sqrtSlopeLoTab[xIndex]; + value *= 0.25; + + result += (value - result * result) * slope; + result += (value - result * result) * slope; + return h*(result + (value - result * result) * slope); + } + + /** + * Quick sqrt, with with a max relative error of about 3.41e-2 + * for values in [Double.MIN_NORMAL,Double.MAX_VALUE], and worse + * accuracy outside this range. + * + * @param value A double value. + * @return Value square root. + */ + public static double sqrtQuick(double value) { + if (USE_JDK_MATH) { + return Math.sqrt(value); + } + final long bits = Double.doubleToRawLongBits(value); + /* + * Constant determined empirically, using a random-based metaheuristic. + * Should be possible to find a better one. + */ + return Double.longBitsToDouble((bits+4606859074900000000L)>>>1); + } + + /** + * Quick inverse of square root, with a max relative error of about 3.44e-2 + * for values in [Double.MIN_NORMAL,Double.MAX_VALUE], and worse accuracy + * outside this range. + * + * This implementation uses zero step of Newton's method. + * Here are the max relative errors on [Double.MIN_NORMAL,Double.MAX_VALUE] + * depending on number of steps, if you want to copy-paste this code + * and use your own number: + * n=0: about 3.44e-2 + * n=1: about 1.75e-3 + * n=2: about 4.6e-6 + * n=3: about 3.17e-11 + * n=4: about 3.92e-16 + * n=5: about 3.03e-16 + * + * @param value A double value. + * @return Inverse of value square root. + */ + public static double invSqrtQuick(double value) { + if (USE_JDK_MATH) { + return 1/Math.sqrt(value); + } + /* + * http://en.wikipedia.org/wiki/Fast_inverse_square_root + */ + if (false) { + // With one Newton step (much slower than + // 1/Math.sqrt(double) if not optimized). + final double halfInitial = value * 0.5; + long bits = Double.doubleToRawLongBits(value); + // If n=0, 6910474759270000000L might be better (3.38e-2 max relative error). + bits = 0x5FE6EB50C7B537A9L - (bits>>1); + value = Double.longBitsToDouble(bits); + value = value * (1.5 - halfInitial * value * value); // Newton step, can repeat. + return value; + } else { + return Double.longBitsToDouble(0x5FE6EB50C7B537A9L - (Double.doubleToRawLongBits(value)>>1)); + } + } + + /** + * @param value A double value. + * @return Value cubic root. + */ + public static double cbrt(double value) { + if (USE_JDK_MATH) { + return Math.cbrt(value); + } + double h; + if (value < 0.0) { + if (value == Double.NEGATIVE_INFINITY) { + return Double.NEGATIVE_INFINITY; + } + value = -value; + // Making sure value is normal. + if (value < DOUBLE_MIN_NORMAL) { + value *= (TWO_POW_52*TWO_POW_26); + // h = * / + h = -2*TWO_POW_N26; + } else { + h = -2.0; + } + } else { + if (!(value < Double.POSITIVE_INFINITY)) { // value is +Infinity, or value is NaN + return value; + } + // Making sure value is normal. + if (value < DOUBLE_MIN_NORMAL) { + if (value == 0.0) { + // cbrt(0.0) = 0.0, cbrt(-0.0) = -0.0 + return value; + } + value *= (TWO_POW_52*TWO_POW_26); + h = 2*TWO_POW_N26; + } else { + h = 2.0; + } + } + + // Normal value is (2^ * ). + // First member cubic root is computed, and multiplied with an approximation + // of the cubic root of the second member, to end up with a good guess of + // the result before using Newton's (or Archimedes's) method. + // To compute the cubic root approximation, we use the formula "cbrt(value) = cbrt(x) * cbrt(value/x)", + // choosing x as close to value as possible but inferior to it, so that cbrt(value/x) is close to 1 + // (we could iterate on this method, using value/x as new value for each iteration, + // but finishing with Newton's method is faster). + + // Shift and cast into an int, which overall is faster than working with a long. + int valueBitsHi = (int)(Double.doubleToRawLongBits(value)>>32); + int valueExponentIndex = (valueBitsHi>>20)+(-MAX_DOUBLE_EXPONENT-MIN_DOUBLE_EXPONENT); + // Getting the first CBRT_LO_BITS bits of the mantissa. + int xIndex = ((valueBitsHi<<12)>>>(32-CBRT_LO_BITS)); + double result = MyTCbrt.cbrtXCbrtHiTab[valueExponentIndex] * MyTCbrt.cbrtXCbrtLoTab[xIndex]; + double slope = MyTCbrt.cbrtSlopeHiTab[valueExponentIndex] * MyTCbrt.cbrtSlopeLoTab[xIndex]; + + // Lowering values to avoid overflows when using Newton's method + // (we will then just have to return twice the result). + // result^3 = value + // (result/2)^3 = value/8 + value *= 0.125; + // No need to divide result here, as division is factorized in result computation tables. + // result *= 0.5; + + // Newton's method, looking for y = x^(1/p): + // y(n) = y(n-1) + (x-y(n-1)^p) * slope(y(n-1)) + // y(n) = y(n-1) + (x-y(n-1)^p) * (1/p)*(x(n-1)^(1/p-1)) + // y(n) = y(n-1) + (x-y(n-1)^p) * (1/p)*(x(n-1)^((1-p)/p)) + // with x(n-1)=y(n-1)^p, i.e.: + // y(n) = y(n-1) + (x-y(n-1)^p) * (1/p)*(y(n-1)^(1-p)) + // + // For p=3: + // y(n) = y(n-1) + (x-y(n-1)^3) * (1/(3*y(n-1)^2)) + + // To save time, we don't recompute the slope between Newton's method steps, + // as initial slope is good enough for a few iterations. + // + // NB: slope = 1/(3*trueResult*trueResult) + // As we have result = trueResult/2 (to avoid overflows), we have: + // slope = 4/(3*result*result) + // = (4/3)*resultInv*resultInv + // with newResultInv = 1/newResult + // = 1/(oldResult+resultDelta) + // = (oldResultInv)*1/(1+resultDelta/oldResult) + // = (oldResultInv)*1/(1+resultDelta*oldResultInv) + // ~= (oldResultInv)*(1-resultDelta*oldResultInv) + // ===> Successive slopes could be computed without division, if needed, + // by computing resultInv (instead of slope right away) and retrieving + // slopes from it. + + result += (value - result * result * result) * slope; + result += (value - result * result * result) * slope; + return h*(result + (value - result * result * result) * slope); + } + + /** + * @return sqrt(x^2+y^2) without intermediate overflow or underflow. + */ + public static double hypot(double x, double y) { + if (USE_JDK_MATH) { + return Math.hypot(x,y); + } + x = Math.abs(x); + y = Math.abs(y); + // Ensuring x <= y. + if (y < x) { + double a = x; + x = y; + y = a; + } else if (!(y >= x)) { // Testing if we have some NaN. + return hypot_NaN(x, y); + } + + if (y-x == y) { + // x too small to subtract from y. + return y; + } else { + double factor; + if (y > HYPOT_MAX_MAG) { + // y is too large: scaling down. + x *= (1/HYPOT_FACTOR); + y *= (1/HYPOT_FACTOR); + factor = HYPOT_FACTOR; + } else if (x < (1/HYPOT_MAX_MAG)) { + // x is too small: scaling up. + x *= HYPOT_FACTOR; + y *= HYPOT_FACTOR; + factor = (1/HYPOT_FACTOR); + } else { + factor = 1.0; + } + return factor * sqrt(x*x+y*y); + } + } + + /** + * @return sqrt(x^2+y^2+z^2) without intermediate overflow or underflow. + */ + public static double hypot(double x, double y, double z) { + if (USE_JDK_MATH) { + // No simple JDK equivalent. + } + x = Math.abs(x); + y = Math.abs(y); + z = Math.abs(z); + /* + * Considering that z magnitude is the most likely to be the smaller, + * hence ensuring z <= y <= x, and not x <= y <= z, for less swaps. + */ + // Ensuring z <= y. + if (z > y) { + // y < z: swapping y and z + double a = z; + z = y; + y = a; + } else if (!(z <= y)) { // Testing if y or z is NaN. + return hypot_NaN(x, y, z); + } + // Ensuring y <= x. + if (z > x) { + // x < z <= y: moving x + double oldZ = z; + z = x; + double oldY = y; + y = oldZ; + x = oldY; + } else if (y > x) { + // z <= x < y: swapping x and y + double a = y; + y = x; + x = a; + } else if (x != x) { // Testing if x is NaN. + return hypot_NaN(x, y, z); + } + + if (x-y == x) { + // y, hence z, too small to subtract from x. + return x; + } else if (y-z == y) { + // z too small to subtract from y, hence x. + double factor; + if (x > HYPOT_MAX_MAG) { + // x is too large: scaling down. + x *= (1/HYPOT_FACTOR); + y *= (1/HYPOT_FACTOR); + factor = HYPOT_FACTOR; + } else if (y < (1/HYPOT_MAX_MAG)) { + // y is too small: scaling up. + x *= HYPOT_FACTOR; + y *= HYPOT_FACTOR; + factor = (1/HYPOT_FACTOR); + } else { + factor = 1.0; + } + return factor * sqrt(x*x+y*y); + } else { + double factor; + if (x > HYPOT_MAX_MAG) { + // x is too large: scaling down. + x *= (1/HYPOT_FACTOR); + y *= (1/HYPOT_FACTOR); + z *= (1/HYPOT_FACTOR); + factor = HYPOT_FACTOR; + } else if (z < (1/HYPOT_MAX_MAG)) { + // z is too small: scaling up. + x *= HYPOT_FACTOR; + y *= HYPOT_FACTOR; + z *= HYPOT_FACTOR; + factor = (1/HYPOT_FACTOR); + } else { + factor = 1.0; + } + // Adding smaller magnitudes together first. + return factor * sqrt(x*x+(y*y+z*z)); + } + } + + /* + * close values + */ + + /** + * @param value A float value. + * @return Floor of value. + */ + public static float floor(float value) { + final int exponent = getExponent(value); + if (exponent < 0) { + // abs(value) < 1. + if (value < 0.0f) { + return -1.0f; + } else { + // 0.0f, or -0.0f if value is -0.0f + return 0.0f * value; + } + } else if (exponent < 23) { + // A bit faster than using casts. + final int bits = Float.floatToRawIntBits(value); + final int anteCommaBits = bits & (0xFF800000>>exponent); + if ((value < 0.0f) && (anteCommaBits != bits)) { + return Float.intBitsToFloat(anteCommaBits) - 1.0f; + } else { + return Float.intBitsToFloat(anteCommaBits); + } + } else { + // +-Infinity, NaN, or a mathematical integer. + return value; + } + } + + /** + * @param value A double value. + * @return Floor of value. + */ + public static double floor(double value) { + if (USE_JDK_MATH) { + return Math.floor(value); + } + if (ANTI_SLOW_CASTS) { + double valueAbs = Math.abs(value); + if (valueAbs <= (double)Integer.MAX_VALUE) { + if (value > 0.0) { + return (double)(int)value; + } else if (value < 0.0) { + double anteCommaDigits = (double)(int)value; + if (value != anteCommaDigits) { + return anteCommaDigits - 1.0; + } else { + return anteCommaDigits; + } + } else { // value is +-0.0 (not NaN due to test against Integer.MAX_VALUE) + return value; + } + } else if (valueAbs < TWO_POW_52) { + // We split the value in two: + // high part, which is a mathematical integer, + // and the rest, for which we can get rid of the + // post comma digits by casting into an int. + double highPart = ((int)(value * TWO_POW_N26)) * TWO_POW_26; + if (value > 0.0) { + return highPart + (double)((int)(value - highPart)); + } else { + double anteCommaDigits = highPart + (double)((int)(value - highPart)); + if (value != anteCommaDigits) { + return anteCommaDigits - 1.0; + } else { + return anteCommaDigits; + } + } + } else { // abs(value) >= 2^52, or value is NaN + return value; + } + } else { + final int exponent = getExponent(value); + if (exponent < 0) { + // abs(value) < 1. + if (value < 0.0) { + return -1.0; + } else { + // 0.0, or -0.0 if value is -0.0 + return 0.0 * value; + } + } else if (exponent < 52) { + // A bit faster than working on bits. + final long matIntPart = (long)value; + final double matIntToValue = value-(double)matIntPart; + if (matIntToValue >= 0.0) { + return (double)matIntPart; + } else { + return (double)(matIntPart - 1); + } + } else { + // +-Infinity, NaN, or a mathematical integer. + return value; + } + } + } + + /** + * @param value A float value. + * @return Ceiling of value. + */ + public static float ceil(float value) { + return -floor(-value); + } + + /** + * @param value A double value. + * @return Ceiling of value. + */ + public static double ceil(double value) { + if (USE_JDK_MATH) { + return Math.ceil(value); + } + return -floor(-value); + } + + /** + * Might have different semantics than Math.round(float), + * see bugs 6430675 and 8010430. + * + * @param value A double value. + * @return Value rounded to nearest int, choosing superior int in case two + * are equally close (i.e. rounding-up). + */ + public static int round(float value) { + /* + * Not delegating to JDK, because we want delegation to provide + * at least as good results, and some supported JDK versions + * have bugged round() methods. + */ + // Algorithm by Dmitry Nadezhin (but replaced an if by a multiply) + // (http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-August/020247.html). + final int bits = Float.floatToRawIntBits(value); + final int biasedExp = ((bits>>23)&0xFF); + // Shift to get rid of bits past comma except first one: will need to + // 1-shift to the right to end up with correct magnitude. + final int shift = (23 - 1 + MAX_FLOAT_EXPONENT) - biasedExp; + if ((shift & -32) == 0) { + int bitsSignum = (((bits >> 31) << 1) + 1); + // shift in [0,31], so unbiased exp in [-9,22]. + int extendedMantissa = (0x00800000 | (bits & 0x007FFFFF)) * bitsSignum; + // If value is positive and first bit past comma is 0, rounding + // to lower integer, else to upper one, which is what "+1" and + // then ">>1" do. + return ((extendedMantissa >> shift) + 1) >> 1; + } else { + // +-Infinity, NaN, or a mathematical integer, or tiny. + if (false && ANTI_SLOW_CASTS) { // not worth it + if (Math.abs(value) >= -(float)Integer.MIN_VALUE) { + // +-Infinity or a mathematical integer (mostly) out of int range. + return (value < 0.0) ? Integer.MIN_VALUE : Integer.MAX_VALUE; + } + // NaN or a mathematical integer (mostly) in int range. + } + return (int)value; + } + } + + /** + * Might have different semantics than Math.round(double), + * see bugs 6430675 and 8010430. + * + * @param value A double value. + * @return Value rounded to nearest long, choosing superior long in case two + * are equally close (i.e. rounding-up). + */ + public static long round(double value) { + /* + * Not delegating to JDK, because we want delegation to provide + * at least as good results, and some supported JDK versions + * have bugged round() methods. + */ + final long bits = Double.doubleToRawLongBits(value); + final int biasedExp = (((int)(bits>>52))&0x7FF); + // Shift to get rid of bits past comma except first one: will need to + // 1-shift to the right to end up with correct magnitude. + final int shift = (52 - 1 + MAX_DOUBLE_EXPONENT) - biasedExp; + if ((shift & -64) == 0) { + long bitsSignum = (((bits >> 63) << 1) + 1); + // shift in [0,63], so unbiased exp in [-12,51]. + long extendedMantissa = (0x0010000000000000L | (bits & 0x000FFFFFFFFFFFFFL)) * bitsSignum; + // If value is positive and first bit past comma is 0, rounding + // to lower integer, else to upper one, which is what "+1" and + // then ">>1" do. + return ((extendedMantissa >> shift) + 1L) >> 1; + } else { + // +-Infinity, NaN, or a mathematical integer, or tiny. + if (ANTI_SLOW_CASTS) { + if (Math.abs(value) >= -(double)Long.MIN_VALUE) { + // +-Infinity or a mathematical integer (mostly) out of long range. + return (value < 0.0) ? Long.MIN_VALUE : Long.MAX_VALUE; + } + // NaN or a mathematical integer (mostly) in long range. + } + return (long)value; + } + } + + /** + * @param value A float value. + * @return Value rounded to nearest int, choosing even int in case two + * are equally close. + */ + public static int roundEven(float value) { + final int sign = signFromBit(value); + value = Math.abs(value); + if (ANTI_SLOW_CASTS) { + if (value < TWO_POW_23_F) { + // Getting rid of post-comma bits. + value = ((value + TWO_POW_23_F) - TWO_POW_23_F); + return sign * (int)value; + } else if (value < (float)Integer.MAX_VALUE) { // "<=" doesn't work, because of float precision + // value is in [-Integer.MAX_VALUE,Integer.MAX_VALUE] + return sign * (int)value; + } + } else { + if (value < TWO_POW_23_F) { + // Getting rid of post-comma bits. + value = ((value + TWO_POW_23_F) - TWO_POW_23_F); + } + } + return (int)(sign * value); + } + + /** + * @param value A double value. + * @return Value rounded to nearest long, choosing even long in case two + * are equally close. + */ + public static long roundEven(double value) { + final int sign = (int)signFromBit(value); + value = Math.abs(value); + if (value < TWO_POW_52) { + // Getting rid of post-comma bits. + value = ((value + TWO_POW_52) - TWO_POW_52); + } + if (ANTI_SLOW_CASTS) { + if (value <= (double)Integer.MAX_VALUE) { + // value is in [-Integer.MAX_VALUE,Integer.MAX_VALUE] + return sign * (int)value; + } + } + return (long)(sign * value); + } + + /** + * @param value A float value. + * @return The float mathematical integer closest to the specified value, + * choosing even one if two are equally close, or respectively + * NaN, +-Infinity or +-0.0f if the value is any of these. + */ + public static float rint(float value) { + final int sign = signFromBit(value); + value = Math.abs(value); + if (value < TWO_POW_23_F) { + // Getting rid of post-comma bits. + value = ((TWO_POW_23_F + value ) - TWO_POW_23_F); + } + // Restoring original sign. + return sign * value; + } + + /** + * @param value A double value. + * @return The double mathematical integer closest to the specified value, + * choosing even one if two are equally close, or respectively + * NaN, +-Infinity or +-0.0 if the value is any of these. + */ + public static double rint(double value) { + if (USE_JDK_MATH) { + return Math.rint(value); + } + final int sign = (int)signFromBit(value); + value = Math.abs(value); + if (value < TWO_POW_52) { + // Getting rid of post-comma bits. + value = ((TWO_POW_52 + value ) - TWO_POW_52); + } + // Restoring original sign. + return sign * value; + } + + /* + * close int values + * + * Never delegating to JDK for these methods, for we should always + * be faster and exact, and JDK doesn't exactly have such methods. + */ + + /** + * @param value A double value. + * @return Floor of value as int, or closest int if floor is out + * of int range, or 0 if value is NaN. + */ + public static int floorToInt(double value) { + int valueInt = (int) value; + if (value < 0.0) { + if (value == (double) valueInt) { + return valueInt; + } else { + if (valueInt == Integer.MIN_VALUE) { + return valueInt; + } else { + return valueInt - 1; + } + } + } else { // >= 0 or NaN. + return valueInt; + } + } + + /** + * @param value A double value. + * @return Ceiling of value as int, or closest int if ceiling is out + * of int range, or 0 if value is NaN. + */ + public static int ceilToInt(double value) { + int valueInt = (int) value; + if (value > 0.0) { + if (value == (double) valueInt) { + return valueInt; + } else { + if (valueInt == Integer.MAX_VALUE) { + return valueInt; + } else { + return valueInt + 1; + } + } + } else { // <= 0 or NaN. + return valueInt; + } + } + + /** + * @param value A double value. + * @return Value rounded to nearest int, choosing superior int in case two + * are equally close (i.e. rounding-up). + */ + public static int roundToInt(double value) { + /* + * We don't gain much by reimplementing rounding, except for + * pathologically large values, which should not be a common case + * when dealing with ints, so we just use round(double). + */ + return NumbersUtils.toInt(round(value)); + } + + /** + * @param value A double value. + * @return Value rounded to nearest int, choosing even int in case two + * are equally close. + */ + public static int roundEvenToInt(double value) { + final int sign = (int)signFromBit(value); + value = Math.abs(value); + /* + * Applying the post-comma bits removal logic even if value is out + * of int range, to avoid a test, for it doesn't mess up the result, + * and we want to optimize for the case of values in int range. + */ + value = ((value + TWO_POW_52) - TWO_POW_52); + return (int)(sign * value); + } + + /* + * ranges + */ + + /** + * @param min A float value. + * @param max A float value. + * @param value A float value. + * @return min if value < min, max if value > max, value otherwise. + */ + public static float toRange(float min, float max, float value) { + return NumbersUtils.toRange(min, max, value); + } + + /** + * @param min A double value. + * @param max A double value. + * @param value A double value. + * @return min if value < min, max if value > max, value otherwise. + */ + public static double toRange(double min, double max, double value) { + return NumbersUtils.toRange(min, max, value); + } + + /* + * binary operators (/,%) + */ + + /** + * Returns dividend - divisor * n, where n is the mathematical integer + * closest to dividend/divisor. + * If dividend/divisor is equally close to surrounding integers, + * we choose n to be the integer of smallest magnitude, which makes + * this treatment differ from Math.IEEEremainder(double,double), + * where n is chosen to be the even integer. + * Note that the choice of n is not done considering the double + * approximation of dividend/divisor, because it could cause + * result to be outside [-|divisor|/2,|divisor|/2] range. + * The practical effect is that if multiple results would be possible, + * we always choose the result that is the closest to (and has the same + * sign as) the dividend. + * Ex. : + * - for (-3.0,2.0), this method returns -1.0, + * whereas Math.IEEEremainder returns 1.0. + * - for (-5.0,2.0), both this method and Math.IEEEremainder return -1.0. + * + * If the remainder is zero, its sign is the same as the sign of the first argument. + * If either argument is NaN, or the first argument is infinite, + * or the second argument is positive zero or negative zero, + * then the result is NaN. + * If the first argument is finite and the second argument is + * infinite, then the result is the same as the first argument. + * + * NB: + * - Modulo operator (%) returns a value in ]-|divisor|,|divisor|[, + * which sign is the same as dividend. + * - As for modulo operator, the sign of the divisor has no effect on the result. + * - On some architecture, % operator has been observed to return NaN + * for some subnormal values of divisor, when dividend exponent is 1023, + * which impacts the correctness of this method. + * + * @param dividend Dividend. + * @param divisor Divisor. + * @return Remainder of dividend/divisor, i.e. a value in [-|divisor|/2,|divisor|/2]. + */ + public static double remainder(double dividend, double divisor) { + if (Double.isInfinite(divisor)) { + if (Double.isInfinite(dividend)) { + return Double.NaN; + } else { + return dividend; + } + } + double value = dividend % divisor; + if (Math.abs(value+value) > Math.abs(divisor)) { + return value + ((value > 0.0) ? -Math.abs(divisor) : Math.abs(divisor)); + } else { + return value; + } + } + + /** + * @param angle Angle in radians. + * @return The same angle, in radians, but in [-PI,PI]. + */ + public static double normalizeMinusPiPi(double angle) { + // Not modifying values in output range. + if ((angle >= -Math.PI) && (angle <= Math.PI)) { + return angle; + } + return remainderTwoPi(angle); + } + + /** + * Not accurate for large values. + * + * @param angle Angle in radians. + * @return The same angle, in radians, but in [-PI,PI]. + */ + public static double normalizeMinusPiPiFast(double angle) { + // Not modifying values in output range. + if ((angle >= -Math.PI) && (angle <= Math.PI)) { + return angle; + } + return remainderTwoPiFast(angle); + } + + /** + * @param angle Angle in radians. + * @return The same angle, in radians, but in [0,2*PI]. + */ + public static double normalizeZeroTwoPi(double angle) { + // Not modifying values in output range. + if ((angle >= 0.0) && (angle <= 2*Math.PI)) { + return angle; + } + angle = remainderTwoPi(angle); + if (angle < 0.0) { + // LO then HI is theoretically better (when starting near 0). + return (angle + TWOPI_LO) + TWOPI_HI; + } else { + return angle; + } + } + + /** + * Not accurate for large values. + * + * @param angle Angle in radians. + * @return The same angle, in radians, but in [0,2*PI]. + */ + public static double normalizeZeroTwoPiFast(double angle) { + // Not modifying values in output range. + if ((angle >= 0.0) && (angle <= 2*Math.PI)) { + return angle; + } + angle = remainderTwoPiFast(angle); + if (angle < 0.0) { + // LO then HI is theoretically better (when starting near 0). + return (angle + TWOPI_LO) + TWOPI_HI; + } else { + return angle; + } + } + + /** + * @param angle Angle in radians. + * @return Angle value modulo PI, in radians, in [-PI/2,PI/2]. + */ + public static double normalizeMinusHalfPiHalfPi(double angle) { + // Not modifying values in output range. + if ((angle >= -Math.PI/2) && (angle <= Math.PI/2)) { + return angle; + } + return remainderPi(angle); + } + + /** + * Not accurate for large values. + * + * @param angle Angle in radians. + * @return Angle value modulo PI, in radians, in [-PI/2,PI/2]. + */ + public static double normalizeMinusHalfPiHalfPiFast(double angle) { + // Not modifying values in output range. + if ((angle >= -Math.PI/2) && (angle <= Math.PI/2)) { + return angle; + } + return remainderPiFast(angle); + } + + /* + * floating points utils + */ + + /** + * @param value A float value. + * @return true if the specified value is NaN or +-Infinity, false otherwise. + */ + public static boolean isNaNOrInfinite(float value) { + return NumbersUtils.isNaNOrInfinite(value); + } + + /** + * @param value A double value. + * @return true if the specified value is NaN or +-Infinity, false otherwise. + */ + public static boolean isNaNOrInfinite(double value) { + return NumbersUtils.isNaNOrInfinite(value); + } + + /** + * @param value A float value. + * @return Value unbiased exponent. + */ + public static int getExponent(float value) { + return ((Float.floatToRawIntBits(value)>>23)&0xFF)-MAX_FLOAT_EXPONENT; + } + + /** + * @param value A double value. + * @return Value unbiased exponent. + */ + public static int getExponent(double value) { + return (((int)(Double.doubleToRawLongBits(value)>>52))&0x7FF)-MAX_DOUBLE_EXPONENT; + } + + /** + * @param value A float value. + * @return -1.0f if the specified value is < 0, 1.0f if it is > 0, + * and the value itself if it is NaN or +-0.0f. + */ + public static float signum(float value) { + if (USE_JDK_MATH) { + return Math.signum(value); + } + if ((value == 0.0f) || (value != value)) { + return value; + } + return (float)signFromBit(value); + } + + /** + * @param value A double value. + * @return -1.0 if the specified value is < 0, 1.0 if it is > 0, + * and the value itself if it is NaN or +-0.0. + */ + public static double signum(double value) { + if (USE_JDK_MATH) { + return Math.signum(value); + } + if ((value == 0.0) || (value != value)) { + return value; + } + if (ANTI_SLOW_CASTS) { + return (double)(int)signFromBit(value); + } else { + return (double)signFromBit(value); + } + } + + /** + * @param value A float value. + * @return -1 if sign bit is 1, 1 if sign bit is 0. + */ + public static int signFromBit(float value) { + return ((Float.floatToRawIntBits(value)>>30)|1); + } + + /** + * @param value A double value. + * @return -1 if sign bit is 1, 1 if sign bit is 0. + */ + public static long signFromBit(double value) { + // Returning a long, to avoid useless cast into int. + return ((Double.doubleToRawLongBits(value)>>62)|1); + } + + /** + * A sign of NaN can be interpreted as positive or negative. + * + * @param magnitude A float value. + * @param sign A float value. + * @return A value with the magnitude of the first argument, and the sign + * of the second argument. + */ + public static float copySign(float magnitude, float sign) { + return Float.intBitsToFloat( + (Float.floatToRawIntBits(sign) & Integer.MIN_VALUE) + | (Float.floatToRawIntBits(magnitude) & Integer.MAX_VALUE)); + } + + /** + * A sign of NaN can be interpreted as positive or negative. + * + * @param magnitude A double value. + * @param sign A double value. + * @return A value with the magnitude of the first argument, and the sign + * of the second argument. + */ + public static double copySign(double magnitude, double sign) { + return Double.longBitsToDouble( + (Double.doubleToRawLongBits(sign) & Long.MIN_VALUE) + | (Double.doubleToRawLongBits(magnitude) & Long.MAX_VALUE)); + } + + /** + * The ULP (Unit in the Last Place) is the distance to the next value larger + * in magnitude. + * + * @param value A float value. + * @return The size of an ulp of the specified value, or Float.MIN_VALUE + * if it is +-0.0f, or +Infinity if it is +-Infinity, or NaN + * if it is NaN. + */ + public static float ulp(float value) { + if (USE_JDK_MATH) { + return Math.ulp(value); + } + /* + * Look-up table not really worth it in micro-benchmark, + * so should be worse with cache-misses. + */ + final int exponent = getExponent(value); + if (exponent >= (MIN_FLOAT_NORMAL_EXPONENT+23)) { + if (exponent == MAX_FLOAT_EXPONENT+1) { + // NaN or +-Infinity + return Math.abs(value); + } + // normal: returning 2^(exponent-23) + return Float.intBitsToFloat((exponent+(MAX_FLOAT_EXPONENT-23))<<23); + } else { + if (exponent == MIN_FLOAT_NORMAL_EXPONENT-1) { + // +-0.0f or subnormal + return Float.MIN_VALUE; + } + // subnormal result + return Float.intBitsToFloat(1<<(exponent-MIN_FLOAT_NORMAL_EXPONENT)); + } + } + + /** + * The ULP (Unit in the Last Place) is the distance to the next value larger + * in magnitude. + * + * @param value A double value. + * @return The size of an ulp of the specified value, or Double.MIN_VALUE + * if it is +-0.0, or +Infinity if it is +-Infinity, or NaN + * if it is NaN. + */ + public static double ulp(double value) { + if (USE_JDK_MATH) { + return Math.ulp(value); + } + /* + * Look-up table not really worth it in micro-benchmark, + * so should be worse with cache-misses. + */ + final int exponent = getExponent(value); + if (exponent >= (MIN_DOUBLE_NORMAL_EXPONENT+52)) { + if (exponent == MAX_DOUBLE_EXPONENT+1) { + // NaN or +-Infinity + return Math.abs(value); + } + // normal: returning 2^(exponent-52) + return Double.longBitsToDouble((exponent+(MAX_DOUBLE_EXPONENT-52L))<<52); + } else { + if (exponent == MIN_DOUBLE_NORMAL_EXPONENT-1) { + // +-0.0f or subnormal + return Double.MIN_VALUE; + } + // subnormal result + return Double.longBitsToDouble(1L<<(exponent-MIN_DOUBLE_NORMAL_EXPONENT)); + } + } + + /** + * If both arguments are +-0.0(f), (float)direction is returned. + * + * If both arguments are +Infinity or -Infinity, + * respectively +Infinity or -Infinity is returned. + * + * @param start A float value. + * @param direction A double value. + * @return The float adjacent to start towards direction, considering that + * +(-)Float.MIN_VALUE is adjacent to +(-)0.0f, and that + * +(-)Float.MAX_VALUE is adjacent to +(-)Infinity, + * or NaN if any argument is NaN. + */ + public static float nextAfter(float start, double direction) { + if (direction < start) { + // Going towards -Infinity. + if (start == 0.0f) { + // +-0.0f + return -Float.MIN_VALUE; + } + final int bits = Float.floatToRawIntBits(start); + return Float.intBitsToFloat(bits + ((bits > 0) ? -1 : 1)); + } else if (direction > start) { + // Going towards +Infinity. + // +0.0f to get rid of eventual -0.0f + final int bits = Float.floatToRawIntBits(start + 0.0f); + return Float.intBitsToFloat(bits + (bits >= 0 ? 1 : -1)); + } else if (start == direction) { + return (float)direction; + } else { + // Returning a NaN derived from the input NaN(s). + return start + (float)direction; + } + } + + /** + * If both arguments are +-0.0, direction is returned. + * + * If both arguments are +Infinity or -Infinity, + * respectively +Infinity or -Infinity is returned. + * + * @param start A double value. + * @param direction A double value. + * @return The double adjacent to start towards direction, considering that + * +(-)Double.MIN_VALUE is adjacent to +(-)0.0, and that + * +(-)Double.MAX_VALUE is adjacent to +(-)Infinity, + * or NaN if any argument is NaN. + */ + public static double nextAfter(double start, double direction) { + if (direction < start) { + // Going towards -Infinity. + if (start == 0.0) { + // +-0.0 + return -Double.MIN_VALUE; + } + final long bits = Double.doubleToRawLongBits(start); + return Double.longBitsToDouble(bits + ((bits > 0) ? -1 : 1)); + } else if (direction > start) { + // Going towards +Infinity. + // +0.0 to get rid of eventual -0.0 + final long bits = Double.doubleToRawLongBits(start + 0.0f); + return Double.longBitsToDouble(bits + (bits >= 0 ? 1 : -1)); + } else if (start == direction) { + return direction; + } else { + // Returning a NaN derived from the input NaN(s). + return start + direction; + } + } + + /** + * Semantically equivalent to nextAfter(start,Double.NEGATIVE_INFINITY). + */ + public static float nextDown(float start) { + if (start > Float.NEGATIVE_INFINITY) { + if (start == 0.0f) { + // +-0.0f + return -Float.MIN_VALUE; + } + final int bits = Float.floatToRawIntBits(start); + return Float.intBitsToFloat(bits + ((bits > 0) ? -1 : 1)); + } else if (start == Float.NEGATIVE_INFINITY) { + return Float.NEGATIVE_INFINITY; + } else { + // NaN + return start; + } + } + + /** + * Semantically equivalent to nextAfter(start,Double.NEGATIVE_INFINITY). + */ + public static double nextDown(double start) { + if (start > Double.NEGATIVE_INFINITY) { + if (start == 0.0) { + // +-0.0 + return -Double.MIN_VALUE; + } + final long bits = Double.doubleToRawLongBits(start); + return Double.longBitsToDouble(bits + ((bits > 0) ? -1 : 1)); + } else if (start == Double.NEGATIVE_INFINITY) { + return Double.NEGATIVE_INFINITY; + } else { + // NaN + return start; + } + } + + /** + * Semantically equivalent to nextAfter(start,Double.POSITIVE_INFINITY). + */ + public static float nextUp(float start) { + if (start < Float.POSITIVE_INFINITY) { + // +0.0f to get rid of eventual -0.0f + final int bits = Float.floatToRawIntBits(start + 0.0f); + return Float.intBitsToFloat(bits + (bits >= 0 ? 1 : -1)); + } else if (start == Float.POSITIVE_INFINITY) { + return Float.POSITIVE_INFINITY; + } else { + // NaN + return start; + } + } + + /** + * Semantically equivalent to nextAfter(start,Double.POSITIVE_INFINITY). + */ + public static double nextUp(double start) { + if (start < Double.POSITIVE_INFINITY) { + // +0.0 to get rid of eventual -0.0 + final long bits = Double.doubleToRawLongBits(start + 0.0); + return Double.longBitsToDouble(bits + (bits >= 0 ? 1 : -1)); + } else if (start == Double.POSITIVE_INFINITY) { + return Double.POSITIVE_INFINITY; + } else { + // NaN + return start; + } + } + + /** + * Precision may be lost if the result is subnormal. + * + * @param value A float value. + * @param scaleFactor An int value. + * @return value * 2^scaleFactor, or a value equivalent to the specified + * one if it is NaN, +-Infinity or +-0.0f. + */ + public static float scalb(float value, int scaleFactor) { + // Large enough to imply overflow or underflow for + // a finite non-zero value. + final int MAX_SCALE = 2*MAX_FLOAT_EXPONENT+23+1; + + // Making sure scaling factor is in a reasonable range. + scaleFactor = Math.max(Math.min(scaleFactor, MAX_SCALE), -MAX_SCALE); + + return (float)(((double)value) * twoPowNormal(scaleFactor)); + } + + /** + * Precision may be lost if the result is subnormal. + * + * @param value A double value. + * @param scaleFactor An int value. + * @return value * 2^scaleFactor, or a value equivalent to the specified + * one if it is NaN, +-Infinity or +-0.0. + */ + public static double scalb(double value, int scaleFactor) { + if ((scaleFactor > -MAX_DOUBLE_EXPONENT) && (scaleFactor <= MAX_DOUBLE_EXPONENT)) { + // Quick case (as done in apache FastMath). + return value * twoPowNormal(scaleFactor); + } + + // Large enough to imply overflow or underflow for + // a finite non-zero value. + final int MAX_SCALE = 2*MAX_DOUBLE_EXPONENT+52+1; + + // Making sure scaling factor is in a reasonable range. + final int exponentAdjust; + final int scaleIncrement; + final double exponentDelta; + if (scaleFactor < 0) { + scaleFactor = Math.max(scaleFactor, -MAX_SCALE); + scaleIncrement = -512; + exponentDelta = TWO_POW_N512; + } else { + scaleFactor = Math.min(scaleFactor, MAX_SCALE); + scaleIncrement = 512; + exponentDelta = TWO_POW_512; + } + + // Calculating (scaleFactor % +-512), 512 = 2^9, using + // technique from "Hacker's Delight" section 10-2. + final int t = ((scaleFactor >> (9-1)) >>> (32-9)); + exponentAdjust = ((scaleFactor + t) & (512-1)) - t; + + value *= twoPowNormal(exponentAdjust); + scaleFactor -= exponentAdjust; + + while (scaleFactor != 0) { + value *= exponentDelta; + scaleFactor -= scaleIncrement; + } + + return value; + } + + /* + * Non-redefined Math public values and treatments. + */ + + public static float abs(float a) { + return Math.abs(a); + } + + public static double abs(double a) { + return Math.abs(a); + } + + public static float min(float a, float b) { + return Math.min(a,b); + } + + public static double min(double a, double b) { + return Math.min(a,b); + } + + public static float max(float a, float b) { + return Math.max(a,b); + } + + public static double max(double a, double b) { + return Math.max(a,b); + } + + public static double IEEEremainder(double f1, double f2) { + return Math.IEEEremainder(f1,f2); + } + + public static double random() { + return Math.random(); + } + + //-------------------------------------------------------------------------- + // PRIVATE METHODS + //-------------------------------------------------------------------------- + + /** + * Non-instantiable. + */ + private FastMath() { + } + + /* + * Remainders (accurate). + */ + + /** + * @param angle Angle in radians. + * @return Remainder of (angle % (2*PI)), in [-PI,PI]. + */ + private static double remainderTwoPi(double angle) { + if (USE_JDK_MATH) { + return jdkRemainderTwoPi(angle); + } + boolean negateResult = false; + if (angle < 0.0) { + angle = -angle; + negateResult = true; + } + if (angle <= (4*NORMALIZE_ANGLE_MAX_MEDIUM_DOUBLE_PIO2)) { + double fn = (double)(int)(angle*TWOPI_INV+0.5); + angle = (angle - fn*TWOPI_HI) - fn*TWOPI_LO; + // Ensuring range. + // HI/LO can help a bit, even though we are always far from 0. + if (angle < -Math.PI) { + angle = (angle + TWOPI_HI) + TWOPI_LO; + } else if (angle > Math.PI) { + angle = (angle - TWOPI_HI) - TWOPI_LO; + } + return negateResult ? -angle : angle; + } else if (angle < Double.POSITIVE_INFINITY) { + angle = heavyRemainderTwoPi(angle); + return negateResult ? -angle : angle; + } else { // angle is +Infinity or NaN + return Double.NaN; + } + } + + /** + * @param angle Angle in radians. + * @return Remainder of (angle % PI), in [-PI/2,PI/2]. + */ + private static double remainderPi(double angle) { + if (USE_JDK_MATH) { + return jdkRemainderPi(angle); + } + boolean negateResult = false; + if (angle < 0.0) { + angle = -angle; + negateResult = true; + } + if (angle <= (2*NORMALIZE_ANGLE_MAX_MEDIUM_DOUBLE_PIO2)) { + double fn = (double)(int)(angle*PI_INV+0.5); + angle = (angle - fn*PI_HI) - fn*PI_LO; + // Ensuring range. + // HI/LO can help a bit, even though we are always far from 0. + if (angle < -Math.PI/2) { + angle = (angle + PI_HI) + PI_LO; + } else if (angle > Math.PI/2) { + angle = (angle - PI_HI) - PI_LO; + } + return negateResult ? -angle : angle; + } else if (angle < Double.POSITIVE_INFINITY) { + angle = heavyRemainderPi(angle); + return negateResult ? -angle : angle; + } else { // angle is +Infinity or NaN + return Double.NaN; + } + } + + /** + * @param angle Angle in radians. + * @return Bits of double corresponding to remainder of (angle % (PI/2)), + * in [-PI/4,PI/4], with quadrant encoded in exponent bits. + */ + private static long remainderPiO2(double angle) { + if (USE_JDK_MATH) { + return jdkRemainderPiO2(angle, false); + } + boolean negateResult = false; + if (angle < 0.0) { + angle = -angle; + negateResult = true; + } + if (angle <= NORMALIZE_ANGLE_MAX_MEDIUM_DOUBLE_PIO2) { + int n = (int)(angle*PIO2_INV+0.5); + double fn = (double)n; + angle = (angle - fn*PIO2_HI) - fn*PIO2_LO; + // Ensuring range. + // HI/LO can help a bit, even though we are always far from 0. + if (angle < -Math.PI/4) { + angle = (angle + PIO2_HI) + PIO2_LO; + n--; + } else if (angle > Math.PI/4) { + angle = (angle - PIO2_HI) - PIO2_LO; + n++; + } + if (negateResult) { + angle = -angle; + } + return encodeRemainderAndQuadrant(angle, n&3); + } else if (angle < Double.POSITIVE_INFINITY) { + return heavyRemainderPiO2(angle, negateResult); + } else { // angle is +Infinity or NaN + return encodeRemainderAndQuadrant(Double.NaN, 0); + } + } + + /* + * Remainders (fast). + */ + + /** + * Not accurate for large values. + * + * @param angle Angle in radians. + * @return Remainder of (angle % (2*PI)), in [-PI,PI]. + */ + private static double remainderTwoPiFast(double angle) { + if (USE_JDK_MATH) { + return jdkRemainderTwoPi(angle); + } + boolean negateResult = false; + if (angle < 0.0) { + angle = -angle; + negateResult = true; + } + // - We don't bother with values higher than (2*PI*(2^52)), + // since they are spaced by 2*PI or more from each other. + // - For large values, we don't use % because it might be very slow, + // and we split computation in two, because cast from double to int + // with large numbers might be very slow also. + if (angle <= TWO_POW_26*(2*Math.PI)) { + // ok + } else if (angle <= TWO_POW_52*(2*Math.PI)) { + // Computing remainder of angle modulo TWO_POW_26*(2*PI). + double fn = (double)(int)(angle*(TWOPI_INV/TWO_POW_26)+0.5); + angle = (angle - fn*(TWOPI_HI*TWO_POW_26)) - fn*(TWOPI_LO*TWO_POW_26); + // Here, angle is in [-TWO_POW_26*PI,TWO_POW_26*PI], or so. + if (angle < 0.0) { + angle = -angle; + negateResult = !negateResult; + } + } else if (angle < Double.POSITIVE_INFINITY) { + return 0.0; + } else { // angle is +Infinity or NaN + return Double.NaN; + } + + // Computing remainder of angle modulo 2*PI. + double fn = (double)(int)(angle*TWOPI_INV+0.5); + angle = (angle - fn*TWOPI_HI) - fn*TWOPI_LO; + + // Ensuring range. + // HI/LO can help a bit, even though we are always far from 0. + if (angle < -Math.PI) { + angle = (angle + TWOPI_HI) + TWOPI_LO; + } else if (angle > Math.PI) { + angle = (angle - TWOPI_HI) - TWOPI_LO; + } + return negateResult ? -angle : angle; + } + + /** + * Not accurate for large values. + * + * @param angle Angle in radians. + * @return Remainder of (angle % PI), in [-PI/2,PI/2]. + */ + private static double remainderPiFast(double angle) { + if (USE_JDK_MATH) { + return jdkRemainderPi(angle); + } + boolean negateResult = false; + if (angle < 0.0) { + angle = -angle; + negateResult = true; + } + // - We don't bother with values higher than (PI*(2^52)), + // since they are spaced by PI or more from each other. + // - For large values, we don't use % because it might be very slow, + // and we split computation in two, because cast from double to int + // with large numbers might be very slow also. + if (angle <= TWO_POW_26*Math.PI) { + // ok + } else if (angle <= TWO_POW_52*Math.PI) { + // Computing remainder of angle modulo TWO_POW_26*PI. + double fn = (double)(int)(angle*(PI_INV/TWO_POW_26)+0.5); + angle = (angle - fn*(PI_HI*TWO_POW_26)) - fn*(PI_LO*TWO_POW_26); + // Here, angle is in [-TWO_POW_26*PI/2,TWO_POW_26*PI/2], or so. + if (angle < 0.0) { + angle = -angle; + negateResult = !negateResult; + } + } else if (angle < Double.POSITIVE_INFINITY) { + return 0.0; + } else { // angle is +Infinity or NaN + return Double.NaN; + } + + // Computing remainder of angle modulo PI. + double fn = (double)(int)(angle*PI_INV+0.5); + angle = (angle - fn*PI_HI) - fn*PI_LO; + + // Ensuring range. + // HI/LO can help a bit, even though we are always far from 0. + if (angle < -Math.PI/2) { + angle = (angle + PI_HI) + PI_LO; + } else if (angle > Math.PI/2) { + angle = (angle - PI_HI) - PI_LO; + } + return negateResult ? -angle : angle; + } +} diff --git a/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/IntWrapper.java b/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/IntWrapper.java new file mode 100644 index 000000000..812e5a22d --- /dev/null +++ b/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/IntWrapper.java @@ -0,0 +1,13 @@ +/* + * Copyright 2018-2021 KMath contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ +package space.kscience.kmath.jafama; + +public class IntWrapper { + public int value; + @Override + public String toString() { + return Integer.toString(this.value); + } +} diff --git a/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/KMathJafama.kt b/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/KMathJafama.kt new file mode 100644 index 000000000..2b6cc3a5a --- /dev/null +++ b/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/KMathJafama.kt @@ -0,0 +1,102 @@ +package space.kscience.kmath.jafama + +import space.kscience.kmath.operations.* + +/** + * Advanced Number-like semifield that implements basic operations. + */ +public interface ExtendedFieldOperations : + FieldOperations, + TrigonometricOperations, + PowerOperations, + ExponentialOperations { + public override fun tan(arg: T): T = sin(arg) / cos(arg) + public override fun tanh(arg: T): T = sinh(arg) / cosh(arg) + + public override fun unaryOperationFunction(operation: String): (arg: T) -> T = when (operation) { + TrigonometricOperations.COS_OPERATION -> ::cos + TrigonometricOperations.SIN_OPERATION -> ::sin + TrigonometricOperations.TAN_OPERATION -> ::tan + TrigonometricOperations.ACOS_OPERATION -> ::acos + TrigonometricOperations.ASIN_OPERATION -> ::asin + TrigonometricOperations.ATAN_OPERATION -> ::atan + PowerOperations.SQRT_OPERATION -> ::sqrt + ExponentialOperations.EXP_OPERATION -> ::exp + ExponentialOperations.LN_OPERATION -> ::ln + ExponentialOperations.COSH_OPERATION -> ::cosh + ExponentialOperations.SINH_OPERATION -> ::sinh + ExponentialOperations.TANH_OPERATION -> ::tanh + ExponentialOperations.ACOSH_OPERATION -> ::acosh + ExponentialOperations.ASINH_OPERATION -> ::asinh + ExponentialOperations.ATANH_OPERATION -> ::atanh + else -> super.unaryOperationFunction(operation) + } +} + +/** + * Advanced Number-like field that implements basic operations. + */ +public interface ExtendedField : ExtendedFieldOperations, Field, NumericAlgebra, ScaleOperations { + public override fun sinh(arg: T): T = (exp(arg) - exp(-arg)) / 2.0 + public override fun cosh(arg: T): T = (exp(arg) + exp(-arg)) / 2.0 + public override fun tanh(arg: T): T = (exp(arg) - exp(-arg)) / (exp(-arg) + exp(arg)) + public override fun asinh(arg: T): T = ln(sqrt(arg * arg + one) + arg) + public override fun acosh(arg: T): T = ln(arg + sqrt((arg - one) * (arg + one))) + public override fun atanh(arg: T): T = (ln(arg + one) - ln(one - arg)) / 2.0 + + public override fun rightSideNumberOperationFunction(operation: String): (left: T, right: Number) -> T = + when (operation) { + PowerOperations.POW_OPERATION -> ::power + else -> super.rightSideNumberOperationFunction(operation) + } +} + +/** + * A field for [Double] without boxing. Does not produce appropriate field element. + */ +@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") +public object DoubleField : ExtendedField, Norm, ScaleOperations { + public override inline val zero: Double get() = 0.0 + public override inline val one: Double get() = 1.0 + + public override fun number(value: Number): Double = value.toDouble() + + public override fun binaryOperationFunction(operation: String): (left: Double, right: Double) -> Double = + when (operation) { + PowerOperations.POW_OPERATION -> ::power + else -> super.binaryOperationFunction(operation) + } + + public override inline fun add(a: Double, b: Double): Double = a + b + + public override inline fun multiply(a: Double, b: Double): Double = a * b + public override inline fun divide(a: Double, b: Double): Double = a / b + + public override fun scale(a: Double, value: Double): Double = a * value + + public override inline fun sin(arg: Double): Double = FastMath.sin(arg) + public override inline fun cos(arg: Double): Double = FastMath.cos(arg) + public override inline fun tan(arg: Double): Double = FastMath.tan(arg) + public override inline fun acos(arg: Double): Double = FastMath.acos(arg) + public override inline fun asin(arg: Double): Double = FastMath.asin(arg) + public override inline fun atan(arg: Double): Double = FastMath.atan(arg) + + public override inline fun sinh(arg: Double): Double = FastMath.sinh(arg) + public override inline fun cosh(arg: Double): Double = FastMath.cosh(arg) + public override inline fun tanh(arg: Double): Double = FastMath.tanh(arg) + public override inline fun asinh(arg: Double): Double = FastMath.asinh(arg) + public override inline fun acosh(arg: Double): Double = FastMath.acosh(arg) + public override inline fun atanh(arg: Double): Double = FastMath.atanh(arg) + + public override inline fun power(arg: Double, pow: Number): Double = FastMath.pow(arg, pow.toDouble()) + public override inline fun exp(arg: Double): Double = FastMath.exp(arg) + public override inline fun ln(arg: Double): Double = FastMath.log(arg) + + public override inline fun norm(arg: Double): Double = FastMath.abs(arg) + + public override inline fun Double.unaryMinus(): Double = -this + public override inline fun Double.plus(b: Double): Double = this + b + public override inline fun Double.minus(b: Double): Double = this - b + public override inline fun Double.times(b: Double): Double = this * b + public override inline fun Double.div(b: Double): Double = this / b +} diff --git a/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/NumbersUtils.java b/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/NumbersUtils.java new file mode 100644 index 000000000..6fdd9dea5 --- /dev/null +++ b/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/NumbersUtils.java @@ -0,0 +1,2647 @@ +/* + * Copyright 2018-2021 KMath contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ +package space.kscience.kmath.jafama; + +/** + * Class containing various basic utility methods to deal with numbers. + * This class is meant to be light (no big look-up tables or such). + * + * Check methods return boolean if success, + * for it allows to use them in assertions. + * + * toString methods use capital letters, unlike JDK's toStrings, for it is more + * readable (especially, "l" and "1" can easily be confused with one another). + * + * Some methods have an int version additionally to the long version, + * even though long version could be used instead, for performance reasons, + * either for the methods themselves (if they do computations with ints + * instead of longs), or to be used in an int use case (like methods + * checking whether or not a signed int can fit in such number of bits). + */ +public final class NumbersUtils { + + //-------------------------------------------------------------------------- + // MEMBERS + //-------------------------------------------------------------------------- + + /** + * Double.MIN_NORMAL since Java 6. + */ + public static final double DOUBLE_MIN_NORMAL = Double.longBitsToDouble(0x0010000000000000L); // 2.2250738585072014E-308 + + /** + * Float.MIN_NORMAL since Java 6. + */ + public static final float FLOAT_MIN_NORMAL = Float.intBitsToFloat(0x00800000); // 1.17549435E-38f + + private static final int MIN_DOUBLE_EXPONENT = -1074; + private static final int MAX_DOUBLE_EXPONENT = 1023; + + /** + * All possible upper case chars for representing a number as a String. + */ + private final static char[] CHAR_BY_DIGIT; + static { + final char minDecimal = '0'; + final char maxDecimal = '9'; + final int n1 = maxDecimal - minDecimal + 1; + final char minLetter = 'A'; + final char maxLetter = 'Z'; + final int n2 = maxLetter - minLetter + 1; + CHAR_BY_DIGIT = new char[n1+n2]; + int i=0; + for (char c=minDecimal;c<=maxDecimal;c++) { + CHAR_BY_DIGIT[i++] = c; + } + for (char c=minLetter;c<=maxLetter;c++) { + CHAR_BY_DIGIT[i++] = c; + } + } + + /** + * For power-of-two radixes only. + */ + private static final int[] DIV_SHIFT_BY_RADIX; + static { + DIV_SHIFT_BY_RADIX = new int[32+1]; + int shift=1; + for (int radix=2;radix<=32;radix*=2) { + DIV_SHIFT_BY_RADIX[radix] = shift++; + } + } + + private final static int[] MAX_NBR_OF_NEG_INT_DIGITS_BY_RADIX = new int[Character.MAX_RADIX+1]; + private final static int[] MAX_NBR_OF_NEG_LONG_DIGITS_BY_RADIX = new int[Character.MAX_RADIX+1]; + static { + for (int radix=Character.MIN_RADIX;radix<=Character.MAX_RADIX;radix++) { + /* + * Brutal but works. + * -1 for the sign. + */ + MAX_NBR_OF_NEG_INT_DIGITS_BY_RADIX[radix] = Integer.toString(Integer.MIN_VALUE, radix).length()-1; + MAX_NBR_OF_NEG_LONG_DIGITS_BY_RADIX[radix] = Long.toString(Long.MIN_VALUE, radix).length()-1; + } + } + + static final double NO_CSN_MIN_BOUND_INCL = 1e-3; + static final double NO_CSN_MAX_BOUND_EXCL = 1e7; + + 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 PI_HI = 2*PIO2_HI; + private static final double PI_LO = 2*PIO2_LO; + private static final double TWOPI_HI = 4*PIO2_HI; + private static final double TWOPI_LO = 4*PIO2_LO; + + //-------------------------------------------------------------------------- + // PUBLIC METHODS + //-------------------------------------------------------------------------- + + /** + * @return True if the specified values are equal or both NaN, false otherwise. + */ + public static boolean equal(float a, float b) { + // Only does one test if a == b. + return (a == b) ? true : ((a != a) && (b != b)); + } + + /** + * @return True if the specified values are equal or both NaN, false otherwise. + */ + public static boolean equal(double a, double b) { + // Only does one test if a == b. + return (a == b) ? true : ((a != a) && (b != b)); + } + + /** + * @return True if the specified value is a mathematical integer, + * false otherwise (which includes NaN and +-Infinity). + */ + public static boolean isMathematicalInteger(float value) { + // Doing magnitude test first, for cast + // might be very slow for huge values. + // It also helps be faster for huge values, + // for which the test with cast always fail. + value = Math.abs(value); + return ((value >= (float)(1<<23) + && (value != Float.POSITIVE_INFINITY))) + || (value == (float)(int)value); + } + + /** + * @return True if the specified value is a mathematical integer, + * false otherwise (which includes NaN and +-Infinity). + */ + public static boolean isMathematicalInteger(double value) { + // Doing magnitude test first, for cast + // might be very slow for huge values. + // It also helps be faster for huge values, + // for which the test with cast always fail. + value = Math.abs(value); + return ((value >= (double)(1L<<52)) + && (value != Double.POSITIVE_INFINITY)) + || (value == (double)(long)value); + } + + /** + * @param value A float value. + * @return True if the specified value is equidistant from two adjacent + * mathematical integers, false otherwise (which includes NaN + * and +-Infinity). + */ + public static boolean isEquidistant(float value) { + if (false) { + // Also works, but slower. + final int bits = Float.floatToRawIntBits(value); + final int exponent = ((bits>>23)&0xFF)-127; + final int nbrOfPostCommaBits = 23 - exponent; + if ((nbrOfPostCommaBits <= 0) || (nbrOfPostCommaBits >= 25)) { + // No mantissa bit after comma, or all mantissa bits + // (including implicit 1) are at least one bit away from it. + //System.out.println("can't be"); + return false; + } + final int mantissa = 0x00800000|(bits&0x007FFFFF); + final int postCommaMask = ~((-1)<>52))&0x7FF)-1023; + final int nbrOfPostCommaBits = 52 - exponent; + if ((nbrOfPostCommaBits <= 0) || (nbrOfPostCommaBits >= 54)) { + // No mantissa bit after comma, or all mantissa bits + // (including implicit 1) are at least one bit away from it. + return false; + } + final long mantissa = 0x0010000000000000L|(bits&0x000FFFFFFFFFFFFFL); + final long postCommaMask = ~((-1L)< value is NaN or +-Infinity + return !(value-value == 0.0f); + } + + /** + * @param value A double value. + * @return True if the specified value is NaN or +-Infinity, false otherwise. + */ + public static boolean isNaNOrInfinite(double value) { + // value-value is not equal to 0.0 (and is NaN) <-> value is NaN or +-Infinity + return !(value-value == 0.0); + } + + /** + * @param value A float value. + * @return -1 if sign bit is 1, 1 if sign bit is 0. + */ + public static int signFromBit(float value) { + return ((Float.floatToRawIntBits(value)>>30)|1); + } + + /** + * @param value A double value. + * @return -1 if sign bit is 1, 1 if sign bit is 0. + */ + public static long signFromBit(double value) { + // Returning a long, to avoid useless cast into int. + return ((Double.doubleToRawLongBits(value)>>62)|1); + } + + /* + * min/max ranges + */ + + /** + * @return True if the specified value is in the specified range (inclusive), false otherwise. + */ + public static boolean isInRange(int min, int max, int a) { + return (min <= a) && (a <= max); + } + + /** + * @return True if the specified value is in the specified range (inclusive), false otherwise. + */ + public static boolean isInRange(long min, long max, long a) { + return (min <= a) && (a <= max); + } + + /** + * Returns false if any value is NaN. + * + * @return True if the specified value is in the specified range (inclusive), false otherwise. + */ + public static boolean isInRange(float min, float max, float a) { + return (min <= a) && (a <= max); + } + + /** + * Returns false if any value is NaN. + * + * @return True if the specified value is in the specified range (inclusive), false otherwise. + */ + public static boolean isInRange(double min, double max, double a) { + return (min <= a) && (a <= max); + } + + /* + * + */ + + /** + * @return True if does not throw. + * @throws IllegalArgumentException if the specified value is not in the specified range (inclusive). + */ + public static boolean checkIsInRange(int min, int max, int a) { + if (!isInRange(min, max, a)) { + throw new IllegalArgumentException(a+" not in ["+min+","+max+"]"); + } + return true; + } + + /** + * @return True if does not throw. + * @throws IllegalArgumentException if the specified value is not in the specified range (inclusive). + */ + public static boolean checkIsInRange(long min, long max, long a) { + if (!isInRange(min, max, a)) { + throw new IllegalArgumentException(a+" not in ["+min+","+max+"]"); + } + return true; + } + + /** + * @return True if does not throw. + * @throws IllegalArgumentException if the specified value is not in the specified range (inclusive) + * or any parameter is NaN. + */ + public static boolean checkIsInRange(float min, float max, float a) { + if (!isInRange(min, max, a)) { + throw new IllegalArgumentException(a+" not in ["+min+","+max+"]"); + } + return true; + } + + /** + * @return True if does not throw. + * @throws IllegalArgumentException if the specified value is not in the specified range (inclusive) + * or any parameter is NaN. + */ + public static boolean checkIsInRange(double min, double max, double a) { + if (!isInRange(min, max, a)) { + throw new IllegalArgumentException(a+" not in ["+min+","+max+"]"); + } + return true; + } + + /* + * + */ + + /** + * @param min A value. + * @param max A value. + * @param a A value. + * @return min if a <= min, else max if a >= max, else a. + */ + public static int toRange(int min, int max, int a) { + if (a <= min) { + return min; + } else if (a >= max) { + return max; + } else { + return a; + } + } + + /** + * @param min A value. + * @param max A value. + * @param a A value. + * @return min if a <= min, else max if a >= max, else a. + */ + public static long toRange(long min, long max, long a) { + if (a <= min) { + return min; + } else if (a >= max) { + return max; + } else { + return a; + } + } + + /** + * @param min A value. + * @param max A value. + * @param a A value. + * @return min if a <= min, else max if a >= max, else a. + */ + public static float toRange(float min, float max, float a) { + if (a <= min) { + return min; + } else if (a >= max) { + return max; + } else { + return a; + } + } + + /** + * @param min A value. + * @param max A value. + * @param a A value. + * @return min if a <= min, else max if a >= max, else a. + */ + public static double toRange(double min, double max, double a) { + if (a <= min) { + return min; + } else if (a >= max) { + return max; + } else { + return a; + } + } + + /* + * bitwise ranges + */ + + /** + * @param bitSize A number of bits, in [1,32]. + * @return True if the specified value fits as a signed integer + * over the specified number of bits, false otherwise. + * @throws IllegalArgumentException if the specified number of bits is not in [1,32]. + */ + public static boolean isInRangeSigned(int a, int bitSize) { + checkBitSizeForSignedInt(bitSize); + return (minSignedIntForBitSize_noCheck(bitSize) <= a) && (a <= maxSignedIntForBitSize_noCheck(bitSize)); + } + + /** + * @param bitSize A number of bits, in [1,64]. + * @return True if the specified value fits as a signed integer + * over the specified number of bits, false otherwise. + * @throws IllegalArgumentException if the specified number of bits is not in [1,64]. + */ + public static boolean isInRangeSigned(long a, int bitSize) { + checkBitSizeForSignedLong(bitSize); + return (minSignedLongForBitSize_noCheck(bitSize) <= a) && (a <= maxSignedLongForBitSize_noCheck(bitSize)); + } + + /** + * @param bitSize A number of bits, in [1,31]. + * @return True if the specified value fits as an unsigned integer + * over the specified number of bits, false otherwise. + * @throws IllegalArgumentException if the specified number of bits is not in [1,31]. + */ + public static boolean isInRangeUnsigned(int a, int bitSize) { + return isInRange(0, maxUnsignedIntForBitSize(bitSize), a); + } + + /** + * @param bitSize A number of bits, in [1,63]. + * @return True if the specified value fits as an unsigned integer + * over the specified number of bits, false otherwise. + * @throws IllegalArgumentException if the specified number of bits is not in [1,63]. + */ + public static boolean isInRangeUnsigned(long a, int bitSize) { + return isInRange(0, maxUnsignedLongForBitSize(bitSize), a); + } + + /* + * + */ + + /** + * @param bitSize A number of bits, in [1,32]. + * @return True if does not throw. + * @throws IllegalArgumentException if the specified value does not fit + * as a signed integer over the specified number of bits. + */ + public static boolean checkIsInRangeSigned(int a, int bitSize) { + if (!isInRangeSigned(a, bitSize)) { + throw new IllegalArgumentException(a+" does not fit as a signed value over "+bitSize+" bits"); + } + return true; + } + + /** + * @param bitSize A number of bits, in [1,64]. + * @return True if does not throw. + * @throws IllegalArgumentException if the specified value does not fit + * as a signed integer over the specified number of bits. + */ + public static boolean checkIsInRangeSigned(long a, int bitSize) { + if (!isInRangeSigned(a, bitSize)) { + throw new IllegalArgumentException(a+" does not fit as a signed value over "+bitSize+" bits"); + } + return true; + } + + /** + * @param bitSize A number of bits, in [1,31]. + * @return True if does not throw. + * @throws IllegalArgumentException if the specified value does not fit + * as an unsigned integer over the specified number of bits. + */ + public static boolean checkIsInRangeUnsigned(int a, int bitSize) { + if (!isInRangeUnsigned(a, bitSize)) { + throw new IllegalArgumentException(a+" does not fit as an unsigned value over "+bitSize+" bits"); + } + return true; + } + + /** + * @param bitSize A number of bits, in [1,63]. + * @return True if does not throw. + * @throws IllegalArgumentException if the specified value does not fit + * as an unsigned integer over the specified number of bits. + */ + public static boolean checkIsInRangeUnsigned(long a, int bitSize) { + if (!isInRangeUnsigned(a, bitSize)) { + throw new IllegalArgumentException(a+" does not fit as an unsigned value over "+bitSize+" bits"); + } + return true; + } + + /* + * masks (int) + */ + + /** + * @param bitSize A number of bits, in [0,32]. + * @return Mask with the specified number of left bits set with 0, + * and other bits set with 1. + */ + public static int intMaskMSBits0(int bitSize) { + checkIsInRange(0, 32, bitSize); + // Shifting in two times, for >>> doesn't work for full bit size (<< as well). + final int halfish = (bitSize>>1); + return ((-1)>>>halfish)>>>(bitSize-halfish); + } + + /** + * @param bitSize A number of bits, in [0,32]. + * @return Mask with the specified number of left bits set with 1, + * and other bits set with 0. + */ + public static int intMaskMSBits1(int bitSize) { + return ~intMaskMSBits0(bitSize); + } + + /** + * @param bitSize A number of bits, in [0,32]. + * @return Mask with the specified number of right bits set with 0, + * and other bits set with 1. + */ + public static int intMaskLSBits0(int bitSize) { + return ~intMaskMSBits0(32-bitSize); + } + + /** + * @param bitSize A number of bits, in [0,32]. + * @return Mask with the specified number of right bits set with 1, + * and other bits set with 0. + */ + public static int intMaskLSBits1(int bitSize) { + return intMaskMSBits0(32-bitSize); + } + + /* + * masks (long) + */ + + /** + * @param bitSize A number of bits, in [0,64]. + * @return Mask with the specified number of left bits set with 0, + * and other bits set with 1. + */ + public static long longMaskMSBits0(int bitSize) { + checkIsInRange(0, 64, bitSize); + // Shifting in two times, for >>> doesn't work for full bit size (<< as well). + final int halfish = (bitSize>>1); + return ((-1L)>>>halfish)>>>(bitSize-halfish); + } + + /** + * @param bitSize A number of bits, in [0,64]. + * @return Mask with the specified number of left bits set with 1, + * and other bits set with 0. + */ + public static long longMaskMSBits1(int bitSize) { + return ~longMaskMSBits0(bitSize); + } + + /** + * @param bitSize A number of bits, in [0,64]. + * @return Mask with the specified number of right bits set with 0, + * and other bits set with 1. + */ + public static long longMaskLSBits0(int bitSize) { + return ~longMaskMSBits0(64-bitSize); + } + + /** + * @param bitSize A number of bits, in [0,64]. + * @return Mask with the specified number of right bits set with 1, + * and other bits set with 0. + */ + public static long longMaskLSBits1(int bitSize) { + return longMaskMSBits0(64-bitSize); + } + + /* + * signed/unsigned + */ + + /** + * @return Unsigned value corresponding to bits of the specified byte. + */ + public static short byteAsUnsigned(byte value) { + return (short)(((short)value) & 0xFF); + } + + /** + * @return Unsigned value corresponding to bits of the specified short. + */ + public static int shortAsUnsigned(short value) { + return ((int)value) & 0xFFFF; + } + + /** + * @return Unsigned value corresponding to bits of the specified int. + */ + public static long intAsUnsigned(int value) { + return ((long)value) & 0xFFFFFFFF; + } + + /* + * bitwise ranges + */ + + /** + * @return True if a signed int value can be read over the specified number of bits, + * i.e. if it is in [1,32], false otherwise. + */ + public static boolean isValidBitSizeForSignedInt(int bitSize) { + return (bitSize > 0) && (bitSize <= 32); + } + + /** + * @return True if a signed long value can be read over the specified number of bits, + * i.e. if it is in [1,64], false otherwise. + */ + public static boolean isValidBitSizeForSignedLong(int bitSize) { + return (bitSize > 0) && (bitSize <= 64); + } + + /** + * @return True if an unsigned int value can be read over the specified number of bits, + * i.e. if it is in [1,31], false otherwise. + */ + public static boolean isValidBitSizeForUnsignedInt(int bitSize) { + return (bitSize > 0) && (bitSize < 32); + } + + /** + * @return True if an unsigned long value can be read over the specified number of bits, + * i.e. if it is in [1,63], false otherwise. + */ + public static boolean isValidBitSizeForUnsignedLong(int bitSize) { + return (bitSize > 0) && (bitSize < 64); + } + + /* + * + */ + + /** + * @return True if does not throw. + * @throws IllegalArgumentException if a signed int value can't be read over the + * specified number of bits, i.e. if it is not in [1,32]. + */ + public static boolean checkBitSizeForSignedInt(int bitSize) { + if (!isValidBitSizeForSignedInt(bitSize)) { + throw new IllegalArgumentException("bit size ["+bitSize+"] must be in [1,32] for signed int values"); + } + return true; + } + + /** + * @return True if does not throw. + * @throws IllegalArgumentException if a signed long value can't be read over the + * specified number of bits, i.e. if it is not in [1,64]. + */ + public static boolean checkBitSizeForSignedLong(int bitSize) { + if (!isValidBitSizeForSignedLong(bitSize)) { + throw new IllegalArgumentException("bit size ["+bitSize+"] must be in [1,64] for signed long values"); + } + return true; + } + + /** + * @return True if does not throw. + * @throws IllegalArgumentException if an unsigned int value can't be read over the + * specified number of bits, i.e. if it is not in [1,31]. + */ + public static boolean checkBitSizeForUnsignedInt(int bitSize) { + if (!isValidBitSizeForUnsignedInt(bitSize)) { + throw new IllegalArgumentException("bit size ["+bitSize+"] must be in [1,31] for unsigned int values"); + } + return true; + } + + /** + * @return True if does not throw. + * @throws IllegalArgumentException if an unsigned long value can't be read over the + * specified number of bits, i.e. if it is not in [1,63]. + */ + public static boolean checkBitSizeForUnsignedLong(int bitSize) { + if (!isValidBitSizeForUnsignedLong(bitSize)) { + throw new IllegalArgumentException("bit size ["+bitSize+"] must be in [1,63] for unsigned long values"); + } + return true; + } + + /* + * + */ + + /** + * @param bitSize A number of bits in [1,32]. + * @return The min signed int value that can be stored over the specified number of bits. + * @throws IllegalArgumentException if the specified number of bits is out of range. + */ + public static int minSignedIntForBitSize(int bitSize) { + checkBitSizeForSignedInt(bitSize); + return minSignedIntForBitSize_noCheck(bitSize); + } + + /** + * @param bitSize A number of bits in [1,64]. + * @return The min signed long value that can be stored over the specified number of bits. + * @throws IllegalArgumentException if the specified number of bits is out of range. + */ + public static long minSignedLongForBitSize(int bitSize) { + checkBitSizeForSignedLong(bitSize); + return minSignedLongForBitSize_noCheck(bitSize); + } + + /** + * @param bitSize A number of bits in [1,32]. + * @return The max signed int value that can be stored over the specified number of bits. + * @throws IllegalArgumentException if the specified number of bits is out of range. + */ + public static int maxSignedIntForBitSize(int bitSize) { + checkBitSizeForSignedInt(bitSize); + return maxSignedIntForBitSize_noCheck(bitSize); + } + + /** + * @param bitSize A number of bits in [1,64]. + * @return The max signed long value that can be stored over the specified number of bits. + * @throws IllegalArgumentException if the specified number of bits is out of range. + */ + public static long maxSignedLongForBitSize(int bitSize) { + checkBitSizeForSignedLong(bitSize); + return maxSignedLongForBitSize_noCheck(bitSize); + } + + /** + * @param bitSize A number of bits in [1,31]. + * @return The max unsigned int value that can be stored over the specified number of bits. + * @throws IllegalArgumentException if the specified number of bits is out of range. + */ + public static int maxUnsignedIntForBitSize(int bitSize) { + checkBitSizeForUnsignedInt(bitSize); + // i.e. (1<>(31-bitSize)); + } + + /** + * @param bitSize A number of bits in [1,63]. + * @return The max unsigned long value that can be stored over the specified number of bits. + * @throws IllegalArgumentException if the specified number of bits is out of range. + */ + public static long maxUnsignedLongForBitSize(int bitSize) { + checkBitSizeForUnsignedLong(bitSize); + // i.e. (1L<>(63-bitSize)); + } + + /* + * + */ + + /** + * @return The number of bits required to store the specified value as a signed integer, + * i.e. a result in [1,32]. + */ + public static int bitSizeForSignedValue(int value) { + if (value > 0) { + return 33-Integer.numberOfLeadingZeros(value); + } else if (value == 0) { + return 1; + } else { + // Works for Integer.MIN_VALUE as well. + return 33-Integer.numberOfLeadingZeros(-value-1); + } + } + + /** + * @return The number of bits required to store the specified value as a signed integer, + * i.e. a result in [1,64]. + */ + public static int bitSizeForSignedValue(long value) { + if (value > 0) { + return 65-Long.numberOfLeadingZeros(value); + } else if (value == 0) { + return 1; + } else { + // Works for Long.MIN_VALUE as well. + return 65-Long.numberOfLeadingZeros(-value-1); + } + } + + /** + * @param value An integer value in [0,Integer.MAX_VALUE]. + * @return The number of bits required to store the specified value as an unsigned integer, + * i.e. a result in [1,31]. + * @throws IllegalArgumentException if the specified value is < 0. + */ + public static int bitSizeForUnsignedValue(int value) { + if (value > 0) { + return 32-Integer.numberOfLeadingZeros(value); + } else { + if (value == 0) { + return 1; + } else { + throw new IllegalArgumentException("unsigned value ["+value+"] must be >= 0"); + } + } + } + + /** + * @param value An integer value in [0,Long.MAX_VALUE]. + * @return The number of bits required to store the specified value as an unsigned integer, + * i.e. a result in [1,63]. + * @throws IllegalArgumentException if the specified value is < 0. + */ + public static int bitSizeForUnsignedValue(long value) { + if (value > 0) { + return 64-Long.numberOfLeadingZeros(value); + } else { + if (value == 0) { + return 1; + } else { + throw new IllegalArgumentException("unsigned value ["+value+"] must be >= 0"); + } + } + } + + /* + * integer functions + */ + + /** + * @return 1 if the specified value is > 0, 0 if it is 0, -1 otherwise. + */ + public static int signum(int a) { + return (a < 0) ? -1 : ((a == 0) ? 0 : 1); + } + + /** + * @return 1 if the specified value is > 0, 0 if it is 0, -1 otherwise. + */ + public static int signum(long a) { + return (a < 0) ? -1 : ((a == 0) ? 0 : 1); + } + + /** + * @return True if the specified value is even, false otherwise. + */ + public static boolean isEven(int a) { + return ((a&1) == 0); + } + + /** + * @return True if the specified value is even, false otherwise. + */ + public static boolean isEven(long a) { + // faster to work on ints + return isEven((int)a); + } + + /** + * @return True if the specified value is odd, false otherwise. + */ + public static boolean isOdd(int a) { + return ((a&1) != 0); + } + + /** + * @return True if the specified value is odd, false otherwise. + */ + public static boolean isOdd(long a) { + // faster to work on ints + return isOdd((int)a); + } + + /** + * @return True if the specified values are both even or both odd, false otherwise. + */ + public static boolean haveSameEvenness(int a, int b) { + return (((a^b)&1) == 0); + } + + /** + * @return True if the specified values are both even or both odd, false otherwise. + */ + public static boolean haveSameEvenness(long a, long b) { + // faster to work on ints + return haveSameEvenness((int)a, (int)b); + } + + /** + * @return True if the specified values are both >= 0 or both < 0, false otherwise. + */ + public static boolean haveSameSign(int a, int b) { + return ((a^b) >= 0); + } + + /** + * @return True if the specified values are both >= 0 or both < 0, false otherwise. + */ + public static boolean haveSameSign(long a, long b) { + return ((a^b) >= 0); + } + + /** + * @return True if the specified value is a power of two, + * i.e. a value of the form 2^k, with k >= 0. + */ + public static boolean isPowerOfTwo(int a) { + if (a <= 0) { + return false; + } + if (false) { + // also works + return (a & -a) == a; + } + return (a & (a-1)) == 0; + } + + /** + * @return True if the specified value is a power of two, + * i.e. a value of the form 2^k, with k >= 0. + */ + public static boolean isPowerOfTwo(long a) { + if (a <= 0) { + return false; + } + if (false) { + // also works + return (a & -a) == a; + } + return (a & (a-1)) == 0; + } + + /** + * @return True if the specified value is a signed power of two, + * i.e. a value of the form +-2^k, with k >= 0. + */ + public static boolean isSignedPowerOfTwo(int a) { + if (a > 0) { + return (a & (a-1)) == 0; + } else { + if (a == -a) { + // a is 0 or Integer.MIN_VALUE + return (a != 0); + } + return ((-a) & (-a-1)) == 0; + } + } + + /** + * @return True if the specified value is a signed power of two, + * i.e. a value of the form +-2^k, with k >= 0. + */ + public static boolean isSignedPowerOfTwo(long a) { + if (a > 0) { + return (a & (a-1)) == 0; + } else { + if (a == -a) { + // a is 0 or Long.MIN_VALUE + return (a != 0); + } + return ((-a) & (-a-1)) == 0; + } + } + + /** + * @param a A value in [1,Integer.MAX_VALUE]. + * @return The highest power of two <= a. + */ + public static int floorPowerOfTwo(int a) { + if (a <= 0) { + throw new IllegalArgumentException("a ["+a+"] must be > 0"); + } + return Integer.highestOneBit(a); + } + + /** + * @param a A value in [1,Long.MAX_VALUE]. + * @return The highest power of two <= a. + */ + public static long floorPowerOfTwo(long a) { + if (a <= 0) { + throw new IllegalArgumentException("a ["+a+"] must be > 0"); + } + // Faster than copying int method + // (less computations on long). + return 1L << (63 - Long.numberOfLeadingZeros(a)); + } + + /** + * @param a A value in [0,2^30]. + * @return The lowest power of two >= a. + */ + public static int ceilingPowerOfTwo(int a) { + checkIsInRange(0, (1<<30), a); + return (a >= 2) ? Integer.highestOneBit((a-1)<<1) : 1; + } + + /** + * @param a A value in [0,2^62]. + * @return The lowest power of two >= a. + */ + public static long ceilingPowerOfTwo(long a) { + checkIsInRange(0L, (1L<<62), a); + // Faster than copying int method + // (less computations on long). + return 1L << (64 - Long.numberOfLeadingZeros(a - 1)); + } + + /** + * @return Mean without overflow, rounded to the lowest value (i.e. mathematical floor((a+b)/2), using floating point division). + */ + public static int meanLow(int a, int b) { + return (a & b) + ((a ^ b) >> 1); + } + + /** + * @return Mean without overflow, rounded to the lowest value (i.e. mathematical floor((a+b)/2), using floating point division). + */ + public static long meanLow(long a, long b) { + return (a & b) + ((a ^ b) >> 1); + } + + /** + * @return Mean without overflow, rounded to the value of smallest magnitude (i.e. mathematical (a+b)/2, using integer division). + */ + public static int meanSml(int a, int b) { + int result = meanLow(a,b); + if (!haveSameEvenness(a, b)) { + // inexact + if (((a&b) < 0) || (((a|b) < 0) && (a+b < 0))) { + // both < 0, or only one is < 0 and it has the largest magnitude + result++; + } + } + return result; + } + + /** + * @return Mean without overflow, rounded to the value of smallest magnitude (i.e. mathematical (a+b)/2, using integer division). + */ + public static long meanSml(long a, long b) { + long result = meanLow(a,b); + if (!haveSameEvenness(a, b)) { + // inexact + if (((a&b) < 0) || (((a|b) < 0) && (a+b < 0))) { + // both < 0, or only one is < 0 and it has the largest magnitude + result++; + } + } + return result; + } + + /** + * Useful because a positive int value could not represent half the width + * of full int range width, which is mathematically Integer.MAX_VALUE+1. + * + * @return Minus half the range width (inclusive, and rounded to the value of smaller magnitude) + * between the specified bounds. + * @throws IllegalArgumentException if min > max. + */ + public static int negHalfWidth(int min, int max) { + if (min > max) { + throw new IllegalArgumentException("min ["+min+"] must be <= max ["+max+"]"); + } + int mean = meanLow(min, max); + return min - mean - ((min^max)&1); + } + + /** + * Useful because a positive long value could not represent half the width + * of full long range width, which is mathematically Long.MAX_VALUE+1. + * + * @return Minus half the range width (inclusive, and rounded to the value of smaller magnitude) + * between the specified bounds. + * @throws IllegalArgumentException if min > max. + */ + public static long negHalfWidth(long min, long max) { + if (min > max) { + throw new IllegalArgumentException("min ["+min+"] must be <= max ["+max+"]"); + } + long mean = meanLow(min, max); + return min - mean - ((min^max)&1); + } + + /** + * This treatment being designed for optimization, the fact that spot + * is a signed power of two is not checked. + * + * @param value A value. + * @param spot A signed power of two (i.e. a value of the form +-2^k, k >= 0). + * @return value % spot, i.e. a value in ]-|spot|,|spot|[. + */ + public static int moduloSignedPowerOfTwo(int value, int spot) { + if (spot == Integer.MIN_VALUE) { + return (value != Integer.MIN_VALUE) ? value : 0; + } else { + int s = (value>>31); + return ((((value+s) ^ s) & (abs(spot)-1)) + s) ^ s; + } + } + + /** + * This treatment being designed for optimization, the fact that spot + * is a signed power of two is not checked. + * + * @param value A value. + * @param spot A signed power of two (i.e. a value of the form +-2^k, k >= 0). + * @return value % spot, i.e. a value in ]-|spot|,|spot|[. + */ + public static long moduloSignedPowerOfTwo(long value, long spot) { + if (spot == Long.MIN_VALUE) { + return (value != Long.MIN_VALUE) ? value : 0; + } else { + long s = (value>>63); + return ((((value+s) ^ s) & (abs(spot)-1)) + s) ^ s; + } + } + + /** + * @param value An integer value > 0. + * @return The integer part of the logarithm, in base 2, of the specified value, + * i.e. a result in [0,30] + * @throws IllegalArgumentException if the specified value is <= 0. + */ + public static int log2(int value) { + if (value <= 0) { + throw new IllegalArgumentException("value ["+value+"] must be > 0"); + } + return 31-Integer.numberOfLeadingZeros(value); + } + + /** + * @param value An integer value > 0. + * @return The integer part of the logarithm, in base 2, of the specified value, + * i.e. a result in [0,62] + * @throws IllegalArgumentException if the specified value is <= 0. + */ + public static int log2(long value) { + if (value <= 0) { + throw new IllegalArgumentException("value ["+value+"] must be > 0"); + } + return 63-Long.numberOfLeadingZeros(value); + } + + /** + * Possibly faster than java.lang.Math.abs(int). + * + * @return The absolute value, except if value is Integer.MIN_VALUE, for which it returns Integer.MIN_VALUE. + */ + public static int abs(int a) { + return (a^(a>>31))-(a>>31); + } + + /** + * Possibly faster than java.lang.Math.abs(long). + * + * @return The absolute value, except if value is Long.MIN_VALUE, for which it returns Long.MIN_VALUE. + */ + public static long abs(long a) { + return (a^(a>>63))-(a>>63); + } + + /** + * @return The negative of the absolute value (always exact). + */ + public static int absNeg(int a) { + return (a>>31)-(a^(a>>31)); + } + + /** + * @return The negative of the absolute value (always exact). + */ + public static long absNeg(long a) { + return (a>>63)-(a^(a>>63)); + } + + /** + * If the specified value is in int range, the returned value is identical. + * + * @return An int hash of the specified value. + */ + public static int intHash(long a) { + if (false) { + // also works + int hash = ((int)(a>>32)) ^ ((int)a); + if (a < 0) { + hash = -hash-1; + } + return hash; + } + int hash = ((int)(a>>32)) + ((int)a); + if (a < 0) { + hash++; + } + return hash; + } + + /** + * @param a An int value. + * @return The specified value as byte. + * @throws ArithmeticException if the specified value is not in [Byte.MIN_VALUE,Byte.MAX_VALUE] range. + */ + public static byte asByte(int a) { + if (a != (byte)a) { + throw new ArithmeticException("overflow: "+a); + } + return (byte)a; + } + + /** + * @param a A long value. + * @return The specified value as int. + * @throws ArithmeticException if the specified value is not in [Integer.MIN_VALUE,Integer.MAX_VALUE] range. + */ + public static int asInt(long a) { + if (a != (int)a) { + throw new ArithmeticException("overflow: "+a); + } + return (int)a; + } + + /** + * @param a A long value. + * @return The closest int value in [Integer.MIN_VALUE,Integer.MAX_VALUE] range. + */ + public static int toInt(long a) { + if (a != (int)a) { + return (a < 0) ? Integer.MIN_VALUE : Integer.MAX_VALUE; + } + return (int)a; + } + + /** + * @param a An int value. + * @param b An int value. + * @return The mathematical result of a+b. + * @throws ArithmeticException if the mathematical result of a+b is not in [Integer.MIN_VALUE,Integer.MAX_VALUE] range. + */ + public static int plusExact(int a, int b) { + final int sum = a + b; + // HD 2-12 Overflow iff both arguments + // have the opposite sign of the result. + if (((a ^ sum) & (b ^ sum)) < 0) { + throw new ArithmeticException("overflow: "+a+"+"+b); + } + return sum; + } + + /** + * @param a A long value. + * @param b A long value. + * @return The mathematical result of a+b. + * @throws ArithmeticException if the mathematical result of a+b is not in [Long.MIN_VALUE,Long.MAX_VALUE] range. + */ + public static long plusExact(long a, long b) { + final long sum = a + b; + // HD 2-12 Overflow iff both arguments + // have the opposite sign of the result. + if (((a ^ sum) & (b ^ sum)) < 0) { + throw new ArithmeticException("overflow: "+a+"+"+b); + } + return sum; + } + + /** + * @param a An int value. + * @param b An int value. + * @return The int value of [Integer.MIN_VALUE,Integer.MAX_VALUE] range which is the closest to mathematical result of a+b. + */ + public static int plusBounded(int a, int b) { + return toInt(((long)a) + ((long)b)); + } + + /** + * @param a A long value. + * @param b A long value. + * @return The long value of [Long.MIN_VALUE,Long.MAX_VALUE] range which is the closest to mathematical result of a+b. + */ + public static long plusBounded(long a, long b) { + final long sum = a + b; + if (((a ^ sum) & (b ^ sum)) < 0) { + return (sum >= 0) ? Long.MIN_VALUE : Long.MAX_VALUE; + } + return sum; + } + + /** + * @param a An int value. + * @param b An int value. + * @return The mathematical result of a-b. + * @throws ArithmeticException if the mathematical result of a-b is not in [Integer.MIN_VALUE,Integer.MAX_VALUE] range. + */ + public static int minusExact(int a, int b) { + final int diff = a - b; + // HD 2-12 Overflow iff the arguments have different signs and + // the sign of the result is different than the sign of "a". + if (((a ^ b) & (a ^ diff)) < 0) { + throw new ArithmeticException("integer overflow"); + } + return diff; + } + + /** + * @param a A long value. + * @param b A long value. + * @return The mathematical result of a-b. + * @throws ArithmeticException if the mathematical result of a-b is not in [Long.MIN_VALUE,Long.MAX_VALUE] range. + */ + public static long minusExact(long a, long b) { + final long diff = a - b; + // HD 2-12 Overflow iff the arguments have different signs and + // the sign of the result is different than the sign of "a". + if (((a ^ b) & (a ^ diff)) < 0) { + throw new ArithmeticException("integer overflow"); + } + return diff; + } + + /** + * @param a An int value. + * @param b An int value. + * @return The int value of [Integer.MIN_VALUE,Integer.MAX_VALUE] range which is the closest to mathematical result of a-b. + */ + public static int minusBounded(int a, int b) { + return toInt(((long)a) - ((long)b)); + } + + /** + * @param a A long value. + * @param b A long value. + * @return The long value of [Long.MIN_VALUE,Long.MAX_VALUE] range which is the closest to mathematical result of a-b. + */ + public static long minusBounded(long a, long b) { + final long diff = a - b; + if (((a ^ b) & (a ^ diff)) < 0) { + return (diff >= 0) ? Long.MIN_VALUE : Long.MAX_VALUE; + } + return diff; + } + + /** + * @param a An int value. + * @param b An int value. + * @return The mathematical result of a*b. + * @throws ArithmeticException if the mathematical result of a*b is not in [Integer.MIN_VALUE,Integer.MAX_VALUE] range. + */ + public static int timesExact(int a, int b) { + final long prod = a * (long)b; + if (prod != (int)prod) { + throw new ArithmeticException("overflow: "+a+"*"+b); + } + return (int)prod; + } + + /** + * @param a A long value. + * @param b A long value. + * @return The mathematical result of a*b. + * @throws ArithmeticException if the mathematical result of a*b is not in [Long.MIN_VALUE,Long.MAX_VALUE] range. + */ + public static long timesExact(long a, long b) { + final long prod = a * b; + final long absA = abs(a); + final long absB = abs(b); + if (((absA|absB)>>>31) != 0) { + // Some bits greater than 2^31 that might cause overflow + // Check the result using the divide operator + // and check for the special case of Long.MIN_VALUE * -1 + if (((b != 0) && (prod/b != a)) || + ((a == Long.MIN_VALUE) && (b == -1))) { + throw new ArithmeticException("overflow: "+a+"*"+b); + } + } + return prod; + } + + /** + * @param a An int value. + * @param b An int value. + * @return The int value of [Integer.MIN_VALUE,Integer.MAX_VALUE] range which is the closest to mathematical result of a*b. + */ + public static int timesBounded(int a, int b) { + return (int)(a * (double)b); + } + + /** + * @param a A long value. + * @param b A long value. + * @return The long value of [Long.MIN_VALUE,Long.MAX_VALUE] range which is the closest to mathematical result of a*b. + */ + public static long timesBounded(long a, long b) { + final long prod = a * b; + final long absA = abs(a); + final long absB = abs(b); + if (((absA|absB)>>>31) != 0) { + // Some bits greater than 2^31 that might cause overflow + // Check the result using the divide operator + // and check for the special case of Long.MIN_VALUE * -1 + if (((b != 0) && (prod/b != a)) || + ((a == Long.MIN_VALUE) && (b == -1))) { + return ((a^b) >= 0) ? Long.MAX_VALUE : Long.MIN_VALUE; + } + } + return prod; + } + + /* + * powers + */ + + /** + * Returns the exact result, provided it's in double range, + * i.e. if power is in [-1074,1023]. + * + * @param power An int power. + * @return 2^power as a double, or +-Infinity in case of overflow. + */ + public static double twoPow(int power) { + if (power <= -MAX_DOUBLE_EXPONENT) { // Not normal. + if (power >= MIN_DOUBLE_EXPONENT) { // Subnormal. + return Double.longBitsToDouble(0x0008000000000000L>>(-(power+MAX_DOUBLE_EXPONENT))); + } else { // Underflow. + return 0.0; + } + } else if (power > MAX_DOUBLE_EXPONENT) { // Overflow. + return Double.POSITIVE_INFINITY; + } else { // Normal. + return Double.longBitsToDouble(((long)(power+MAX_DOUBLE_EXPONENT))<<52); + } + } + + /** + * @param power An int power. + * @return 2^power as an int. + * @throws ArithmeticException if the mathematical result + * is not in int range, i.e. if power is not in [0,30]. + */ + public static int twoPowAsIntExact(int power) { + if ((power < 0) || (power > 30)) { + throw new ArithmeticException("integer overflow"); + } + return 1 << power; + } + + /** + * @param power An int power. + * @return 2^power as an int, or the closest power of two in int range + * in case of overflow, i.e. if power is not in [0,30]. + */ + public static int twoPowAsIntBounded(int power) { + power = toRange(0, 30, power); + return 1 << power; + } + + /** + * @param power An int power. + * @return 2^power as a long. + * @throws ArithmeticException if the mathematical result + * is not in long range, i.e. if power is not in [0,62]. + */ + public static long twoPowAsLongExact(int power) { + if ((power < 0) || (power > 62)) { + throw new ArithmeticException("long overflow"); + } + return 1L << power; + } + + /** + * @param power An int power. + * @return 2^power as a long, or the closest power of two in long range + * in case of overflow, i.e. if power is not in [0,62]. + */ + public static long twoPowAsLongBounded(int power) { + power = toRange(0, 62, power); + return 1L << power; + } + + /** + * @param a A value. + * @return a*a. + */ + public static int pow2(int a) { + return a*a; + } + + /** + * @param a A value. + * @return a*a. + */ + public static long pow2(long a) { + return a*a; + } + + /** + * @param a A value. + * @return a*a. + */ + public static float pow2(float a) { + return a*a; + } + + /** + * Strict version. + * + * @param a A value. + * @return a*a. + */ + public static strictfp float pow2_strict(float a) { + return a*a; + } + + /** + * @param a A value. + * @return a*a. + */ + public static double pow2(double a) { + return a*a; + } + + /** + * Strict version. + * + * @param a A value. + * @return a*a. + */ + public static strictfp double pow2_strict(double a) { + return a*a; + } + + /** + * @param a A value. + * @return a*a*a. + */ + public static int pow3(int a) { + return a*a*a; + } + + /** + * @param a A value. + * @return a*a*a. + */ + public static long pow3(long a) { + return a*a*a; + } + + /** + * @param a A value. + * @return a*a*a. + */ + public static float pow3(float a) { + return a*a*a; + } + + /** + * Strict version. + * + * @param a A value. + * @return a*a*a. + */ + public static strictfp float pow3_strict(float a) { + return a*a*a; + } + + /** + * @param a A value. + * @return a*a*a. + */ + public static double pow3(double a) { + return a*a*a; + } + + /** + * Strict version. + * + * @param a A value. + * @return a*a*a. + */ + public static strictfp double pow3_strict(double a) { + return a*a*a; + } + + /* + * Accurate +-m*PI/n. + */ + + /** + * @param angRad An angle, in radians. + * @return angRad + 2*PI, accurately computed. + */ + public static double plus2PI(double angRad) { + if (angRad > -Math.PI) { + // LO then HI, for better accuracy (if starting near 0). + return (angRad + TWOPI_LO) + TWOPI_HI; + } else { + // HI then LO, for better accuracy (if ending near 0). + return (angRad + TWOPI_HI) + TWOPI_LO; + } + } + + /** + * Strict version. + * + * @param angRad An angle, in radians. + * @return angRad + 2*PI, accurately computed. + */ + public static strictfp double plus2PI_strict(double angRad) { + if (angRad > -Math.PI) { + // LO then HI, for better accuracy (if starting near 0). + return (angRad + TWOPI_LO) + TWOPI_HI; + } else { + // HI then LO, for better accuracy (if ending near 0). + return (angRad + TWOPI_HI) + TWOPI_LO; + } + } + + /** + * @param angRad An angle, in radians. + * @return angRad - 2*PI, accurately computed. + */ + public static double minus2PI(double angRad) { + if (angRad < Math.PI) { + // LO then HI, for better accuracy (if starting near 0). + return (angRad - TWOPI_LO) - TWOPI_HI; + } else { + // HI then LO, for better accuracy (if ending near 0). + return (angRad - TWOPI_HI) - TWOPI_LO; + } + } + + /** + * Strict version. + * + * @param angRad An angle, in radians. + * @return angRad - 2*PI, accurately computed. + */ + public static strictfp double minus2PI_strict(double angRad) { + if (angRad < Math.PI) { + // LO then HI, for better accuracy (if starting near 0). + return (angRad - TWOPI_LO) - TWOPI_HI; + } else { + // HI then LO, for better accuracy (if ending near 0). + return (angRad - TWOPI_HI) - TWOPI_LO; + } + } + + /** + * @param angRad An angle, in radians. + * @return angRad + PI, accurately computed. + */ + public static double plusPI(double angRad) { + if (angRad > -Math.PI/2) { + // LO then HI, for better accuracy (if starting near 0). + return (angRad + PI_LO) + PI_HI; + } else { + // HI then LO, for better accuracy (if ending near 0). + return (angRad + PI_HI) + PI_LO; + } + } + + /** + * Strict version. + * + * @param angRad An angle, in radians. + * @return angRad + PI, accurately computed. + */ + public static strictfp double plusPI_strict(double angRad) { + if (angRad > -Math.PI/2) { + // LO then HI, for better accuracy (if starting near 0). + return (angRad + PI_LO) + PI_HI; + } else { + // HI then LO, for better accuracy (if ending near 0). + return (angRad + PI_HI) + PI_LO; + } + } + + /** + * @param angRad An angle, in radians. + * @return angRad - PI, accurately computed. + */ + public static double minusPI(double angRad) { + if (angRad < Math.PI/2) { + // LO then HI, for better accuracy (if starting near 0). + return (angRad - PI_LO) - PI_HI; + } else { + // HI then LO, for better accuracy (if ending near 0). + return (angRad - PI_HI) - PI_LO; + } + } + + /** + * Strict version. + * + * @param angRad An angle, in radians. + * @return angRad - PI, accurately computed. + */ + public static strictfp double minusPI_strict(double angRad) { + if (angRad < Math.PI/2) { + // LO then HI, for better accuracy (if starting near 0). + return (angRad - PI_LO) - PI_HI; + } else { + // HI then LO, for better accuracy (if ending near 0). + return (angRad - PI_HI) - PI_LO; + } + } + + /** + * @param angRad An angle, in radians. + * @return angRad + PI/2, accurately computed. + */ + public static double plusPIO2(double angRad) { + if (angRad > -Math.PI/4) { + // LO then HI, for better accuracy (if starting near 0). + return (angRad + PIO2_LO) + PIO2_HI; + } else { + // HI then LO, for better accuracy (if ending near 0). + return (angRad + PIO2_HI) + PIO2_LO; + } + } + + /** + * Strict version. + * + * @param angRad An angle, in radians. + * @return angRad + PI/2, accurately computed. + */ + public static strictfp double plusPIO2_strict(double angRad) { + if (angRad > -Math.PI/4) { + // LO then HI, for better accuracy (if starting near 0). + return (angRad + PIO2_LO) + PIO2_HI; + } else { + // HI then LO, for better accuracy (if ending near 0). + return (angRad + PIO2_HI) + PIO2_LO; + } + } + + /** + * @param angRad An angle, in radians. + * @return angRad - PI/2, accurately computed. + */ + public static double minusPIO2(double angRad) { + if (angRad < Math.PI/4) { + // LO then HI, for better accuracy (if starting near 0). + return (angRad - PIO2_LO) - PIO2_HI; + } else { + // HI then LO, for better accuracy (if ending near 0). + return (angRad - PIO2_HI) - PIO2_LO; + } + } + + /** + * Strict version. + * + * @param angRad An angle, in radians. + * @return angRad - PI/2, accurately computed. + */ + public static strictfp double minusPIO2_strict(double angRad) { + if (angRad < Math.PI/4) { + // LO then HI, for better accuracy (if starting near 0). + return (angRad - PIO2_LO) - PIO2_HI; + } else { + // HI then LO, for better accuracy (if ending near 0). + return (angRad - PIO2_HI) - PIO2_LO; + } + } + + /* + * toString (radix) + */ + + /** + * @param radix Radix to be checked. + * @return True if does not throw. + * @throws IllegalArgumentException if the specified radix is not in [2,36]. + */ + public static boolean checkRadix(int radix) { + if (!isInRange(Character.MIN_RADIX, Character.MAX_RADIX, radix)) { + throw new IllegalArgumentException("radix ["+radix+"] must be in ["+Character.MIN_RADIX+","+Character.MAX_RADIX+"]"); + } + return true; + } + + /** + * @param radix A radix in [2,36]. + * @return Number of characters (minus sign included) + * to represent the specified value in the specified radix. + */ + public static int computeNbrOfChars(int value, int radix) { + if (value < 0) { + // 1 for sign + return 1 + computeNbrOfDigits_negValue(value, radix); + } else { + return computeNbrOfDigits_negValue(-value, radix); + } + } + + /** + * @param radix A radix in [2,36]. + * @return Number of characters (minus sign included) + * to represent the specified value in the specified radix. + */ + public static int computeNbrOfChars(long value, int radix) { + if (value < 0) { + // 1 for sign + return 1 + computeNbrOfDigits_negValue(value, radix); + } else { + return computeNbrOfDigits_negValue(-value, radix); + } + } + + /** + * @param radix A radix in [2,36]. + * @param paddingUpTo Number of digits (sign excluded) up to which left-padding with zeros is done. + * @return Number of characters (minus sign included) + * to represent the specified value in the specified radix. + */ + public static int computeNbrOfChars(int value, int radix, int paddingUpTo) { + if (value < 0) { + // 1 for sign + return 1 + Math.max(paddingUpTo, computeNbrOfDigits_negValue(value, radix)); + } else { + return Math.max(paddingUpTo, computeNbrOfDigits_negValue(-value, radix)); + } + } + + /** + * @param radix A radix in [2,36]. + * @param paddingUpTo Number of digits (sign excluded) up to which left-padding with zeros is done. + * @return Number of characters (minus sign included) + * to represent the specified value in the specified radix. + */ + public static int computeNbrOfChars(long value, int radix, int paddingUpTo) { + if (value < 0) { + // 1 for sign + return 1 + Math.max(paddingUpTo, computeNbrOfDigits_negValue(value, radix)); + } else { + return Math.max(paddingUpTo, computeNbrOfDigits_negValue(-value, radix)); + } + } + + /** + * @param radix A radix in [2,36]. + * @return Number of digits of the specified value in the specified radix. + */ + public static int computeNbrOfDigits(int value, int radix) { + return computeNbrOfDigits_negValue(-abs(value), radix); + } + + /** + * @param radix A radix in [2,36]. + * @return Number of digits of the specified value in the specified radix. + */ + public static int computeNbrOfDigits(long value, int radix) { + return computeNbrOfDigits_negValue(-abs(value), radix); + } + + /** + * @param radix A radix in [2,36]. + * @param paddingUpTo Number of digits (sign excluded) up to which left-padding with zeros is done. + * @return Number of digits of the specified value in the specified radix, + * including the specified padding. + */ + public static int computeNbrOfDigits(int value, int radix, int paddingUpTo) { + return Math.max(paddingUpTo,computeNbrOfDigits(value, radix)); + } + + /** + * @param radix A radix in [2,36]. + * @param paddingUpTo Number of digits (sign excluded) up to which left-padding with zeros is done. + * @return Number of digits of the specified value in the specified radix, + * including the specified padding. + */ + public static int computeNbrOfDigits(long value, int radix, int paddingUpTo) { + return Math.max(paddingUpTo,computeNbrOfDigits(value, radix)); + } + + /** + * This method just delegates to Integer.toString(int), + * but is defined here to complete the API. + * + * @return String representation of the specified value in base 10. + */ + public static String toString(int value) { + return Integer.toString(value); + } + + /** + * This method just delegates to Long.toString(long), + * but is defined here to complete the API. + * + * @return String representation of the specified value in base 10. + */ + public static String toString(long value) { + return Long.toString(value); + } + + /** + * @param radix A radix in [2,36]. + * @return String representation of the specified value in the specified radix. + * @throws IllegalArgumentException if the specified radix is out of range. + */ + public static String toString(int value, int radix) { + return toString(value, radix, 0); + } + + /** + * @param radix A radix in [2,36]. + * @return String representation of the specified value in the specified radix. + * @throws IllegalArgumentException if the specified radix is out of range. + */ + public static String toString(long value, int radix) { + return toString(value, radix, 0); + } + + /** + * @param radix A radix in [2,36]. + * @param paddingUpTo Number of digits (sign excluded) up to which left-padding with zeros is done. + * @return String representation of the specified value in the specified radix. + * @throws IllegalArgumentException if the specified radix is out of range. + */ + public static String toString(int value, int radix, int paddingUpTo) { + // Only one test if radix+paddingUpTo != 10. + if ((radix+paddingUpTo == 10) && (paddingUpTo == 0)) { + // Using JDK's optimized algorithm. + return Integer.toString(value); + } + + int negValue; + final int signSize; + final boolean negative = (value < 0); + if (negative) { + negValue = value; + signSize = 1; + } else { + negValue = -value; + signSize = 0; + } + // Faster if we just use max possible number of characters (33), + // but we prefer to take care of garbage's memory footprint. + // Checks radix. + final int nbrOfChars = signSize + Math.max(paddingUpTo, computeNbrOfDigits_negValue(negValue, radix)); + + final char[] chars = new char[nbrOfChars]; + + int charPos = nbrOfChars; + + final boolean radixIsPowerOfTwo = ((radix & (radix-1)) == 0); + // Not allowing Integer.MIN_VALUE so it can be negated. + if (radixIsPowerOfTwo && (negValue != Integer.MIN_VALUE)) { + final int mask = radix-1; + final int divShift = DIV_SHIFT_BY_RADIX[radix]; + while (negValue <= -radix) { + chars[--charPos] = CHAR_BY_DIGIT[(int)((-negValue) & mask)]; + negValue = -((-negValue) >> divShift); + } + } else { + while (negValue <= -radix) { + chars[--charPos] = CHAR_BY_DIGIT[(int)(-(negValue % radix))]; + negValue /= radix; + } + } + chars[--charPos] = CHAR_BY_DIGIT[(int)(-negValue)]; + + while (charPos > signSize) { + chars[--charPos] = '0'; + } + + if (negative) { + chars[0] = '-'; + } + + return new String(chars); + } + + /** + * @param radix A radix in [2,36]. + * @param paddingUpTo Number of digits (sign excluded) up to which left-padding with zeros is done. + * @return String representation of the specified value in the specified radix. + * @throws IllegalArgumentException if the specified radix is out of range. + */ + public static String toString(long value, int radix, int paddingUpTo) { + // Only one test if radix+paddingUpTo != 10. + if ((radix+paddingUpTo == 10) && (paddingUpTo == 0)) { + // Using JDK's optimized algorithm. + return Long.toString(value); + } + + long negValue; + final int signSize; + final boolean negative = (value < 0); + if (negative) { + negValue = value; + signSize = 1; + } else { + negValue = -value; + signSize = 0; + } + // Checks radix. + final int nbrOfChars = signSize + Math.max(paddingUpTo, computeNbrOfDigits_negValue(negValue, radix)); + + final char[] chars = new char[nbrOfChars]; + + int charPos = nbrOfChars; + + final boolean radixIsPowerOfTwo = ((radix & (radix-1)) == 0); + // Not allowing Long.MIN_VALUE so it can be negated. + if (radixIsPowerOfTwo && (negValue != Long.MIN_VALUE)) { + final int mask = radix-1; + final int divShift = DIV_SHIFT_BY_RADIX[radix]; + while (negValue <= -radix) { + chars[--charPos] = CHAR_BY_DIGIT[(int)((-negValue) & mask)]; + negValue = -((-negValue) >> divShift); + } + } else { + while (negValue <= -radix) { + chars[--charPos] = CHAR_BY_DIGIT[(int)(-(negValue % radix))]; + negValue /= radix; + } + } + chars[--charPos] = CHAR_BY_DIGIT[(int)(-negValue)]; + + while (charPos > signSize) { + chars[--charPos] = '0'; + } + + if (negative) { + chars[0] = '-'; + } + + return new String(chars); + } + + /* + * toString (bits) + */ + + /** + * @param firstBitPos First bit position (inclusive). + * @param lastBitPosExcl Last bit position (exclusive). + * @return True if does not throw. + * @throws IllegalArgumentException if the specified bit range does not fit in a byte. + */ + public static boolean checkBitPositionsByte(int firstBitPos, int lastBitPosExcl) { + return checkBitPositions(firstBitPos, lastBitPosExcl, 8); + } + + /** + * @param firstBitPos First bit position (inclusive). + * @param lastBitPosExcl Last bit position (exclusive). + * @return True if does not throw. + * @throws IllegalArgumentException if the specified bit range does not fit in a short. + */ + public static boolean checkBitPositionsShort(int firstBitPos, int lastBitPosExcl) { + return checkBitPositions(firstBitPos, lastBitPosExcl, 16); + } + + /** + * @param firstBitPos First bit position (inclusive). + * @param lastBitPosExcl Last bit position (exclusive). + * @return True if does not throw. + * @throws IllegalArgumentException if the specified bit range does not fit in an int. + */ + public static boolean checkBitPositionsInt(int firstBitPos, int lastBitPosExcl) { + return checkBitPositions(firstBitPos, lastBitPosExcl, 32); + } + + /** + * @param firstBitPos First bit position (inclusive). + * @param lastBitPosExcl Last bit position (exclusive). + * @return True if does not throw. + * @throws IllegalArgumentException if the specified bit range does not fit in a long. + */ + public static boolean checkBitPositionsLong(int firstBitPos, int lastBitPosExcl) { + return checkBitPositions(firstBitPos, lastBitPosExcl, 64); + } + + /** + * @return String representation of specified bits, in big endian. + */ + public static String toStringBits(byte bits) { + final char[] chars = new char[8]; + int bitIndex = 8; + while (--bitIndex >= 0) { + chars[7-bitIndex] = (char)('0'+((bits>>bitIndex)&1)); + } + return new String(chars); + } + + /** + * @return String representation of specified bits, in big endian. + */ + public static String toStringBits(short bits) { + final char[] chars = new char[16]; + int bitIndex = 16; + while (--bitIndex >= 0) { + chars[15-bitIndex] = (char)('0'+((bits>>bitIndex)&1)); + } + return new String(chars); + } + + /** + * @return String representation of specified bits, in big endian. + */ + public static String toStringBits(int bits) { + final char[] chars = new char[32]; + int bitIndex = 32; + while (--bitIndex >= 0) { + chars[31-bitIndex] = (char)('0'+((bits>>bitIndex)&1)); + } + return new String(chars); + } + + /** + * @return String representation of specified bits, in big endian. + */ + public static String toStringBits(long bits) { + final char[] chars = new char[64]; + int bitIndex = 64; + while (--bitIndex >= 0) { + chars[63-bitIndex] = (char)('0'+((bits>>bitIndex)&1)); + } + return new String(chars); + } + + /** + * @param firstBitPos First bit position (inclusive). + * @param lastBitPosExcl Last bit position (exclusive). + * @param bigEndian True for bits to be added in big endian order (MSBit to LSBit) + * false for little endian order. + * @param padding True if underscores must be added instead of out-of-range bits, + * false to just add characters corresponding to in-range bits. + * @return String representation of specified bits. + */ + public static String toStringBits( + byte bits, + int firstBitPos, + int lastBitPosExcl, + boolean bigEndian, + boolean padding) { + checkBitPositionsByte(firstBitPos, lastBitPosExcl); + return toStringBits_0_32_bitPosAlreadyChecked(8,bits, firstBitPos, lastBitPosExcl, bigEndian, padding); + } + + /** + * @param firstBitPos First bit position (inclusive). + * @param lastBitPosExcl Last bit position (exclusive). + * @param bigEndian True for bits to be added in big endian order (MSBit to LSBit) + * false for little endian order. + * @param padding True if underscores must be added instead of out-of-range bits, + * false to just add characters corresponding to in-range bits. + * @return String representation of specified bits. + */ + public static String toStringBits( + short bits, + int firstBitPos, + int lastBitPosExcl, + boolean bigEndian, + boolean padding) { + checkBitPositionsShort(firstBitPos, lastBitPosExcl); + return toStringBits_0_32_bitPosAlreadyChecked(16,bits, firstBitPos, lastBitPosExcl, bigEndian, padding); + } + + /** + * @param firstBitPos First bit position (inclusive). + * @param lastBitPosExcl Last bit position (exclusive). + * @param bigEndian True for bits to be added in big endian order (MSBit to LSBit) + * false for little endian order. + * @param padding True if underscores must be added instead of out-of-range bits, + * false to just add characters corresponding to in-range bits. + * @return String representation of specified bits. + */ + public static String toStringBits( + int bits, + int firstBitPos, + int lastBitPosExcl, + boolean bigEndian, + boolean padding) { + checkBitPositionsInt(firstBitPos, lastBitPosExcl); + return toStringBits_0_32_bitPosAlreadyChecked(32,bits, firstBitPos, lastBitPosExcl, bigEndian, padding); + } + + /** + * @param firstBitPos First bit position (inclusive). + * @param lastBitPosExcl Last bit position (exclusive). + * @param bigEndian True for bits to be added in big endian order (MSBit to LSBit) + * false for little endian order. + * @param padding True if underscores must be added instead of out-of-range bits, + * false to just add characters corresponding to in-range bits. + * @return String representation of specified bits. + */ + public static String toStringBits( + long bits, + int firstBitPos, + int lastBitPosExcl, + boolean bigEndian, + boolean padding) { + checkBitPositionsLong(firstBitPos, lastBitPosExcl); + final int bitSize = 64; + final int bitSizeM1 = bitSize-1; + final int lastBitPos = lastBitPosExcl-1; + if (padding) { + final int nbrOfChars = bitSize; + final char[] chars = new char[nbrOfChars]; + int bitIndex = bitSizeM1; + if (bigEndian) { + final int firstBitIndex = bitSizeM1-lastBitPos; + final int lastBitIndex = bitSizeM1-firstBitPos; + while (bitIndex > lastBitIndex) { + chars[bitSizeM1-bitIndex] = '_'; + --bitIndex; + } + while (bitIndex >= firstBitIndex) { + chars[bitSizeM1-bitIndex] = (char)('0'+((bits>>bitIndex)&1)); + --bitIndex; + } + while (bitIndex >= 0) { + chars[bitSizeM1-bitIndex] = '_'; + --bitIndex; + } + } else { + while (bitIndex > lastBitPos) { + chars[bitIndex] = '_'; + --bitIndex; + } + while (bitIndex >= firstBitPos) { + chars[bitIndex] = (char)('0'+((bits>>bitIndex)&1)); + --bitIndex; + } + while (bitIndex >= 0) { + chars[bitIndex] = '_'; + --bitIndex; + } + } + return new String(chars); + } else { + final int nbrOfChars = (lastBitPosExcl - firstBitPos); + final char[] chars = new char[nbrOfChars]; + if (bigEndian) { + final int firstBitIndex = bitSizeM1-lastBitPos; + final int lastBitIndex = bitSizeM1-firstBitPos; + int bitIndex = lastBitIndex; + while (bitIndex >= firstBitIndex) { + chars[lastBitIndex-bitIndex] = (char)('0'+((bits>>bitIndex)&1)); + --bitIndex; + } + } else { + int bitIndex = lastBitPos; + while (bitIndex >= firstBitPos) { + chars[bitIndex-firstBitPos] = (char)('0'+((bits>>bitIndex)&1)); + --bitIndex; + } + } + return new String(chars); + } + } + + /* + * toString (floating points) + * + * toStringCSN(double) and toStringNoCSN(double) + * could be made faster, by using directly internals + * of Double.toString(double), but this would require + * copy-paste of much tricky code from JDK, and + * the overhead of our little rework is relatively + * negligible. + */ + + /** + * @param value A double value. + * @return String representing the specified value, + * using "computerized scientific notation", + * which Double.toString(double) uses for non-infinite + * values, when |value| < 1e-3 or |value| >= 1e7. + */ + public static String toStringCSN(double value) { + // Quick case (also to get rid of +-0.0, + // for which Double.toString(double) doesn't use CSN). + if (value == 0.0) { + if (Double.doubleToRawLongBits(value) < 0) { + return "-0.0E0"; + } else { + return "0.0E0"; + } + } + + final double abs = Math.abs(value); + if ((abs >= NO_CSN_MIN_BOUND_INCL) && (abs < NO_CSN_MAX_BOUND_EXCL)) { + final boolean neg = (value < 0.0); + + final String rawAbs = Double.toString(abs); + if (abs >= 1.0) { + /* + * 0123456 + * 12.3456 ===> 1.23456E1 + * 123.0 ===> 1.23E2 + */ + final int dotIndex = rawAbs.indexOf((int)'.'); + final int powerOfTen = dotIndex-1; + final StringBuilder sb = new StringBuilder(); + if (neg) { + sb.append('-'); + } + // Adding unit-or-above digits, with dot after first one. + sb.append(rawAbs.charAt(0)); + sb.append('.'); + sb.append(rawAbs,1,dotIndex); + if ((value != (int)value) || (abs < 10.0)) { + // Adding below-unit digits (possibly just 0 if abs < 10.0, + // to end up for example with "3.0E0" instead of "3.E0"). + sb.append(rawAbs,dotIndex+1,rawAbs.length()); + } + sb.append('E'); + sb.append(CHAR_BY_DIGIT[powerOfTen]); + return sb.toString(); + } else { + /* + * 012345678 + * 0.0123456 ===> 1.23456E-2 + * 0.01 ===> 1.0E-2 + */ + int nonZeroIndex = 1; + while (rawAbs.charAt(++nonZeroIndex) == '0') { + } + // Negative. + final int powerOfTen = 1-nonZeroIndex; + final int nbrOfSignificantDigitsPastDot = (rawAbs.length() - (nonZeroIndex+1)); + final StringBuilder sb = new StringBuilder(); + if (neg) { + sb.append('-'); + } + sb.append(rawAbs.charAt(nonZeroIndex)); + sb.append('.'); + if (nbrOfSignificantDigitsPastDot > 0) { + // If bug 4428022 make rawAbs being something like "0.0010", + // we add the last '0' here after the dot, which is fine. + sb.append(rawAbs,nonZeroIndex+1,rawAbs.length()); + } else { + sb.append('0'); + } + sb.append("E-"); + sb.append(CHAR_BY_DIGIT[-powerOfTen]); + return sb.toString(); + } + } else { + return Double.toString(value); + } + } + + /** + * @param value A double value. + * @return String representing the specified value, + * not in "computerized scientific notation", + * which Double.toString(double) uses for non-infinite + * values, when |value| < 1e-3 or |value| >= 1e7. + */ + public static String toStringNoCSN(double value) { + // Quick case. + // Should also work with long instead of int, + // but less obvious (due to roundings...), + // and we just want to speed up the more common + // case of "small" integer values. + final int intValue = (int)value; + if (value == intValue) { + if (value == 0.0) { + if (Double.doubleToRawLongBits(value) < 0) { + return "-0.0"; + } else { + return "0.0"; + } + } else { + return Integer.toString(intValue)+".0"; + } + } + + final String raw = Double.toString(value); + final double abs = Math.abs(value); + if (abs >= NO_CSN_MAX_BOUND_EXCL) { + if (abs == Double.POSITIVE_INFINITY) { + return raw; + } + /* + * 0123456789 + * 1.234567E5 ===> 123456.7 + * 1.23456E5 ===> 123456.0 (adding 0) + * 1.23E5 ===> 123000.0 + * 1.0E5 ===> 100000.0 + */ + // "." close to start, so using indexOf. + final int dotIndex = raw.indexOf((int)'.'); + // "E" close to end, so using lastIndexOf. + final int eIndex = raw.lastIndexOf((int)'E'); + final int powerOfTen = Integer.parseInt(raw.substring(eIndex+1)); + final int nbrOfSignificantLoDigits = (eIndex - dotIndex - 1); + final int nbrOfZerosToAddBeforeDot = (powerOfTen - nbrOfSignificantLoDigits); + + int start; + int end; + + final StringBuilder sb = new StringBuilder(); + sb.append(raw,0,dotIndex); + if (nbrOfZerosToAddBeforeDot >= 0) { + // Can copy all digits that were between '.' and 'E'. + sb.append(raw,dotIndex+1,eIndex); + for (int i=0;i 0.0001234 + * 1.0E-4 ===> 0.0001 + */ + // "." close to start, so using indexOf. + final int dotIndex = raw.indexOf((int)'.'); + // "E" close to end, so using lastIndexOf. + final int eIndex = raw.lastIndexOf((int)'E'); + // Negative. + final int powerOfTen = Integer.parseInt(raw.substring(eIndex+1)); + final int nbrOfZerosToAddAfterDot = (-powerOfTen-1); + + final StringBuilder sb = new StringBuilder(); + if (value < 0.0) { + sb.append("-0."); + } else { + sb.append("0."); + } + for (int i=0;i>(32-bitSize)); + } + + private static long minSignedLongForBitSize_noCheck(int bitSize) { + // i.e. (-1L<<(bitSize-1)) + return (Long.MIN_VALUE>>(64-bitSize)); + } + + private static int maxSignedIntForBitSize_noCheck(int bitSize) { + // i.e. (1<<(bitSize-1))-1 + return (Integer.MAX_VALUE>>(32-bitSize)); + } + + private static long maxSignedLongForBitSize_noCheck(int bitSize) { + // i.e. (1L<<(bitSize-1))-1 + return (Long.MAX_VALUE>>(64-bitSize)); + } + + /* + * + */ + + /** + * @throws IllegalArgumentException if the specified radix is out of range. + */ + private static int computeNbrOfDigits_negValue(int negValue, int radix) { + checkRadix(radix); + final int maxNbrOfDigits = MAX_NBR_OF_NEG_INT_DIGITS_BY_RADIX[radix]; + int p = radix; + for (int i=1;i -p) { + return i; + } + p *= radix; + } + return maxNbrOfDigits; + } + + /** + * @throws IllegalArgumentException if the specified radix is out of range. + */ + private static int computeNbrOfDigits_negValue(long negValue, int radix) { + checkRadix(radix); + final int maxNbrOfDigits = MAX_NBR_OF_NEG_LONG_DIGITS_BY_RADIX[radix]; + long p = radix; + for (int i=1;i -p) { + return i; + } + p *= radix; + } + return maxNbrOfDigits; + } + + /* + * + */ + + private static boolean checkBitPositions(int firstBitPos, int lastBitPosExcl, int bitSize) { + if ((firstBitPos < 0) || (firstBitPos > lastBitPosExcl) || (lastBitPosExcl > bitSize)) { + throw new IllegalArgumentException( + "bit positions (first="+firstBitPos+",lastExcl="+lastBitPosExcl + +") must verify 0 <= first <= lastExcl <= "+bitSize); + } + return true; + } + + /** + * Common method for byte, short and int. + * Could be a bit faster to have specific methods for byte and short, + * but not much, and that would also make more messy (byte-)code. + * + * @param bitSize Must be in [0,32]. + */ + private static String toStringBits_0_32_bitPosAlreadyChecked( + int bitSize, + int bits, + int firstBitPos, + int lastBitPosExcl, + boolean bigEndian, + boolean padding) { + final int bitSizeM1 = bitSize-1; + final int lastBitPos = lastBitPosExcl-1; + if (padding) { + final int nbrOfChars = bitSize; + final char[] chars = new char[nbrOfChars]; + int bitIndex = bitSizeM1; + if (bigEndian) { + final int firstBitIndex = bitSizeM1-lastBitPos; + final int lastBitIndex = bitSizeM1-firstBitPos; + while (bitIndex > lastBitIndex) { + chars[bitSizeM1-bitIndex] = '_'; + --bitIndex; + } + while (bitIndex >= firstBitIndex) { + chars[bitSizeM1-bitIndex] = (char)('0'+((bits>>bitIndex)&1)); + --bitIndex; + } + while (bitIndex >= 0) { + chars[bitSizeM1-bitIndex] = '_'; + --bitIndex; + } + } else { + while (bitIndex > lastBitPos) { + chars[bitIndex] = '_'; + --bitIndex; + } + while (bitIndex >= firstBitPos) { + chars[bitIndex] = (char)('0'+((bits>>bitIndex)&1)); + --bitIndex; + } + while (bitIndex >= 0) { + chars[bitIndex] = '_'; + --bitIndex; + } + } + return new String(chars); + } else { + final int nbrOfChars = (lastBitPosExcl - firstBitPos); + final char[] chars = new char[nbrOfChars]; + if (bigEndian) { + final int firstBitIndex = bitSizeM1-lastBitPos; + final int lastBitIndex = bitSizeM1-firstBitPos; + int bitIndex = lastBitIndex; + while (bitIndex >= firstBitIndex) { + chars[lastBitIndex-bitIndex] = (char)('0'+((bits>>bitIndex)&1)); + --bitIndex; + } + } else { + int bitIndex = lastBitPos; + while (bitIndex >= firstBitPos) { + chars[bitIndex-firstBitPos] = (char)('0'+((bits>>bitIndex)&1)); + --bitIndex; + } + } + return new String(chars); + } + } +} diff --git a/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/StrictFastMath.java b/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/StrictFastMath.java new file mode 100644 index 000000000..a61ac9772 --- /dev/null +++ b/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/StrictFastMath.java @@ -0,0 +1,2998 @@ +/* + * Copyright 2018-2021 KMath contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package space.kscience.kmath.jafama; + +/** + * Strict versions of FastMath methods. + * Cf. README.txt for more info. + */ +public final strictfp class StrictFastMath extends CmnFastMath { + + /* + * We use strictfp for the whole class: + * - for simplicity, + * - to reduce strictfp/non-strictfp switching, which can add overhead, + * when these treatments are used from within strictfp code, + * - to make sure that we only use and return non-extended values, + * else if strictfp gets added later to some treatments they might then + * behave differently due to no longer being inlinable into FP-wide + * expressions, + * - to make sure we don't mistakenly not use it. + */ + + //-------------------------------------------------------------------------- + // CONFIGURATION + //-------------------------------------------------------------------------- + + private static final boolean USE_JDK_MATH = SFM_USE_JDK_MATH; + + private static final boolean USE_REDEFINED_LOG = SFM_USE_REDEFINED_LOG; + + private static final boolean USE_REDEFINED_SQRT = SFM_USE_REDEFINED_SQRT; + + private static final boolean USE_POWTABS_FOR_ASIN = SFM_USE_POWTABS_FOR_ASIN; + + //-------------------------------------------------------------------------- + // PUBLIC METHODS + //-------------------------------------------------------------------------- + + /* + * trigonometry + */ + + /** + * @param angle Angle in radians. + * @return Angle sine. + */ + public static double sin(double angle) { + if (USE_JDK_MATH) { + return StrictMath.sin(angle); + } + boolean negateResult = false; + if (angle < 0.0) { + angle = -angle; + negateResult = true; + } + if (angle > SIN_COS_MAX_VALUE_FOR_INT_MODULO) { + if (false) { + // Can give very bad relative error near PI (mod 2*PI). + angle = remainderTwoPi(angle); + if (angle < 0.0) { + angle = -angle; + negateResult = !negateResult; + } + } else { + final long remAndQuad = remainderPiO2(angle); + angle = decodeRemainder(remAndQuad); + final double sin; + final int q = decodeQuadrant(remAndQuad); + if (q == 0) { + sin = sin(angle); + } else if (q == 1) { + sin = cos(angle); + } else if (q == 2) { + sin = -sin(angle); + } else { + sin = -cos(angle); + } + return (negateResult ? -sin : sin); + } + } + // 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 indexSin = MyTSinCos.sinTab[index]; + double indexCos = MyTSinCos.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; + } + + /** + * Quick sin, with accuracy of about 1.6e-3 (PI/) + * for |angle| < 6588395.0 (Integer.MAX_VALUE * (2*PI/) - 2) + * (- 2 due to removing PI/2 before using cosine tab), + * and no accuracy at all for larger values. + * + * @param angle Angle in radians. + * @return Angle sine. + */ + public static double sinQuick(double angle) { + if (USE_JDK_MATH) { + return StrictMath.sin(angle); + } + return MyTSinCos.cosTab[((int)(Math.abs(angle-Math.PI/2) * SIN_COS_INDEXER + 0.5)) & (SIN_COS_TABS_SIZE-2)]; + } + + /** + * @param angle Angle in radians. + * @return Angle cosine. + */ + public static double cos(double angle) { + if (USE_JDK_MATH) { + return StrictMath.cos(angle); + } + angle = Math.abs(angle); + if (angle > SIN_COS_MAX_VALUE_FOR_INT_MODULO) { + if (false) { + // Can give very bad relative error near PI (mod 2*PI). + angle = remainderTwoPi(angle); + if (angle < 0.0) { + angle = -angle; + } + } else { + final long remAndQuad = remainderPiO2(angle); + angle = decodeRemainder(remAndQuad); + final double cos; + final int q = decodeQuadrant(remAndQuad); + if (q == 0) { + cos = cos(angle); + } else if (q == 1) { + cos = -sin(angle); + } else if (q == 2) { + cos = -cos(angle); + } else { + cos = sin(angle); + } + return cos; + } + } + // 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 = MyTSinCos.cosTab[index]; + double indexSin = MyTSinCos.sinTab[index]; + return indexCos + delta * (-indexSin + delta * (-indexCos * ONE_DIV_F2 + delta * (indexSin * ONE_DIV_F3 + delta * indexCos * ONE_DIV_F4))); + } + + /** + * Quick cos, with accuracy of about 1.6e-3 (PI/) + * for |angle| < 6588397.0 (Integer.MAX_VALUE * (2*PI/)), + * and no accuracy at all for larger values. + * + * @param angle Angle in radians. + * @return Angle cosine. + */ + public static double cosQuick(double angle) { + if (USE_JDK_MATH) { + return StrictMath.cos(angle); + } + return MyTSinCos.cosTab[((int)(Math.abs(angle) * SIN_COS_INDEXER + 0.5)) & (SIN_COS_TABS_SIZE-2)]; + } + + /** + * Computes sine and cosine together. + * + * @param angle Angle in radians. + * @param cosine (out) Angle cosine. + * @return Angle sine. + */ + public static double sinAndCos(double angle, DoubleWrapper cosine) { + if (USE_JDK_MATH) { + cosine.value = StrictMath.cos(angle); + return StrictMath.sin(angle); + } + // Using the same algorithm than sin(double) method, + // and computing also cosine at the end. + boolean negateResult = false; + if (angle < 0.0) { + angle = -angle; + negateResult = true; + } + if (angle > SIN_COS_MAX_VALUE_FOR_INT_MODULO) { + if (false) { + // Can give very bad relative error near PI (mod 2*PI). + angle = remainderTwoPi(angle); + if (angle < 0.0) { + angle = -angle; + negateResult = !negateResult; + } + } else { + final long remAndQuad = remainderPiO2(angle); + angle = decodeRemainder(remAndQuad); + final double sin; + final int q = decodeQuadrant(remAndQuad); + if (q == 0) { + sin = sin(angle); + cosine.value = cos(angle); + } else if (q == 1) { + sin = cos(angle); + cosine.value = -sin(angle); + } else if (q == 2) { + sin = -sin(angle); + cosine.value = -cos(angle); + } else { + sin = -cos(angle); + cosine.value = sin(angle); + } + return (negateResult ? -sin : sin); + } + } + 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 = MyTSinCos.sinTab[index]; + double indexCos = MyTSinCos.cosTab[index]; + // Could factor some multiplications (delta * factorials), but then is less accurate. + cosine.value = indexCos + delta * (-indexSin + delta * (-indexCos * ONE_DIV_F2 + delta * (indexSin * ONE_DIV_F3 + delta * indexCos * ONE_DIV_F4))); + double result = indexSin + delta * (indexCos + delta * (-indexSin * ONE_DIV_F2 + delta * (-indexCos * ONE_DIV_F3 + delta * indexSin * ONE_DIV_F4))); + return negateResult ? -result : result; + } + + /** + * Can have very bad relative error near +-PI/2, + * but of the same magnitude than the relative delta between + * StrictMath.tan(PI/2) and StrictMath.tan(nextDown(PI/2)). + * + * @param angle Angle in radians. + * @return Angle tangent. + */ + public static double tan(double angle) { + if (USE_JDK_MATH) { + return StrictMath.tan(angle); + } + boolean negateResult = false; + if (angle < 0.0) { + angle = -angle; + negateResult = true; + } + if (angle > TAN_MAX_VALUE_FOR_INT_MODULO) { + angle = remainderPi(angle); + if (angle < 0.0) { + angle = -angle; + negateResult = !negateResult; + } + } + // index: possibly outside tables range. + int index = (int)(angle * TAN_INDEXER + 0.5); + double delta = (angle - index * TAN_DELTA_HI) - index * TAN_DELTA_LO; + // Making sure index is within tables range. + // 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; + } + double result; + if (index < TAN_TABS_SIZE) { + result = MyTTan.tanTab[index] + + delta * (MyTTan.tanDer1DivF1Tab[index] + + delta * (MyTTan.tanDer2DivF2Tab[index] + + delta * (MyTTan.tanDer3DivF3Tab[index] + + delta * MyTTan.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/(MyTTan.tanTab[index] + - delta * (MyTTan.tanDer1DivF1Tab[index] + - delta * (MyTTan.tanDer2DivF2Tab[index] + - delta * (MyTTan.tanDer3DivF3Tab[index] + - delta * MyTTan.tanDer4DivF4Tab[index])))); + } + return negateResult ? -result : result; + } + + /** + * @param value Value in [-1,1]. + * @return Value arcsine, in radians, in [-PI/2,PI/2]. + */ + public static double asin(double value) { + if (USE_JDK_MATH) { + return StrictMath.asin(value); + } + boolean negateResult = false; + if (value < 0.0) { + value = -value; + negateResult = true; + } + if (value <= ASIN_MAX_VALUE_FOR_TABS) { + int index = (int)(value * ASIN_INDEXER + 0.5); + double delta = value - index * ASIN_DELTA; + double result = MyTAsin.asinTab[index] + + delta * (MyTAsin.asinDer1DivF1Tab[index] + + delta * (MyTAsin.asinDer2DivF2Tab[index] + + delta * (MyTAsin.asinDer3DivF3Tab[index] + + delta * MyTAsin.asinDer4DivF4Tab[index]))); + return negateResult ? -result : result; + } else if (USE_POWTABS_FOR_ASIN && (value <= ASIN_MAX_VALUE_FOR_POWTABS)) { + int index = (int)(powFast(value * ASIN_POWTABS_ONE_DIV_MAX_VALUE, ASIN_POWTABS_POWER) * ASIN_POWTABS_SIZE_MINUS_ONE + 0.5); + double delta = value - MyTAsinPow.asinParamPowTab[index]; + double result = MyTAsinPow.asinPowTab[index] + + delta * (MyTAsinPow.asinDer1DivF1PowTab[index] + + delta * (MyTAsinPow.asinDer2DivF2PowTab[index] + + delta * (MyTAsinPow.asinDer3DivF3PowTab[index] + + delta * MyTAsinPow.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 = 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; + } + } + } + } + + /** + * If value is not NaN and is outside [-1,1] range, closest value in this range is used. + * + * @param value Value in [-1,1]. + * @return Value arcsine, in radians, in [-PI/2,PI/2]. + */ + public static double asinInRange(double value) { + if (value <= -1.0) { + return -Math.PI/2; + } else if (value >= 1.0) { + return Math.PI/2; + } else { + return asin(value); + } + } + + /** + * @param value Value in [-1,1]. + * @return Value arccosine, in radians, in [0,PI]. + */ + public static double acos(double value) { + if (USE_JDK_MATH) { + return StrictMath.acos(value); + } + return Math.PI/2 - asin(value); + } + + /** + * If value is not NaN and is outside [-1,1] range, + * closest value in this range is used. + * + * @param value Value in [-1,1]. + * @return Value arccosine, in radians, in [0,PI]. + */ + public static double acosInRange(double value) { + if (value <= -1.0) { + return Math.PI; + } else if (value >= 1.0) { + return 0.0; + } else { + return acos(value); + } + } + + /** + * @param value A double value. + * @return Value arctangent, in radians, in [-PI/2,PI/2]. + */ + public static double atan(double value) { + if (USE_JDK_MATH) { + return StrictMath.atan(value); + } + boolean negateResult = false; + if (value < 0.0) { + value = -value; + negateResult = true; + } + 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 = MyTAtan.atanTab[index] + + delta * (MyTAtan.atanDer1DivF1Tab[index] + + delta * (MyTAtan.atanDer2DivF2Tab[index] + + delta * (MyTAtan.atanDer3DivF3Tab[index] + + delta * MyTAtan.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 (value != 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 StrictMath.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 (USE_JDK_MATH) { + return StrictMath.atan2(y,x); + } + /* + * Using sub-methods, to make method lighter for general case, + * and to avoid JIT-optimization crash on NaN. + */ + if (x > 0.0) { + if (y == 0.0) { + // +-0.0 + return y; + } + if (x == Double.POSITIVE_INFINITY) { + return atan2_pinf_yyy(y); + } else { + return atan(y/x); + } + } else if (x < 0.0) { + if (y == 0.0) { + return signFromBit(y) * Math.PI; + } + if (x == Double.NEGATIVE_INFINITY) { + return atan2_ninf_yyy(y); + } else if (y > 0.0) { + return Math.PI/2 - atan(x/y); + } else if (y < 0.0) { + return -Math.PI/2 - atan(x/y); + } else { + return Double.NaN; + } + } else { + return atan2_yyy_zeroOrNaN(y, x); + } + } + + /** + * Gives same result as StrictMath.toRadians for some particular values + * like 90.0, 180.0 or 360.0, but is faster (no division). + * + * @param angdeg Angle value in degrees. + * @return Angle value in radians. + */ + public static double toRadians(double angdeg) { + if (USE_JDK_MATH) { + return StrictMath.toRadians(angdeg); + } + return angdeg * (Math.PI/180); + } + + /** + * Gives same result as StrictMath.toDegrees for some particular values + * like Math.PI/2, Math.PI or 2*Math.PI, but is faster (no division). + * + * @param angrad Angle value in radians. + * @return Angle value in degrees. + */ + public static double toDegrees(double angrad) { + if (USE_JDK_MATH) { + return StrictMath.toDegrees(angrad); + } + return angrad * (180/Math.PI); + } + + /** + * @param sign Sign of the angle: true for positive, false for negative. + * @param degrees Degrees, in [0,180]. + * @param minutes Minutes, in [0,59]. + * @param seconds Seconds, in [0.0,60.0[. + * @return Angle in radians. + */ + public static double toRadians(boolean sign, int degrees, int minutes, double seconds) { + return toRadians(toDegrees(sign, degrees, minutes, seconds)); + } + + /** + * @param sign Sign of the angle: true for positive, false for negative. + * @param degrees Degrees, in [0,180]. + * @param minutes Minutes, in [0,59]. + * @param seconds Seconds, in [0.0,60.0[. + * @return Angle in degrees. + */ + public static double toDegrees(boolean sign, int degrees, int minutes, double seconds) { + double signFactor = sign ? 1.0 : -1.0; + return signFactor * (degrees + (1.0/60)*(minutes + (1.0/60)*seconds)); + } + + /** + * @param angrad Angle in radians. + * @param degrees (out) Degrees, in [0,180]. + * @param minutes (out) Minutes, in [0,59]. + * @param seconds (out) Seconds, in [0.0,60.0[. + * @return true if the resulting angle in [-180deg,180deg] is positive, false if it is negative. + */ + public static boolean toDMS(double angrad, IntWrapper degrees, IntWrapper minutes, DoubleWrapper seconds) { + // Computing longitude DMS. + double tmp = toDegrees(normalizeMinusPiPi(angrad)); + boolean isNeg = (tmp < 0.0); + if (isNeg) { + tmp = -tmp; + } + degrees.value = (int)tmp; + tmp = (tmp-degrees.value)*60.0; + minutes.value = (int)tmp; + seconds.value = Math.min((tmp-minutes.value)*60.0,DOUBLE_BEFORE_60); + return !isNeg; + } + + /** + * NB: Since 2*Math.PI < 2*PI, a span of 2*Math.PI does not mean full angular range. + * ex.: isInClockwiseDomain(0.0, 2*Math.PI, -1e-20) returns false. + * ---> For full angular range, use a span > 2*Math.PI, like 2*PI_SUP constant of this class. + * + * @param startAngRad An angle, in radians. + * @param angSpanRad An angular span, >= 0.0, in radians. + * @param angRad An angle, in radians. + * @return true if angRad is in the clockwise angular domain going from startAngRad, over angSpanRad, + * extremities included, false otherwise. + */ + public static boolean isInClockwiseDomain(double startAngRad, double angSpanRad, double angRad) { + if (Math.abs(angRad) < -TWO_MATH_PI_IN_MINUS_PI_PI) { + // special case for angular values of small magnitude + if (angSpanRad <= 2*Math.PI) { + if (angSpanRad < 0.0) { + // empty domain + return false; + } + // angSpanRad is in [0,2*PI] + startAngRad = normalizeMinusPiPi(startAngRad); + double endAngRad = normalizeMinusPiPi(startAngRad + angSpanRad); + if (startAngRad <= endAngRad) { + return (angRad >= startAngRad) && (angRad <= endAngRad); + } else { + return (angRad >= startAngRad) || (angRad <= endAngRad); + } + } else { // angSpanRad > 2*Math.PI, or is NaN + return (angSpanRad == angSpanRad); + } + } else { + // general case + return (normalizeZeroTwoPi(angRad - startAngRad) <= angSpanRad); + } + } + + /* + * hyperbolic trigonometry + */ + + /** + * Some properties of sinh(x) = (exp(x)-exp(-x))/2: + * 1) defined on ]-Infinity,+Infinity[ + * 2) result in ]-Infinity,+Infinity[ + * 3) sinh(x) = -sinh(-x) (implies sinh(0) = 0) + * 4) sinh(epsilon) ~= epsilon + * 5) lim(sinh(x),x->+Infinity) = +Infinity + * (y increasing exponentially faster than x) + * 6) reaches +Infinity (double overflow) for x >= 710.475860073944, + * i.e. a bit further than exp(x) + * + * @param value A double value. + * @return Value hyperbolic sine. + */ + public static double sinh(double value) { + if (USE_JDK_MATH) { + return StrictMath.sinh(value); + } + // sinh(x) = (exp(x)-exp(-x))/2 + double h; + if (value < 0.0) { + value = -value; + h = -0.5; + } else { + h = 0.5; + } + if (value < 22.0) { + if (value < TWO_POW_N28) { + return (h < 0.0) ? -value : value; + } else { + // sinh(x) + // = (exp(x)-exp(-x))/2 + // = (exp(x)-1/exp(x))/2 + // = (expm1(x) + 1 - 1/(expm1(x)+1))/2 + // = (expm1(x) + (expm1(x)+1)/(expm1(x)+1) - 1/(expm1(x)+1))/2 + // = (expm1(x) + expm1(x)/(expm1(x)+1))/2 + double t = expm1(value); + // Might be more accurate, if value < 1: return h*((t+t)-t*t/(t+1.0)). + return h * (t + t/(t+1.0)); + } + } else if (value < LOG_DOUBLE_MAX_VALUE) { + return h * exp(value); + } else { + double t = exp(value*0.5); + return (h*t)*t; + } + } + + /** + * Some properties of cosh(x) = (exp(x)+exp(-x))/2: + * 1) defined on ]-Infinity,+Infinity[ + * 2) result in [1,+Infinity[ + * 3) cosh(0) = 1 + * 4) cosh(x) = cosh(-x) + * 5) lim(cosh(x),x->+Infinity) = +Infinity + * (y increasing exponentially faster than x) + * 6) reaches +Infinity (double overflow) for x >= 710.475860073944, + * i.e. a bit further than exp(x) + * + * @param value A double value. + * @return Value hyperbolic cosine. + */ + public static double cosh(double value) { + if (USE_JDK_MATH) { + return StrictMath.cosh(value); + } + // cosh(x) = (exp(x)+exp(-x))/2 + if (value < 0.0) { + value = -value; + } + if (value < LOG_TWO_POW_27) { + if (value < TWO_POW_N27) { + // cosh(x) + // = (exp(x)+exp(-x))/2 + // = ((1+x+x^2/2!+...) + (1-x+x^2/2!-...))/2 + // = 1+x^2/2!+x^4/4!+... + // For value of x small in magnitude, the sum of the terms does not add to 1. + return 1; + } else { + // cosh(x) + // = (exp(x)+exp(-x))/2 + // = (exp(x)+1/exp(x))/2 + double t = exp(value); + return 0.5 * (t+1/t); + } + } else if (value < LOG_DOUBLE_MAX_VALUE) { + return 0.5 * exp(value); + } else { + double t = exp(value*0.5); + return (0.5*t)*t; + } + } + + /** + * Much more accurate than cosh(value)-1, + * for arguments (and results) close to zero. + * + * coshm1(-0.0) = -0.0, for homogeneity with + * acosh1p(-0.0) = -0.0. + * + * @param value A double value. + * @return Value hyperbolic cosine, minus 1. + */ + public static double coshm1(double value) { + // cosh(x)-1 = (exp(x)+exp(-x))/2 - 1 + if (value < 0.0) { + value = -value; + } + if (value < LOG_TWO_POW_27) { + if (value < TWO_POW_N27) { + if (value == 0.0) { + // +-0.0 + return value; + } + // Using (expm1(x)+expm1(-x))/2 + // is not accurate for tiny values, + // for expm1 results are of higher + // magnitude than the result and + // of different signs, such as their + // sum is not accurate. + // cosh(x) - 1 + // = (exp(x)+exp(-x))/2 - 1 + // = ((1+x+x^2/2!+...) + (1-x+x^2/2!-...))/2 - 1 + // = x^2/2!+x^4/4!+... + // ~= x^2 * (1/2 + x^2 * 1/24) + // = x^2 * 0.5 (since x < 2^-27) + return 0.5 * value*value; + } else { + // cosh(x) - 1 + // = (exp(x)+exp(-x))/2 - 1 + // = (exp(x)-1+exp(-x)-1)/2 + // = (expm1(x)+expm1(-x))/2 + return 0.5 * (expm1(value)+expm1(-value)); + } + } else if (value < LOG_DOUBLE_MAX_VALUE) { + return 0.5 * exp(value) - 1.0; + } else { + // No need to subtract 1 from result. + double t = exp(value*0.5); + return (0.5*t)*t; + } + } + + /** + * Computes hyperbolic sine and hyperbolic cosine together. + * + * @param value A double value. + * @param hcosine (out) Value hyperbolic cosine. + * @return Value hyperbolic sine. + */ + public static double sinhAndCosh(double value, DoubleWrapper hcosine) { + if (USE_JDK_MATH) { + hcosine.value = StrictMath.cosh(value); + return StrictMath.sinh(value); + } + // Mixup of sinh and cosh treatments: if you modify them, + // you might want to also modify this. + double h; + if (value < 0.0) { + value = -value; + h = -0.5; + } else { + h = 0.5; + } + final double hsine; + // LOG_TWO_POW_27 = 18.714973875118524 + if (value < LOG_TWO_POW_27) { // test from cosh + // sinh + if (value < TWO_POW_N28) { + hsine = (h < 0.0) ? -value : value; + } else { + double t = expm1(value); + hsine = h * (t + t/(t+1.0)); + } + // cosh + if (value < TWO_POW_N27) { + hcosine.value = 1; + } else { + double t = exp(value); + hcosine.value = 0.5 * (t+1/t); + } + } else if (value < 22.0) { // test from sinh + // Here, value is in [18.714973875118524,22.0[. + double t = expm1(value); + hsine = h * (t + t/(t+1.0)); + hcosine.value = 0.5 * (t+1.0); + } else { + if (value < LOG_DOUBLE_MAX_VALUE) { + hsine = h * exp(value); + } else { + double t = exp(value*0.5); + hsine = (h*t)*t; + } + hcosine.value = Math.abs(hsine); + } + return hsine; + } + + /** + * Some properties of tanh(x) = sinh(x)/cosh(x) = (exp(2*x)-1)/(exp(2*x)+1): + * 1) defined on ]-Infinity,+Infinity[ + * 2) result in ]-1,1[ + * 3) tanh(x) = -tanh(-x) (implies tanh(0) = 0) + * 4) tanh(epsilon) ~= epsilon + * 5) lim(tanh(x),x->+Infinity) = 1 + * 6) reaches 1 (double loss of precision) for x = 19.061547465398498 + * + * @param value A double value. + * @return Value hyperbolic tangent. + */ + public static double tanh(double value) { + if (USE_JDK_MATH) { + return StrictMath.tanh(value); + } + // tanh(x) = sinh(x)/cosh(x) + // = (exp(x)-exp(-x))/(exp(x)+exp(-x)) + // = (exp(2*x)-1)/(exp(2*x)+1) + boolean negateResult = false; + if (value < 0.0) { + value = -value; + negateResult = true; + } + double z; + if (value < TANH_1_THRESHOLD) { + if (value < TWO_POW_N55) { + return negateResult ? -value*(1.0-value) : value*(1.0+value); + } else if (value >= 1) { + z = 1.0-2.0/(expm1(value+value)+2.0); + } else { + double t = expm1(-(value+value)); + z = -t/(t+2.0); + } + } else { + z = (value != value) ? Double.NaN : 1.0; + } + return negateResult ? -z : z; + } + + /** + * Some properties of asinh(x) = log(x + sqrt(x^2 + 1)) + * 1) defined on ]-Infinity,+Infinity[ + * 2) result in ]-Infinity,+Infinity[ + * 3) asinh(x) = -asinh(-x) (implies asinh(0) = 0) + * 4) asinh(epsilon) ~= epsilon + * 5) lim(asinh(x),x->+Infinity) = +Infinity + * (y increasing logarithmically slower than x) + * + * @param value A double value. + * @return Value hyperbolic arcsine. + */ + public static double asinh(double value) { + // asinh(x) = log(x + sqrt(x^2 + 1)) + boolean negateResult = false; + if (value < 0.0) { + value = -value; + negateResult = true; + } + double result; + // (about) smallest possible for + // non-log1p case to be accurate. + if (value < ASINH_LOG1P_THRESHOLD) { + // Around this range, FDLIBM uses + // log1p(value+value*value/(1+sqrt(value*value+1))), + // but it's slower, so we don't use it. + /* + * If x is close to zero, log argument is close to 1, + * so to avoid precision loss we use log1p(double), + * with + * (1+x)^p = 1 + p * x + (p*(p-1))/2! * x^2 + (p*(p-1)*(p-2))/3! * x^3 + ... + * (1+x)^p = 1 + p * x * (1 + (p-1)/2 * x * (1 + (p-2)/3 * x + ...) + * (1+x)^0.5 = 1 + 0.5 * x * (1 + (0.5-1)/2 * x * (1 + (0.5-2)/3 * x + ...) + * (1+x^2)^0.5 = 1 + 0.5 * x^2 * (1 + (0.5-1)/2 * x^2 * (1 + (0.5-2)/3 * x^2 + ...) + * x + (1+x^2)^0.5 = 1 + x * (1 + 0.5 * x * (1 + (0.5-1)/2 * x^2 * (1 + (0.5-2)/3 * x^2 + ...)) + * so + * asinh(x) = log1p(x * (1 + 0.5 * x * (1 + (0.5-1)/2 * x^2 * (1 + (0.5-2)/3 * x^2 + ...))) + */ + final double x = value; + final double x2 = x*x; + // Enough terms for good accuracy, + // given our threshold. + final double argLog1p = (x * + (1 + 0.5 * x + * (1 + (0.5-1)/2 * x2 + * (1 + (0.5-2)/3 * x2 + * (1 + (0.5-3)/4 * x2 + * (1 + (0.5-4)/5 * x2 + )))))); + result = log1p(argLog1p); + } else if (value < ASINH_ACOSH_SQRT_ELISION_THRESHOLD) { + // Around this range, FDLIBM uses + // log(2*value+1/(value+sqrt(value*value+1))), + // but it involves an additional division + // so we don't use it. + result = log(value + sqrt(value*value + 1.0)); + } else { + // log(2*value) would overflow for value > Double.MAX_VALUE/2, + // so we compute otherwise. + result = LOG_2 + log(value); + } + return negateResult ? -result : result; + } + + /** + * Some properties of acosh(x) = log(x + sqrt(x^2 - 1)): + * 1) defined on [1,+Infinity[ + * 2) result in ]0,+Infinity[ (by convention, since cosh(x) = cosh(-x)) + * 3) acosh(1) = 0 + * 4) acosh(1+epsilon) ~= log(1 + sqrt(2*epsilon)) ~= sqrt(2*epsilon) + * 5) lim(acosh(x),x->+Infinity) = +Infinity + * (y increasing logarithmically slower than x) + * + * @param value A double value. + * @return Value hyperbolic arccosine. + */ + public static double acosh(double value) { + if (!(value > 1.0)) { + // NaN, or value <= 1 + if (ANTI_JIT_OPTIM_CRASH_ON_NAN) { + return (value < 1.0) ? Double.NaN : value - 1.0; + } else { + return (value == 1.0) ? 0.0 : Double.NaN; + } + } + double result; + if (value < ASINH_ACOSH_SQRT_ELISION_THRESHOLD) { + // Around this range, FDLIBM uses + // log(2*value-1/(value+sqrt(value*value-1))), + // but it involves an additional division + // so we don't use it. + result = log(value + sqrt(value*value - 1.0)); + } else { + // log(2*value) would overflow for value > Double.MAX_VALUE/2, + // so we compute otherwise. + result = LOG_2 + log(value); + } + return result; + } + + /** + * Much more accurate than acosh(1+value), + * for arguments (and results) close to zero. + * + * acosh1p(-0.0) = -0.0, for homogeneity with + * sqrt(-0.0) = -0.0, which looks about the same + * near 0. + * + * @param value A double value. + * @return Hyperbolic arccosine of (1+value). + */ + public static double acosh1p(double value) { + if (!(value > 0.0)) { + // NaN, or value <= 0. + // If value is -0.0, returning -0.0. + if (ANTI_JIT_OPTIM_CRASH_ON_NAN) { + return (value < 0.0) ? Double.NaN : value; + } else { + return (value == 0.0) ? value : Double.NaN; + } + } + double result; + if (value < (ASINH_ACOSH_SQRT_ELISION_THRESHOLD-1)) { + // acosh(1+x) + // = log((1+x) + sqrt((1+x)^2 - 1)) + // = log(1 + x + sqrt(1 + 2*x + x^2 - 1)) + // = log1p(x + sqrt(2*x + x^2)) + // = log1p(x + sqrt(x * (2 + x)) + result = log1p(value + sqrt(value * (2 + value))); + } else { + result = LOG_2 + log(1+value); + } + return result; + } + + /** + * Some properties of atanh(x) = log((1+x)/(1-x))/2: + * 1) defined on ]-1,1[ + * 2) result in ]-Infinity,+Infinity[ + * 3) atanh(-1) = -Infinity (by continuity) + * 4) atanh(1) = +Infinity (by continuity) + * 5) atanh(epsilon) ~= epsilon + * 6) lim(atanh(x),x->1) = +Infinity + * + * @param value A double value. + * @return Value hyperbolic arctangent. + */ + public static double atanh(double value) { + boolean negateResult = false; + if (value < 0.0) { + value = -value; + negateResult = true; + } + double result; + if (!(value < 1.0)) { + // NaN, or value >= 1 + if (ANTI_JIT_OPTIM_CRASH_ON_NAN) { + result = (value > 1.0) ? Double.NaN : Double.POSITIVE_INFINITY + value; + } else { + result = (value == 1.0) ? Double.POSITIVE_INFINITY : Double.NaN; + } + } else { + // For value < 0.5, FDLIBM uses + // 0.5 * log1p((value+value) + (value+value)*value/(1-value)), + // instead, but this is good enough for us. + // atanh(x) + // = log((1+x)/(1-x))/2 + // = log((1-x+2x)/(1-x))/2 + // = log1p(2x/(1-x))/2 + result = 0.5 * log1p((value+value)/(1.0-value)); + } + return negateResult ? -result : result; + } + + /* + * exponentials + */ + + /** + * @param value A double value. + * @return e^value. + */ + public static double exp(double value) { + if (USE_JDK_MATH) { + return StrictMath.exp(value); + } + // exp(x) = exp([x])*exp(y) + // with [x] the integer part of x, and y = x-[x] + // ===> + // We find an approximation of y, called z. + // ===> + // exp(x) = exp([x])*(exp(z)*exp(epsilon)) + // with epsilon = y - z + // ===> + // We have exp([x]) and exp(z) pre-computed in tables, we "just" have to compute exp(epsilon). + // + // We use the same indexing (cast to int) to compute x integer part and the + // table index corresponding to z, to avoid two int casts. + // Also, to optimize index multiplication and division, we use powers of two, + // so that we can do it with bits shifts. + + if (value > EXP_OVERFLOW_LIMIT) { + return Double.POSITIVE_INFINITY; + } else if (!(value >= EXP_UNDERFLOW_LIMIT)) { + return (value != value) ? Double.NaN : 0.0; + } + + final int indexes = (int)(value*EXP_LO_INDEXING); + + final int valueInt; + if (indexes >= 0) { + valueInt = (indexes>>EXP_LO_INDEXING_DIV_SHIFT); + } else { + valueInt = -((-indexes)>>EXP_LO_INDEXING_DIV_SHIFT); + } + final double hiTerm = MyTExp.expHiTab[valueInt-(int)EXP_UNDERFLOW_LIMIT]; + + final int zIndex = indexes - (valueInt< 0.0) { + if (value == Double.POSITIVE_INFINITY) { + return Double.POSITIVE_INFINITY; + } + + // For normal values not close to 1.0, we use the following formula: + // log(value) + // = log(2^exponent*1.mantissa) + // = log(2^exponent) + log(1.mantissa) + // = exponent * log(2) + log(1.mantissa) + // = exponent * log(2) + log(1.mantissaApprox) + log(1.mantissa/1.mantissaApprox) + // = exponent * log(2) + log(1.mantissaApprox) + log(1+epsilon) + // = exponent * log(2) + log(1.mantissaApprox) + epsilon-epsilon^2/2+epsilon^3/3-epsilon^4/4+... + // with: + // 1.mantissaApprox <= 1.mantissa, + // log(1.mantissaApprox) in table, + // epsilon = (1.mantissa/1.mantissaApprox)-1 + // + // To avoid bad relative error for small results, + // values close to 1.0 are treated aside, with the formula: + // log(x) = z*(2+z^2*((2.0/3)+z^2*((2.0/5))+z^2*((2.0/7))+...))) + // with z=(x-1)/(x+1) + + double h; + if (value > 0.95) { + if (value < 1.14) { + double z = (value-1.0)/(value+1.0); + double z2 = z*z; + return z*(2+z2*((2.0/3)+z2*((2.0/5)+z2*((2.0/7)+z2*((2.0/9)+z2*((2.0/11))))))); + } + h = 0.0; + } else if (value < DOUBLE_MIN_NORMAL) { + // Ensuring value is normal. + value *= TWO_POW_52; + // log(x*2^52) + // = log(x)-ln(2^52) + // = log(x)-52*ln(2) + h = -52*LOG_2; + } else { + h = 0.0; + } + + int valueBitsHi = (int)(Double.doubleToRawLongBits(value)>>32); + int valueExp = (valueBitsHi>>20)-MAX_DOUBLE_EXPONENT; + // Getting the first LOG_BITS bits of the mantissa. + int xIndex = ((valueBitsHi<<12)>>>(32-LOG_BITS)); + + // 1.mantissa/1.mantissaApprox - 1 + double z = (value * twoPowNormalOrSubnormal(-valueExp)) * MyTLog.logXInvTab[xIndex] - 1; + + z *= (1-z*((1.0/2)-z*((1.0/3)))); + + return h + valueExp * LOG_2 + (MyTLog.logXLogTab[xIndex] + z); + + } else if (value == 0.0) { + return Double.NEGATIVE_INFINITY; + } else { // value < 0.0, or value is NaN + return Double.NaN; + } + } + + /** + * Quick log, with a max relative error of about 1.9e-3 + * for values in ]Double.MIN_NORMAL,+Infinity[, and + * worse accuracy outside this range. + * + * @param value A double value, in ]0,+Infinity[ (strictly positive and finite). + * @return Value logarithm (base e). + */ + public static double logQuick(double value) { + if (USE_JDK_MATH) { + return StrictMath.log(value); + } + /* + * Inverse of Schraudolph's method for exp, is very inaccurate near 1, + * and not that fast (even using floats), especially with added if's + * to deal with values near 1, so we don't use it, and use a simplified + * version of our log's redefined algorithm. + */ + + // Simplified version of log's redefined algorithm: + // log(value) ~= exponent * log(2) + log(1.mantissaApprox) + + double h; + if (value > 0.87) { + if (value < 1.16) { + return 2.0 * (value-1.0)/(value+1.0); + } + h = 0.0; + } else if (value < DOUBLE_MIN_NORMAL) { + value *= TWO_POW_52; + h = -52*LOG_2; + } else { + h = 0.0; + } + + int valueBitsHi = (int)(Double.doubleToRawLongBits(value)>>32); + int valueExp = (valueBitsHi>>20)-MAX_DOUBLE_EXPONENT; + int xIndex = ((valueBitsHi<<12)>>>(32-LOG_BITS)); + + return h + valueExp * LOG_2 + MyTLog.logXLogTab[xIndex]; + } + + /** + * @param value A double value. + * @return Value logarithm (base 10). + */ + public static double log10(double value) { + if (USE_JDK_MATH || (!USE_REDEFINED_LOG)) { + return StrictMath.log10(value); + } + // INV_LOG_10 is < 1, but there is no risk of log(double) + // overflow (positive or negative) while the end result shouldn't, + // since log(Double.MIN_VALUE) and log(Double.MAX_VALUE) have + // magnitudes of just a few hundreds. + return log(value) * INV_LOG_10; + } + + /** + * Much more accurate than log(1+value), + * for arguments (and results) close to zero. + * + * @param value A double value. + * @return Logarithm (base e) of (1+value). + */ + public static double log1p(double value) { + if (USE_JDK_MATH) { + return StrictMath.log1p(value); + } + if (false) { + // This also works. Simpler but a bit slower. + if (value == Double.POSITIVE_INFINITY) { + return Double.POSITIVE_INFINITY; + } + double valuePlusOne = 1+value; + if (valuePlusOne == 1.0) { + return value; + } else { + return log(valuePlusOne)*(value/(valuePlusOne-1.0)); + } + } + if (value > -1.0) { + if (value == Double.POSITIVE_INFINITY) { + return Double.POSITIVE_INFINITY; + } + + // ln'(x) = 1/x + // so + // log(x+epsilon) ~= log(x) + epsilon/x + // + // Let u be 1+value rounded: + // 1+value = u+epsilon + // + // log(1+value) + // = log(u+epsilon) + // ~= log(u) + epsilon/value + // We compute log(u) as done in log(double), and then add the corrective term. + + double valuePlusOne = 1.0+value; + if (valuePlusOne == 1.0) { + return value; + } else if (Math.abs(value) < 0.15) { + double z = value/(value+2.0); + double z2 = z*z; + return z*(2+z2*((2.0/3)+z2*((2.0/5)+z2*((2.0/7)+z2*((2.0/9)+z2*((2.0/11))))))); + } + + int valuePlusOneBitsHi = (int)(Double.doubleToRawLongBits(valuePlusOne)>>32) & 0x7FFFFFFF; + int valuePlusOneExp = (valuePlusOneBitsHi>>20)-MAX_DOUBLE_EXPONENT; + // Getting the first LOG_BITS bits of the mantissa. + int xIndex = ((valuePlusOneBitsHi<<12)>>>(32-LOG_BITS)); + + // 1.mantissa/1.mantissaApprox - 1 + double z = (valuePlusOne * twoPowNormalOrSubnormal(-valuePlusOneExp)) * MyTLog.logXInvTab[xIndex] - 1; + + z *= (1-z*((1.0/2)-z*(1.0/3))); + + // Adding epsilon/valuePlusOne to z, + // with + // epsilon = value - (valuePlusOne-1) + // (valuePlusOne + epsilon ~= 1+value (not rounded)) + + return valuePlusOneExp * LOG_2 + MyTLog.logXLogTab[xIndex] + (z + (value - (valuePlusOne-1))/valuePlusOne); + } else if (value == -1.0) { + return Double.NEGATIVE_INFINITY; + } else { // value < -1.0, or value is NaN + return Double.NaN; + } + } + + /* + * powers + */ + + /** + * 1e-13ish accuracy or better on whole double range. + * + * @param value A double value. + * @param power A power. + * @return value^power. + */ + public static double pow(double value, double power) { + if (USE_JDK_MATH) { + return StrictMath.pow(value,power); + } + if (power == 0.0) { + return 1.0; + } else if (power == 1.0) { + return value; + } + if (value <= 0.0) { + // powerInfo: 0 if not integer, 1 if even integer, -1 if odd integer + int powerInfo; + if (Math.abs(power) >= (TWO_POW_52*2)) { + // The binary digit just before comma is outside mantissa, + // thus it is always 0: power is an even integer. + powerInfo = 1; + } else { + // If power's magnitude permits, we cast into int instead of into long, + // as it is faster. + if (Math.abs(power) <= (double)Integer.MAX_VALUE) { + int powerAsInt = (int)power; + if (power == (double)powerAsInt) { + powerInfo = ((powerAsInt & 1) == 0) ? 1 : -1; + } else { // power is not an integer (and not NaN, due to test against Integer.MAX_VALUE) + powerInfo = 0; + } + } else { + long powerAsLong = (long)power; + if (power == (double)powerAsLong) { + powerInfo = ((powerAsLong & 1) == 0) ? 1 : -1; + } else { // power is not an integer, or is NaN + if (power != power) { + return Double.NaN; + } + powerInfo = 0; + } + } + } + + if (value == 0.0) { + if (power < 0.0) { + return (powerInfo < 0) ? 1/value : Double.POSITIVE_INFINITY; + } else { // power > 0.0 (0 and NaN cases already treated) + return (powerInfo < 0) ? value : 0.0; + } + } else { // value < 0.0 + if (value == Double.NEGATIVE_INFINITY) { + if (powerInfo < 0) { // power odd integer + return (power < 0.0) ? -0.0 : Double.NEGATIVE_INFINITY; + } else { // power even integer, or not an integer + return (power < 0.0) ? 0.0 : Double.POSITIVE_INFINITY; + } + } else { + return (powerInfo == 0) ? Double.NaN : powerInfo * exp(power*log(-value)); + } + } + } else { // value > 0.0, or value is NaN + return exp(power*log(value)); + } + } + + /** + * Quick pow, with a max relative error of about 1e-2 + * for value >= Double.MIN_NORMAL and 1e-10 < |value^power| < 1e10, + * of about 6e-2 for value >= Double.MIN_NORMAL and 1e-40 < |value^power| < 1e40, + * and worse accuracy otherwise. + * + * @param value A double value, in ]0,+Infinity[ (strictly positive and finite). + * @param power A double value. + * @return value^power. + */ + public static double powQuick(double value, double power) { + if (USE_JDK_MATH) { + return StrictMath.pow(value,power); + } + return exp(power*logQuick(value)); + } + + /** + * 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. + */ + public static double powFast(double value, int power) { + if (USE_JDK_MATH) { + return StrictMath.pow(value,power); + } + if (power < 3) { + if (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/(powFast(value,Integer.MAX_VALUE) * value); + } else { + return 1.0/powFast(value,-power); + } + } else { + // Here, power is in [0,2]. + if (power == 2) { // Most common case first. + return value * value; + } else if (power == 0) { + return 1.0; + } else { // power == 1 + return value; + } + } + } else { // power >= 4 + double oddRemains = 1.0; + // If power <= 5, faster to finish outside the loop. + while (power > 5) { + // Test if power is odd. + if ((power & 1) != 0) { + oddRemains *= value; + } + value *= value; + power >>= 1; // power = power / 2 + } + // Here, power is in [3,5]. + if (power == 3) { + return oddRemains * value * value * value; + } else { // power in [4,5]. + double v2 = value * value; + if (power == 4) { + return oddRemains * v2 * v2; + } else { // power == 5 + return oddRemains * v2 * v2 * value; + } + } + } + } + + /** + * @param value A float value. + * @return value*value. + */ + public static float pow2(float value) { + return value*value; + } + + /** + * @param value A double value. + * @return value*value. + */ + public static double pow2(double value) { + return value*value; + } + + /** + * @param value A float value. + * @return value*value*value. + */ + public static float pow3(float value) { + return value*value*value; + } + + /** + * @param value A double value. + * @return value*value*value. + */ + public static double pow3(double value) { + return value*value*value; + } + + /* + * roots + */ + + /** + * @param value A double value. + * @return Value square root. + */ + public static double sqrt(double value) { + if (USE_JDK_MATH || (!USE_REDEFINED_SQRT)) { + return StrictMath.sqrt(value); + } + // See cbrt for comments, sqrt uses the same ideas. + + if (!(value > 0.0)) { // value <= 0.0, or value is NaN + if (ANTI_JIT_OPTIM_CRASH_ON_NAN) { + return (value < 0.0) ? Double.NaN : value; + } else { + return (value == 0.0) ? value : Double.NaN; + } + } else if (value == Double.POSITIVE_INFINITY) { + return Double.POSITIVE_INFINITY; + } + + double h; + if (value < DOUBLE_MIN_NORMAL) { + value *= TWO_POW_52; + h = 2*TWO_POW_N26; + } else { + h = 2.0; + } + + int valueBitsHi = (int)(Double.doubleToRawLongBits(value)>>32); + int valueExponentIndex = (valueBitsHi>>20)+(-MAX_DOUBLE_EXPONENT-MIN_DOUBLE_EXPONENT); + int xIndex = ((valueBitsHi<<12)>>>(32-SQRT_LO_BITS)); + + double result = MyTSqrt.sqrtXSqrtHiTab[valueExponentIndex] * MyTSqrt.sqrtXSqrtLoTab[xIndex]; + double slope = MyTSqrt.sqrtSlopeHiTab[valueExponentIndex] * MyTSqrt.sqrtSlopeLoTab[xIndex]; + value *= 0.25; + + result += (value - result * result) * slope; + result += (value - result * result) * slope; + return h*(result + (value - result * result) * slope); + } + + /** + * Quick sqrt, with with a max relative error of about 3.41e-2 + * for values in [Double.MIN_NORMAL,Double.MAX_VALUE], and worse + * accuracy outside this range. + * + * @param value A double value. + * @return Value square root. + */ + public static double sqrtQuick(double value) { + if (USE_JDK_MATH) { + return StrictMath.sqrt(value); + } + final long bits = Double.doubleToRawLongBits(value); + /* + * Constant determined empirically, using a random-based metaheuristic. + * Should be possible to find a better one. + */ + return Double.longBitsToDouble((bits+4606859074900000000L)>>>1); + } + + /** + * Quick inverse of square root, with a max relative error of about 3.44e-2 + * for values in [Double.MIN_NORMAL,Double.MAX_VALUE], and worse accuracy + * outside this range. + * + * This implementation uses zero step of Newton's method. + * Here are the max relative errors on [Double.MIN_NORMAL,Double.MAX_VALUE] + * depending on number of steps, if you want to copy-paste this code + * and use your own number: + * n=0: about 3.44e-2 + * n=1: about 1.75e-3 + * n=2: about 4.6e-6 + * n=3: about 3.17e-11 + * n=4: about 3.92e-16 + * n=5: about 3.03e-16 + * + * @param value A double value. + * @return Inverse of value square root. + */ + public static double invSqrtQuick(double value) { + if (USE_JDK_MATH) { + return 1/StrictMath.sqrt(value); + } + /* + * http://en.wikipedia.org/wiki/Fast_inverse_square_root + */ + if (false) { + // With one Newton step (much slower than + // 1/Math.sqrt(double) if not optimized). + final double halfInitial = value * 0.5; + long bits = Double.doubleToRawLongBits(value); + // If n=0, 6910474759270000000L might be better (3.38e-2 max relative error). + bits = 0x5FE6EB50C7B537A9L - (bits>>1); + value = Double.longBitsToDouble(bits); + value = value * (1.5 - halfInitial * value * value); // Newton step, can repeat. + return value; + } else { + return Double.longBitsToDouble(0x5FE6EB50C7B537A9L - (Double.doubleToRawLongBits(value)>>1)); + } + } + + /** + * @param value A double value. + * @return Value cubic root. + */ + public static double cbrt(double value) { + if (USE_JDK_MATH) { + return StrictMath.cbrt(value); + } + double h; + if (value < 0.0) { + if (value == Double.NEGATIVE_INFINITY) { + return Double.NEGATIVE_INFINITY; + } + value = -value; + // Making sure value is normal. + if (value < DOUBLE_MIN_NORMAL) { + value *= (TWO_POW_52*TWO_POW_26); + // h = * / + h = -2*TWO_POW_N26; + } else { + h = -2.0; + } + } else { + if (!(value < Double.POSITIVE_INFINITY)) { // value is +Infinity, or value is NaN + return value; + } + // Making sure value is normal. + if (value < DOUBLE_MIN_NORMAL) { + if (value == 0.0) { + // cbrt(0.0) = 0.0, cbrt(-0.0) = -0.0 + return value; + } + value *= (TWO_POW_52*TWO_POW_26); + h = 2*TWO_POW_N26; + } else { + h = 2.0; + } + } + + // Normal value is (2^ * ). + // First member cubic root is computed, and multiplied with an approximation + // of the cubic root of the second member, to end up with a good guess of + // the result before using Newton's (or Archimedes's) method. + // To compute the cubic root approximation, we use the formula "cbrt(value) = cbrt(x) * cbrt(value/x)", + // choosing x as close to value as possible but inferior to it, so that cbrt(value/x) is close to 1 + // (we could iterate on this method, using value/x as new value for each iteration, + // but finishing with Newton's method is faster). + + // Shift and cast into an int, which overall is faster than working with a long. + int valueBitsHi = (int)(Double.doubleToRawLongBits(value)>>32); + int valueExponentIndex = (valueBitsHi>>20)+(-MAX_DOUBLE_EXPONENT-MIN_DOUBLE_EXPONENT); + // Getting the first CBRT_LO_BITS bits of the mantissa. + int xIndex = ((valueBitsHi<<12)>>>(32-CBRT_LO_BITS)); + double result = MyTCbrt.cbrtXCbrtHiTab[valueExponentIndex] * MyTCbrt.cbrtXCbrtLoTab[xIndex]; + double slope = MyTCbrt.cbrtSlopeHiTab[valueExponentIndex] * MyTCbrt.cbrtSlopeLoTab[xIndex]; + + // Lowering values to avoid overflows when using Newton's method + // (we will then just have to return twice the result). + // result^3 = value + // (result/2)^3 = value/8 + value *= 0.125; + // No need to divide result here, as division is factorized in result computation tables. + // result *= 0.5; + + // Newton's method, looking for y = x^(1/p): + // y(n) = y(n-1) + (x-y(n-1)^p) * slope(y(n-1)) + // y(n) = y(n-1) + (x-y(n-1)^p) * (1/p)*(x(n-1)^(1/p-1)) + // y(n) = y(n-1) + (x-y(n-1)^p) * (1/p)*(x(n-1)^((1-p)/p)) + // with x(n-1)=y(n-1)^p, i.e.: + // y(n) = y(n-1) + (x-y(n-1)^p) * (1/p)*(y(n-1)^(1-p)) + // + // For p=3: + // y(n) = y(n-1) + (x-y(n-1)^3) * (1/(3*y(n-1)^2)) + + // To save time, we don't recompute the slope between Newton's method steps, + // as initial slope is good enough for a few iterations. + // + // NB: slope = 1/(3*trueResult*trueResult) + // As we have result = trueResult/2 (to avoid overflows), we have: + // slope = 4/(3*result*result) + // = (4/3)*resultInv*resultInv + // with newResultInv = 1/newResult + // = 1/(oldResult+resultDelta) + // = (oldResultInv)*1/(1+resultDelta/oldResult) + // = (oldResultInv)*1/(1+resultDelta*oldResultInv) + // ~= (oldResultInv)*(1-resultDelta*oldResultInv) + // ===> Successive slopes could be computed without division, if needed, + // by computing resultInv (instead of slope right away) and retrieving + // slopes from it. + + result += (value - result * result * result) * slope; + result += (value - result * result * result) * slope; + return h*(result + (value - result * result * result) * slope); + } + + /** + * @return sqrt(x^2+y^2) without intermediate overflow or underflow. + */ + public static double hypot(double x, double y) { + if (USE_JDK_MATH) { + return StrictMath.hypot(x,y); + } + x = Math.abs(x); + y = Math.abs(y); + // Ensuring x <= y. + if (y < x) { + double a = x; + x = y; + y = a; + } else if (!(y >= x)) { // Testing if we have some NaN. + return hypot_NaN(x, y); + } + + if (y-x == y) { + // x too small to subtract from y. + return y; + } else { + double factor; + if (y > HYPOT_MAX_MAG) { + // y is too large: scaling down. + x *= (1/HYPOT_FACTOR); + y *= (1/HYPOT_FACTOR); + factor = HYPOT_FACTOR; + } else if (x < (1/HYPOT_MAX_MAG)) { + // x is too small: scaling up. + x *= HYPOT_FACTOR; + y *= HYPOT_FACTOR; + factor = (1/HYPOT_FACTOR); + } else { + factor = 1.0; + } + return factor * sqrt(x*x+y*y); + } + } + + /** + * @return sqrt(x^2+y^2+z^2) without intermediate overflow or underflow. + */ + public static double hypot(double x, double y, double z) { + if (USE_JDK_MATH) { + // No simple JDK equivalent. + } + x = Math.abs(x); + y = Math.abs(y); + z = Math.abs(z); + /* + * Considering that z magnitude is the most likely to be the smaller, + * hence ensuring z <= y <= x, and not x <= y <= z, for less swaps. + */ + // Ensuring z <= y. + if (z > y) { + // y < z: swapping y and z + double a = z; + z = y; + y = a; + } else if (!(z <= y)) { // Testing if y or z is NaN. + return hypot_NaN(x, y, z); + } + // Ensuring y <= x. + if (z > x) { + // x < z <= y: moving x + double oldZ = z; + z = x; + double oldY = y; + y = oldZ; + x = oldY; + } else if (y > x) { + // z <= x < y: swapping x and y + double a = y; + y = x; + x = a; + } else if (x != x) { // Testing if x is NaN. + return hypot_NaN(x, y, z); + } + + if (x-y == x) { + // y, hence z, too small to subtract from x. + return x; + } else if (y-z == y) { + // z too small to subtract from y, hence x. + double factor; + if (x > HYPOT_MAX_MAG) { + // x is too large: scaling down. + x *= (1/HYPOT_FACTOR); + y *= (1/HYPOT_FACTOR); + factor = HYPOT_FACTOR; + } else if (y < (1/HYPOT_MAX_MAG)) { + // y is too small: scaling up. + x *= HYPOT_FACTOR; + y *= HYPOT_FACTOR; + factor = (1/HYPOT_FACTOR); + } else { + factor = 1.0; + } + return factor * sqrt(x*x+y*y); + } else { + double factor; + if (x > HYPOT_MAX_MAG) { + // x is too large: scaling down. + x *= (1/HYPOT_FACTOR); + y *= (1/HYPOT_FACTOR); + z *= (1/HYPOT_FACTOR); + factor = HYPOT_FACTOR; + } else if (z < (1/HYPOT_MAX_MAG)) { + // z is too small: scaling up. + x *= HYPOT_FACTOR; + y *= HYPOT_FACTOR; + z *= HYPOT_FACTOR; + factor = (1/HYPOT_FACTOR); + } else { + factor = 1.0; + } + // Adding smaller magnitudes together first. + return factor * sqrt(x*x+(y*y+z*z)); + } + } + + /* + * close values + */ + + /** + * @param value A float value. + * @return Floor of value. + */ + public static float floor(float value) { + final int exponent = getExponent(value); + if (exponent < 0) { + // abs(value) < 1. + if (value < 0.0f) { + return -1.0f; + } else { + // 0.0f, or -0.0f if value is -0.0f + return 0.0f * value; + } + } else if (exponent < 23) { + // A bit faster than using casts. + final int bits = Float.floatToRawIntBits(value); + final int anteCommaBits = bits & (0xFF800000>>exponent); + if ((value < 0.0f) && (anteCommaBits != bits)) { + return Float.intBitsToFloat(anteCommaBits) - 1.0f; + } else { + return Float.intBitsToFloat(anteCommaBits); + } + } else { + // +-Infinity, NaN, or a mathematical integer. + return value; + } + } + + /** + * @param value A double value. + * @return Floor of value. + */ + public static double floor(double value) { + if (USE_JDK_MATH) { + return StrictMath.floor(value); + } + if (ANTI_SLOW_CASTS) { + double valueAbs = Math.abs(value); + if (valueAbs <= (double)Integer.MAX_VALUE) { + if (value > 0.0) { + return (double)(int)value; + } else if (value < 0.0) { + double anteCommaDigits = (double)(int)value; + if (value != anteCommaDigits) { + return anteCommaDigits - 1.0; + } else { + return anteCommaDigits; + } + } else { // value is +-0.0 (not NaN due to test against Integer.MAX_VALUE) + return value; + } + } else if (valueAbs < TWO_POW_52) { + // We split the value in two: + // high part, which is a mathematical integer, + // and the rest, for which we can get rid of the + // post comma digits by casting into an int. + double highPart = ((int)(value * TWO_POW_N26)) * TWO_POW_26; + if (value > 0.0) { + return highPart + (double)((int)(value - highPart)); + } else { + double anteCommaDigits = highPart + (double)((int)(value - highPart)); + if (value != anteCommaDigits) { + return anteCommaDigits - 1.0; + } else { + return anteCommaDigits; + } + } + } else { // abs(value) >= 2^52, or value is NaN + return value; + } + } else { + final int exponent = getExponent(value); + if (exponent < 0) { + // abs(value) < 1. + if (value < 0.0) { + return -1.0; + } else { + // 0.0, or -0.0 if value is -0.0 + return 0.0 * value; + } + } else if (exponent < 52) { + // A bit faster than working on bits. + final long matIntPart = (long)value; + final double matIntToValue = value-(double)matIntPart; + if (matIntToValue >= 0.0) { + return (double)matIntPart; + } else { + return (double)(matIntPart - 1); + } + } else { + // +-Infinity, NaN, or a mathematical integer. + return value; + } + } + } + + /** + * @param value A float value. + * @return Ceiling of value. + */ + public static float ceil(float value) { + return -floor(-value); + } + + /** + * @param value A double value. + * @return Ceiling of value. + */ + public static double ceil(double value) { + if (USE_JDK_MATH) { + return StrictMath.ceil(value); + } + return -floor(-value); + } + + /** + * Might have different semantics than StrictMath.round(float), + * see bugs 6430675 and 8010430. + * + * @param value A double value. + * @return Value rounded to nearest int, choosing superior int in case two + * are equally close (i.e. rounding-up). + */ + public static int round(float value) { + /* + * Not delegating to JDK, because we want delegation to provide + * at least as good results, and some supported JDK versions + * have bugged round() methods. + */ + // Algorithm by Dmitry Nadezhin (but replaced an if by a multiply) + // (http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-August/020247.html). + final int bits = Float.floatToRawIntBits(value); + final int biasedExp = ((bits>>23)&0xFF); + // Shift to get rid of bits past comma except first one: will need to + // 1-shift to the right to end up with correct magnitude. + final int shift = (23 - 1 + MAX_FLOAT_EXPONENT) - biasedExp; + if ((shift & -32) == 0) { + int bitsSignum = (((bits >> 31) << 1) + 1); + // shift in [0,31], so unbiased exp in [-9,22]. + int extendedMantissa = (0x00800000 | (bits & 0x007FFFFF)) * bitsSignum; + // If value is positive and first bit past comma is 0, rounding + // to lower integer, else to upper one, which is what "+1" and + // then ">>1" do. + return ((extendedMantissa >> shift) + 1) >> 1; + } else { + // +-Infinity, NaN, or a mathematical integer, or tiny. + if (false && ANTI_SLOW_CASTS) { // not worth it + if (Math.abs(value) >= -(float)Integer.MIN_VALUE) { + // +-Infinity or a mathematical integer (mostly) out of int range. + return (value < 0.0) ? Integer.MIN_VALUE : Integer.MAX_VALUE; + } + // NaN or a mathematical integer (mostly) in int range. + } + return (int)value; + } + } + + /** + * Might have different semantics than StrictMath.round(double), + * see bugs 6430675 and 8010430. + * + * @param value A double value. + * @return Value rounded to nearest long, choosing superior long in case two + * are equally close (i.e. rounding-up). + */ + public static long round(double value) { + /* + * Not delegating to JDK, because we want delegation to provide + * at least as good results, and some supported JDK versions + * have bugged round() methods. + */ + final long bits = Double.doubleToRawLongBits(value); + final int biasedExp = (((int)(bits>>52))&0x7FF); + // Shift to get rid of bits past comma except first one: will need to + // 1-shift to the right to end up with correct magnitude. + final int shift = (52 - 1 + MAX_DOUBLE_EXPONENT) - biasedExp; + if ((shift & -64) == 0) { + long bitsSignum = (((bits >> 63) << 1) + 1); + // shift in [0,63], so unbiased exp in [-12,51]. + long extendedMantissa = (0x0010000000000000L | (bits & 0x000FFFFFFFFFFFFFL)) * bitsSignum; + // If value is positive and first bit past comma is 0, rounding + // to lower integer, else to upper one, which is what "+1" and + // then ">>1" do. + return ((extendedMantissa >> shift) + 1L) >> 1; + } else { + // +-Infinity, NaN, or a mathematical integer, or tiny. + if (ANTI_SLOW_CASTS) { + if (Math.abs(value) >= -(double)Long.MIN_VALUE) { + // +-Infinity or a mathematical integer (mostly) out of long range. + return (value < 0.0) ? Long.MIN_VALUE : Long.MAX_VALUE; + } + // NaN or a mathematical integer (mostly) in long range. + } + return (long)value; + } + } + + /** + * @param value A float value. + * @return Value rounded to nearest int, choosing even int in case two + * are equally close. + */ + public static int roundEven(float value) { + final int sign = signFromBit(value); + value = Math.abs(value); + if (ANTI_SLOW_CASTS) { + if (value < TWO_POW_23_F) { + // Getting rid of post-comma bits. + value = ((value + TWO_POW_23_F) - TWO_POW_23_F); + return sign * (int)value; + } else if (value < (float)Integer.MAX_VALUE) { // "<=" doesn't work, because of float precision + // value is in [-Integer.MAX_VALUE,Integer.MAX_VALUE] + return sign * (int)value; + } + } else { + if (value < TWO_POW_23_F) { + // Getting rid of post-comma bits. + value = ((value + TWO_POW_23_F) - TWO_POW_23_F); + } + } + return (int)(sign * value); + } + + /** + * @param value A double value. + * @return Value rounded to nearest long, choosing even long in case two + * are equally close. + */ + public static long roundEven(double value) { + final int sign = (int)signFromBit(value); + value = Math.abs(value); + if (value < TWO_POW_52) { + // Getting rid of post-comma bits. + value = ((value + TWO_POW_52) - TWO_POW_52); + } + if (ANTI_SLOW_CASTS) { + if (value <= (double)Integer.MAX_VALUE) { + // value is in [-Integer.MAX_VALUE,Integer.MAX_VALUE] + return sign * (int)value; + } + } + return (long)(sign * value); + } + + /** + * @param value A float value. + * @return The float mathematical integer closest to the specified value, + * choosing even one if two are equally close, or respectively + * NaN, +-Infinity or +-0.0f if the value is any of these. + */ + public static float rint(float value) { + final int sign = signFromBit(value); + value = Math.abs(value); + if (value < TWO_POW_23_F) { + // Getting rid of post-comma bits. + value = ((TWO_POW_23_F + value ) - TWO_POW_23_F); + } + // Restoring original sign. + return sign * value; + } + + /** + * @param value A double value. + * @return The double mathematical integer closest to the specified value, + * choosing even one if two are equally close, or respectively + * NaN, +-Infinity or +-0.0 if the value is any of these. + */ + public static double rint(double value) { + if (USE_JDK_MATH) { + return StrictMath.rint(value); + } + final int sign = (int)signFromBit(value); + value = Math.abs(value); + if (value < TWO_POW_52) { + // Getting rid of post-comma bits. + value = ((TWO_POW_52 + value ) - TWO_POW_52); + } + // Restoring original sign. + return sign * value; + } + + /* + * close int values + * + * Never delegating to JDK for these methods, for we should always + * be faster and exact, and JDK doesn't exactly have such methods. + */ + + /** + * @param value A double value. + * @return Floor of value as int, or closest int if floor is out + * of int range, or 0 if value is NaN. + */ + public static int floorToInt(double value) { + int valueInt = (int) value; + if (value < 0.0) { + if (value == (double) valueInt) { + return valueInt; + } else { + if (valueInt == Integer.MIN_VALUE) { + return valueInt; + } else { + return valueInt - 1; + } + } + } else { // >= 0 or NaN. + return valueInt; + } + } + + /** + * @param value A double value. + * @return Ceiling of value as int, or closest int if ceiling is out + * of int range, or 0 if value is NaN. + */ + public static int ceilToInt(double value) { + int valueInt = (int) value; + if (value > 0.0) { + if (value == (double) valueInt) { + return valueInt; + } else { + if (valueInt == Integer.MAX_VALUE) { + return valueInt; + } else { + return valueInt + 1; + } + } + } else { // <= 0 or NaN. + return valueInt; + } + } + + /** + * @param value A double value. + * @return Value rounded to nearest int, choosing superior int in case two + * are equally close (i.e. rounding-up). + */ + public static int roundToInt(double value) { + /* + * We don't gain much by reimplementing rounding, except for + * pathologically large values, which should not be a common case + * when dealing with ints, so we just use round(double). + */ + return NumbersUtils.toInt(round(value)); + } + + /** + * @param value A double value. + * @return Value rounded to nearest int, choosing even int in case two + * are equally close. + */ + public static int roundEvenToInt(double value) { + final int sign = (int)signFromBit(value); + value = Math.abs(value); + /* + * Applying the post-comma bits removal logic even if value is out + * of int range, to avoid a test, for it doesn't mess up the result, + * and we want to optimize for the case of values in int range. + */ + value = ((value + TWO_POW_52) - TWO_POW_52); + return (int)(sign * value); + } + + /* + * ranges + */ + + /** + * @param min A float value. + * @param max A float value. + * @param value A float value. + * @return min if value < min, max if value > max, value otherwise. + */ + public static float toRange(float min, float max, float value) { + return NumbersUtils.toRange(min, max, value); + } + + /** + * @param min A double value. + * @param max A double value. + * @param value A double value. + * @return min if value < min, max if value > max, value otherwise. + */ + public static double toRange(double min, double max, double value) { + return NumbersUtils.toRange(min, max, value); + } + + /* + * binary operators (/,%) + */ + + /** + * Returns dividend - divisor * n, where n is the mathematical integer + * closest to dividend/divisor. + * If dividend/divisor is equally close to surrounding integers, + * we choose n to be the integer of smallest magnitude, which makes + * this treatment differ from StrictMath.IEEEremainder(double,double), + * where n is chosen to be the even integer. + * Note that the choice of n is not done considering the double + * approximation of dividend/divisor, because it could cause + * result to be outside [-|divisor|/2,|divisor|/2] range. + * The practical effect is that if multiple results would be possible, + * we always choose the result that is the closest to (and has the same + * sign as) the dividend. + * Ex. : + * - for (-3.0,2.0), this method returns -1.0, + * whereas StrictMath.IEEEremainder returns 1.0. + * - for (-5.0,2.0), both this method and StrictMath.IEEEremainder return -1.0. + * + * If the remainder is zero, its sign is the same as the sign of the first argument. + * If either argument is NaN, or the first argument is infinite, + * or the second argument is positive zero or negative zero, + * then the result is NaN. + * If the first argument is finite and the second argument is + * infinite, then the result is the same as the first argument. + * + * NB: + * - Modulo operator (%) returns a value in ]-|divisor|,|divisor|[, + * which sign is the same as dividend. + * - As for modulo operator, the sign of the divisor has no effect on the result. + * - On some architecture, % operator has been observed to return NaN + * for some subnormal values of divisor, when dividend exponent is 1023, + * which impacts the correctness of this method. + * + * @param dividend Dividend. + * @param divisor Divisor. + * @return Remainder of dividend/divisor, i.e. a value in [-|divisor|/2,|divisor|/2]. + */ + public static double remainder(double dividend, double divisor) { + if (Double.isInfinite(divisor)) { + if (Double.isInfinite(dividend)) { + return Double.NaN; + } else { + return dividend; + } + } + double value = dividend % divisor; + if (Math.abs(value+value) > Math.abs(divisor)) { + return value + ((value > 0.0) ? -Math.abs(divisor) : Math.abs(divisor)); + } else { + return value; + } + } + + /** + * @param angle Angle in radians. + * @return The same angle, in radians, but in [-PI,PI]. + */ + public static double normalizeMinusPiPi(double angle) { + // Not modifying values in output range. + if ((angle >= -Math.PI) && (angle <= Math.PI)) { + return angle; + } + return remainderTwoPi(angle); + } + + /** + * Not accurate for large values. + * + * @param angle Angle in radians. + * @return The same angle, in radians, but in [-PI,PI]. + */ + public static double normalizeMinusPiPiFast(double angle) { + // Not modifying values in output range. + if ((angle >= -Math.PI) && (angle <= Math.PI)) { + return angle; + } + return remainderTwoPiFast(angle); + } + + /** + * @param angle Angle in radians. + * @return The same angle, in radians, but in [0,2*PI]. + */ + public static double normalizeZeroTwoPi(double angle) { + // Not modifying values in output range. + if ((angle >= 0.0) && (angle <= 2*Math.PI)) { + return angle; + } + angle = remainderTwoPi(angle); + if (angle < 0.0) { + // LO then HI is theoretically better (when starting near 0). + return (angle + TWOPI_LO) + TWOPI_HI; + } else { + return angle; + } + } + + /** + * Not accurate for large values. + * + * @param angle Angle in radians. + * @return The same angle, in radians, but in [0,2*PI]. + */ + public static double normalizeZeroTwoPiFast(double angle) { + // Not modifying values in output range. + if ((angle >= 0.0) && (angle <= 2*Math.PI)) { + return angle; + } + angle = remainderTwoPiFast(angle); + if (angle < 0.0) { + // LO then HI is theoretically better (when starting near 0). + return (angle + TWOPI_LO) + TWOPI_HI; + } else { + return angle; + } + } + + /** + * @param angle Angle in radians. + * @return Angle value modulo PI, in radians, in [-PI/2,PI/2]. + */ + public static double normalizeMinusHalfPiHalfPi(double angle) { + // Not modifying values in output range. + if ((angle >= -Math.PI/2) && (angle <= Math.PI/2)) { + return angle; + } + return remainderPi(angle); + } + + /** + * Not accurate for large values. + * + * @param angle Angle in radians. + * @return Angle value modulo PI, in radians, in [-PI/2,PI/2]. + */ + public static double normalizeMinusHalfPiHalfPiFast(double angle) { + // Not modifying values in output range. + if ((angle >= -Math.PI/2) && (angle <= Math.PI/2)) { + return angle; + } + return remainderPiFast(angle); + } + + /* + * floating points utils + */ + + /** + * @param value A float value. + * @return true if the specified value is NaN or +-Infinity, false otherwise. + */ + public static boolean isNaNOrInfinite(float value) { + return NumbersUtils.isNaNOrInfinite(value); + } + + /** + * @param value A double value. + * @return true if the specified value is NaN or +-Infinity, false otherwise. + */ + public static boolean isNaNOrInfinite(double value) { + return NumbersUtils.isNaNOrInfinite(value); + } + + /** + * @param value A float value. + * @return Value unbiased exponent. + */ + public static int getExponent(float value) { + return ((Float.floatToRawIntBits(value)>>23)&0xFF)-MAX_FLOAT_EXPONENT; + } + + /** + * @param value A double value. + * @return Value unbiased exponent. + */ + public static int getExponent(double value) { + return (((int)(Double.doubleToRawLongBits(value)>>52))&0x7FF)-MAX_DOUBLE_EXPONENT; + } + + /** + * @param value A float value. + * @return -1.0f if the specified value is < 0, 1.0f if it is > 0, + * and the value itself if it is NaN or +-0.0f. + */ + public static float signum(float value) { + if (USE_JDK_MATH) { + return StrictMath.signum(value); + } + if ((value == 0.0f) || (value != value)) { + return value; + } + return (float)signFromBit(value); + } + + /** + * @param value A double value. + * @return -1.0 if the specified value is < 0, 1.0 if it is > 0, + * and the value itself if it is NaN or +-0.0. + */ + public static double signum(double value) { + if (USE_JDK_MATH) { + return StrictMath.signum(value); + } + if ((value == 0.0) || (value != value)) { + return value; + } + if (ANTI_SLOW_CASTS) { + return (double)(int)signFromBit(value); + } else { + return (double)signFromBit(value); + } + } + + /** + * @param value A float value. + * @return -1 if sign bit is 1, 1 if sign bit is 0. + */ + public static int signFromBit(float value) { + return ((Float.floatToRawIntBits(value)>>30)|1); + } + + /** + * @param value A double value. + * @return -1 if sign bit is 1, 1 if sign bit is 0. + */ + public static long signFromBit(double value) { + // Returning a long, to avoid useless cast into int. + return ((Double.doubleToRawLongBits(value)>>62)|1); + } + + /** + * A sign of NaN is interpreted as positive. + * + * @param magnitude A float value. + * @param sign A float value. + * @return A value with the magnitude of the first argument, and the sign + * of the second argument. + */ + public static float copySign(float magnitude, float sign) { + return Float.intBitsToFloat( + (Float.floatToRawIntBits((sign != sign) ? 1.0f : sign) & Integer.MIN_VALUE) + | (Float.floatToRawIntBits(magnitude) & Integer.MAX_VALUE)); + } + + /** + * A sign of NaN is interpreted as positive. + * + * @param magnitude A double value. + * @param sign A double value. + * @return A value with the magnitude of the first argument, and the sign + * of the second argument. + */ + public static double copySign(double magnitude, double sign) { + return Double.longBitsToDouble( + (Double.doubleToRawLongBits((sign != sign) ? 1.0 : sign) & Long.MIN_VALUE) + | (Double.doubleToRawLongBits(magnitude) & Long.MAX_VALUE)); + } + + /** + * The ULP (Unit in the Last Place) is the distance to the next value larger + * in magnitude. + * + * @param value A float value. + * @return The size of an ulp of the specified value, or Float.MIN_VALUE + * if it is +-0.0f, or +Infinity if it is +-Infinity, or NaN + * if it is NaN. + */ + public static float ulp(float value) { + if (USE_JDK_MATH) { + return StrictMath.ulp(value); + } + /* + * Look-up table not really worth it in micro-benchmark, + * so should be worse with cache-misses. + */ + final int exponent = getExponent(value); + if (exponent >= (MIN_FLOAT_NORMAL_EXPONENT+23)) { + if (exponent == MAX_FLOAT_EXPONENT+1) { + // NaN or +-Infinity + return Math.abs(value); + } + // normal: returning 2^(exponent-23) + return Float.intBitsToFloat((exponent+(MAX_FLOAT_EXPONENT-23))<<23); + } else { + if (exponent == MIN_FLOAT_NORMAL_EXPONENT-1) { + // +-0.0f or subnormal + return Float.MIN_VALUE; + } + // subnormal result + return Float.intBitsToFloat(1<<(exponent-MIN_FLOAT_NORMAL_EXPONENT)); + } + } + + /** + * The ULP (Unit in the Last Place) is the distance to the next value larger + * in magnitude. + * + * @param value A double value. + * @return The size of an ulp of the specified value, or Double.MIN_VALUE + * if it is +-0.0, or +Infinity if it is +-Infinity, or NaN + * if it is NaN. + */ + public static double ulp(double value) { + if (USE_JDK_MATH) { + return StrictMath.ulp(value); + } + /* + * Look-up table not really worth it in micro-benchmark, + * so should be worse with cache-misses. + */ + final int exponent = getExponent(value); + if (exponent >= (MIN_DOUBLE_NORMAL_EXPONENT+52)) { + if (exponent == MAX_DOUBLE_EXPONENT+1) { + // NaN or +-Infinity + return Math.abs(value); + } + // normal: returning 2^(exponent-52) + return Double.longBitsToDouble((exponent+(MAX_DOUBLE_EXPONENT-52L))<<52); + } else { + if (exponent == MIN_DOUBLE_NORMAL_EXPONENT-1) { + // +-0.0f or subnormal + return Double.MIN_VALUE; + } + // subnormal result + return Double.longBitsToDouble(1L<<(exponent-MIN_DOUBLE_NORMAL_EXPONENT)); + } + } + + /** + * If both arguments are +-0.0(f), (float)direction is returned. + * + * If both arguments are +Infinity or -Infinity, + * respectively +Infinity or -Infinity is returned. + * + * @param start A float value. + * @param direction A double value. + * @return The float adjacent to start towards direction, considering that + * +(-)Float.MIN_VALUE is adjacent to +(-)0.0f, and that + * +(-)Float.MAX_VALUE is adjacent to +(-)Infinity, + * or NaN if any argument is NaN. + */ + public static float nextAfter(float start, double direction) { + if (direction < start) { + // Going towards -Infinity. + if (start == 0.0f) { + // +-0.0f + return -Float.MIN_VALUE; + } + final int bits = Float.floatToRawIntBits(start); + return Float.intBitsToFloat(bits + ((bits > 0) ? -1 : 1)); + } else if (direction > start) { + // Going towards +Infinity. + // +0.0f to get rid of eventual -0.0f + final int bits = Float.floatToRawIntBits(start + 0.0f); + return Float.intBitsToFloat(bits + (bits >= 0 ? 1 : -1)); + } else if (start == direction) { + return (float)direction; + } else { + // Returning a NaN derived from the input NaN(s). + return start + (float)direction; + } + } + + /** + * If both arguments are +-0.0, direction is returned. + * + * If both arguments are +Infinity or -Infinity, + * respectively +Infinity or -Infinity is returned. + * + * @param start A double value. + * @param direction A double value. + * @return The double adjacent to start towards direction, considering that + * +(-)Double.MIN_VALUE is adjacent to +(-)0.0, and that + * +(-)Double.MAX_VALUE is adjacent to +(-)Infinity, + * or NaN if any argument is NaN. + */ + public static double nextAfter(double start, double direction) { + if (direction < start) { + // Going towards -Infinity. + if (start == 0.0) { + // +-0.0 + return -Double.MIN_VALUE; + } + final long bits = Double.doubleToRawLongBits(start); + return Double.longBitsToDouble(bits + ((bits > 0) ? -1 : 1)); + } else if (direction > start) { + // Going towards +Infinity. + // +0.0 to get rid of eventual -0.0 + final long bits = Double.doubleToRawLongBits(start + 0.0f); + return Double.longBitsToDouble(bits + (bits >= 0 ? 1 : -1)); + } else if (start == direction) { + return direction; + } else { + // Returning a NaN derived from the input NaN(s). + return start + direction; + } + } + + /** + * Semantically equivalent to nextAfter(start,Double.NEGATIVE_INFINITY). + */ + public static float nextDown(float start) { + if (start > Float.NEGATIVE_INFINITY) { + if (start == 0.0f) { + // +-0.0f + return -Float.MIN_VALUE; + } + final int bits = Float.floatToRawIntBits(start); + return Float.intBitsToFloat(bits + ((bits > 0) ? -1 : 1)); + } else if (start == Float.NEGATIVE_INFINITY) { + return Float.NEGATIVE_INFINITY; + } else { + // NaN + return start; + } + } + + /** + * Semantically equivalent to nextAfter(start,Double.NEGATIVE_INFINITY). + */ + public static double nextDown(double start) { + if (start > Double.NEGATIVE_INFINITY) { + if (start == 0.0) { + // +-0.0 + return -Double.MIN_VALUE; + } + final long bits = Double.doubleToRawLongBits(start); + return Double.longBitsToDouble(bits + ((bits > 0) ? -1 : 1)); + } else if (start == Double.NEGATIVE_INFINITY) { + return Double.NEGATIVE_INFINITY; + } else { + // NaN + return start; + } + } + + /** + * Semantically equivalent to nextAfter(start,Double.POSITIVE_INFINITY). + */ + public static float nextUp(float start) { + if (start < Float.POSITIVE_INFINITY) { + // +0.0f to get rid of eventual -0.0f + final int bits = Float.floatToRawIntBits(start + 0.0f); + return Float.intBitsToFloat(bits + (bits >= 0 ? 1 : -1)); + } else if (start == Float.POSITIVE_INFINITY) { + return Float.POSITIVE_INFINITY; + } else { + // NaN + return start; + } + } + + /** + * Semantically equivalent to nextAfter(start,Double.POSITIVE_INFINITY). + */ + public static double nextUp(double start) { + if (start < Double.POSITIVE_INFINITY) { + // +0.0 to get rid of eventual -0.0 + final long bits = Double.doubleToRawLongBits(start + 0.0); + return Double.longBitsToDouble(bits + (bits >= 0 ? 1 : -1)); + } else if (start == Double.POSITIVE_INFINITY) { + return Double.POSITIVE_INFINITY; + } else { + // NaN + return start; + } + } + + /** + * Precision may be lost if the result is subnormal. + * + * @param value A float value. + * @param scaleFactor An int value. + * @return value * 2^scaleFactor, or a value equivalent to the specified + * one if it is NaN, +-Infinity or +-0.0f. + */ + public static float scalb(float value, int scaleFactor) { + // Large enough to imply overflow or underflow for + // a finite non-zero value. + final int MAX_SCALE = 2*MAX_FLOAT_EXPONENT+23+1; + + // Making sure scaling factor is in a reasonable range. + scaleFactor = Math.max(Math.min(scaleFactor, MAX_SCALE), -MAX_SCALE); + + return (float)(((double)value) * twoPowNormal(scaleFactor)); + } + + /** + * Precision may be lost if the result is subnormal. + * + * @param value A double value. + * @param scaleFactor An int value. + * @return value * 2^scaleFactor, or a value equivalent to the specified + * one if it is NaN, +-Infinity or +-0.0. + */ + public static double scalb(double value, int scaleFactor) { + if ((scaleFactor > -MAX_DOUBLE_EXPONENT) && (scaleFactor <= MAX_DOUBLE_EXPONENT)) { + // Quick case (as done in apache FastMath). + return value * twoPowNormal(scaleFactor); + } + + // Large enough to imply overflow or underflow for + // a finite non-zero value. + final int MAX_SCALE = 2*MAX_DOUBLE_EXPONENT+52+1; + + // Making sure scaling factor is in a reasonable range. + final int exponentAdjust; + final int scaleIncrement; + final double exponentDelta; + if (scaleFactor < 0) { + scaleFactor = Math.max(scaleFactor, -MAX_SCALE); + scaleIncrement = -512; + exponentDelta = TWO_POW_N512; + } else { + scaleFactor = Math.min(scaleFactor, MAX_SCALE); + scaleIncrement = 512; + exponentDelta = TWO_POW_512; + } + + // Calculating (scaleFactor % +-512), 512 = 2^9, using + // technique from "Hacker's Delight" section 10-2. + final int t = ((scaleFactor >> (9-1)) >>> (32-9)); + exponentAdjust = ((scaleFactor + t) & (512-1)) - t; + + value *= twoPowNormal(exponentAdjust); + scaleFactor -= exponentAdjust; + + while (scaleFactor != 0) { + value *= exponentDelta; + scaleFactor -= scaleIncrement; + } + + return value; + } + + /* + * Non-redefined StrictMath public values and treatments. + */ + + public static float abs(float a) { + return StrictMath.abs(a); + } + + public static double abs(double a) { + return StrictMath.abs(a); + } + + public static float min(float a, float b) { + return StrictMath.min(a,b); + } + + public static double min(double a, double b) { + return StrictMath.min(a,b); + } + + public static float max(float a, float b) { + return StrictMath.max(a,b); + } + + public static double max(double a, double b) { + return StrictMath.max(a,b); + } + + public static double IEEEremainder(double f1, double f2) { + return StrictMath.IEEEremainder(f1,f2); + } + + public static double random() { + return StrictMath.random(); + } + + //-------------------------------------------------------------------------- + // PRIVATE METHODS + //-------------------------------------------------------------------------- + + /** + * Non-instantiable. + */ + private StrictFastMath() { + } + + /* + * Remainders (accurate). + */ + + /** + * @param angle Angle in radians. + * @return Remainder of (angle % (2*PI)), in [-PI,PI]. + */ + private static double remainderTwoPi(double angle) { + if (USE_JDK_MATH) { + return jdkRemainderTwoPi(angle); + } + boolean negateResult = false; + if (angle < 0.0) { + angle = -angle; + negateResult = true; + } + if (angle <= (4*NORMALIZE_ANGLE_MAX_MEDIUM_DOUBLE_PIO2)) { + double fn = (double)(int)(angle*TWOPI_INV+0.5); + angle = (angle - fn*TWOPI_HI) - fn*TWOPI_LO; + // Ensuring range. + // HI/LO can help a bit, even though we are always far from 0. + if (angle < -Math.PI) { + angle = (angle + TWOPI_HI) + TWOPI_LO; + } else if (angle > Math.PI) { + angle = (angle - TWOPI_HI) - TWOPI_LO; + } + return negateResult ? -angle : angle; + } else if (angle < Double.POSITIVE_INFINITY) { + angle = heavyRemainderTwoPi(angle); + return negateResult ? -angle : angle; + } else { // angle is +Infinity or NaN + return Double.NaN; + } + } + + /** + * @param angle Angle in radians. + * @return Remainder of (angle % PI), in [-PI/2,PI/2]. + */ + private static double remainderPi(double angle) { + if (USE_JDK_MATH) { + return jdkRemainderPi(angle); + } + boolean negateResult = false; + if (angle < 0.0) { + angle = -angle; + negateResult = true; + } + if (angle <= (2*NORMALIZE_ANGLE_MAX_MEDIUM_DOUBLE_PIO2)) { + double fn = (double)(int)(angle*PI_INV+0.5); + angle = (angle - fn*PI_HI) - fn*PI_LO; + // Ensuring range. + // HI/LO can help a bit, even though we are always far from 0. + if (angle < -Math.PI/2) { + angle = (angle + PI_HI) + PI_LO; + } else if (angle > Math.PI/2) { + angle = (angle - PI_HI) - PI_LO; + } + return negateResult ? -angle : angle; + } else if (angle < Double.POSITIVE_INFINITY) { + angle = heavyRemainderPi(angle); + return negateResult ? -angle : angle; + } else { // angle is +Infinity or NaN + return Double.NaN; + } + } + + /** + * @param angle Angle in radians. + * @return Bits of double corresponding to remainder of (angle % (PI/2)), + * in [-PI/4,PI/4], with quadrant encoded in exponent bits. + */ + private static long remainderPiO2(double angle) { + if (USE_JDK_MATH) { + return jdkRemainderPiO2(angle, false); + } + boolean negateResult = false; + if (angle < 0.0) { + angle = -angle; + negateResult = true; + } + if (angle <= NORMALIZE_ANGLE_MAX_MEDIUM_DOUBLE_PIO2) { + int n = (int)(angle*PIO2_INV+0.5); + double fn = (double)n; + angle = (angle - fn*PIO2_HI) - fn*PIO2_LO; + // Ensuring range. + // HI/LO can help a bit, even though we are always far from 0. + if (angle < -Math.PI/4) { + angle = (angle + PIO2_HI) + PIO2_LO; + n--; + } else if (angle > Math.PI/4) { + angle = (angle - PIO2_HI) - PIO2_LO; + n++; + } + if (negateResult) { + angle = -angle; + } + return encodeRemainderAndQuadrant(angle, n&3); + } else if (angle < Double.POSITIVE_INFINITY) { + return heavyRemainderPiO2(angle, negateResult); + } else { // angle is +Infinity or NaN + return encodeRemainderAndQuadrant(Double.NaN, 0); + } + } + + /* + * Remainders (fast). + */ + + /** + * Not accurate for large values. + * + * @param angle Angle in radians. + * @return Remainder of (angle % (2*PI)), in [-PI,PI]. + */ + private static double remainderTwoPiFast(double angle) { + if (USE_JDK_MATH) { + return jdkRemainderTwoPi(angle); + } + boolean negateResult = false; + if (angle < 0.0) { + angle = -angle; + negateResult = true; + } + // - We don't bother with values higher than (2*PI*(2^52)), + // since they are spaced by 2*PI or more from each other. + // - For large values, we don't use % because it might be very slow, + // and we split computation in two, because cast from double to int + // with large numbers might be very slow also. + if (angle <= TWO_POW_26*(2*Math.PI)) { + // ok + } else if (angle <= TWO_POW_52*(2*Math.PI)) { + // Computing remainder of angle modulo TWO_POW_26*(2*PI). + double fn = (double)(int)(angle*(TWOPI_INV/TWO_POW_26)+0.5); + angle = (angle - fn*(TWOPI_HI*TWO_POW_26)) - fn*(TWOPI_LO*TWO_POW_26); + // Here, angle is in [-TWO_POW_26*PI,TWO_POW_26*PI], or so. + if (angle < 0.0) { + angle = -angle; + negateResult = !negateResult; + } + } else if (angle < Double.POSITIVE_INFINITY) { + return 0.0; + } else { // angle is +Infinity or NaN + return Double.NaN; + } + + // Computing remainder of angle modulo 2*PI. + double fn = (double)(int)(angle*TWOPI_INV+0.5); + angle = (angle - fn*TWOPI_HI) - fn*TWOPI_LO; + + // Ensuring range. + // HI/LO can help a bit, even though we are always far from 0. + if (angle < -Math.PI) { + angle = (angle + TWOPI_HI) + TWOPI_LO; + } else if (angle > Math.PI) { + angle = (angle - TWOPI_HI) - TWOPI_LO; + } + return negateResult ? -angle : angle; + } + + /** + * Not accurate for large values. + * + * @param angle Angle in radians. + * @return Remainder of (angle % PI), in [-PI/2,PI/2]. + */ + private static double remainderPiFast(double angle) { + if (USE_JDK_MATH) { + return jdkRemainderPi(angle); + } + boolean negateResult = false; + if (angle < 0.0) { + angle = -angle; + negateResult = true; + } + // - We don't bother with values higher than (PI*(2^52)), + // since they are spaced by PI or more from each other. + // - For large values, we don't use % because it might be very slow, + // and we split computation in two, because cast from double to int + // with large numbers might be very slow also. + if (angle <= TWO_POW_26*Math.PI) { + // ok + } else if (angle <= TWO_POW_52*Math.PI) { + // Computing remainder of angle modulo TWO_POW_26*PI. + double fn = (double)(int)(angle*(PI_INV/TWO_POW_26)+0.5); + angle = (angle - fn*(PI_HI*TWO_POW_26)) - fn*(PI_LO*TWO_POW_26); + // Here, angle is in [-TWO_POW_26*PI/2,TWO_POW_26*PI/2], or so. + if (angle < 0.0) { + angle = -angle; + negateResult = !negateResult; + } + } else if (angle < Double.POSITIVE_INFINITY) { + return 0.0; + } else { // angle is +Infinity or NaN + return Double.NaN; + } + + // Computing remainder of angle modulo PI. + double fn = (double)(int)(angle*PI_INV+0.5); + angle = (angle - fn*PI_HI) - fn*PI_LO; + + // Ensuring range. + // HI/LO can help a bit, even though we are always far from 0. + if (angle < -Math.PI/2) { + angle = (angle + PI_HI) + PI_LO; + } else if (angle > Math.PI/2) { + angle = (angle - PI_HI) - PI_LO; + } + return negateResult ? -angle : angle; + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 661afa67c..15a6a176c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -43,6 +43,7 @@ include( ":kmath-tensors", ":kmath-jupyter", ":kmath-symja", + ":kmath-jafama", ":examples", ":benchmarks" )