From 2dff15c5aa2afe2ce99edfe8ae0fe4dc1db64087 Mon Sep 17 00:00:00 2001 From: therealansh Date: Thu, 3 Jun 2021 01:14:19 +0530 Subject: [PATCH 1/7] integrate: Jafama + KMath --- kmath-jafama/build.gradle.kts | 21 + .../kscience/kmath/jafama/CmnFastMath.java | 2112 ++++++++++++ .../kscience/kmath/jafama/DoubleWrapper.java | 13 + .../space/kscience/kmath/jafama/FastMath.java | 2986 ++++++++++++++++ .../kscience/kmath/jafama/IntWrapper.java | 13 + .../kscience/kmath/jafama/KMathJafama.kt | 102 + .../kscience/kmath/jafama/NumbersUtils.java | 2647 +++++++++++++++ .../kscience/kmath/jafama/StrictFastMath.java | 2998 +++++++++++++++++ settings.gradle.kts | 1 + 9 files changed, 10893 insertions(+) create mode 100644 kmath-jafama/build.gradle.kts create mode 100644 kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/CmnFastMath.java create mode 100644 kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/DoubleWrapper.java create mode 100644 kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/FastMath.java create mode 100644 kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/IntWrapper.java create mode 100644 kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/KMathJafama.kt create mode 100644 kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/NumbersUtils.java create mode 100644 kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/StrictFastMath.java 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" ) -- 2.34.1 From 6c815abd54a248cd1f95e02d580382e4b55377ba Mon Sep 17 00:00:00 2001 From: therealansh Date: Thu, 3 Jun 2021 01:37:23 +0530 Subject: [PATCH 2/7] chore: refactored and dependency add --- kmath-jafama/build.gradle.kts | 9 +- .../kscience/kmath/jafama/CmnFastMath.java | 2112 ------------ .../kscience/kmath/jafama/DoubleWrapper.java | 13 - .../space/kscience/kmath/jafama/FastMath.java | 2986 ---------------- .../kscience/kmath/jafama/IntWrapper.java | 13 - .../kscience/kmath/jafama/KMathJafama.kt | 3 +- .../kscience/kmath/jafama/NumbersUtils.java | 2647 --------------- .../kscience/kmath/jafama/StrictFastMath.java | 2998 ----------------- 8 files changed, 6 insertions(+), 10775 deletions(-) delete mode 100644 kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/CmnFastMath.java delete mode 100644 kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/DoubleWrapper.java delete mode 100644 kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/FastMath.java delete mode 100644 kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/IntWrapper.java delete mode 100644 kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/NumbersUtils.java delete mode 100644 kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/StrictFastMath.java diff --git a/kmath-jafama/build.gradle.kts b/kmath-jafama/build.gradle.kts index 22d50f89c..f31f2602f 100644 --- a/kmath-jafama/build.gradle.kts +++ b/kmath-jafama/build.gradle.kts @@ -3,13 +3,12 @@ plugins { } dependencies { - api(project(":kmath-ast")) - api(project(":kmath-complex")) - api(project(":kmath-for-real")) + api(project(":kmath-core")) + api("net.jafama:jafama:2.3.2") } -kscience{ - useHtml() +repositories { + mavenCentral() } readme { 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 deleted file mode 100644 index 0abc5d95d..000000000 --- a/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/CmnFastMath.java +++ /dev/null @@ -1,2112 +0,0 @@ -/* - * 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 deleted file mode 100644 index e7adc8d59..000000000 --- a/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/DoubleWrapper.java +++ /dev/null @@ -1,13 +0,0 @@ -/* - * 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 deleted file mode 100644 index a83c01f7b..000000000 --- a/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/FastMath.java +++ /dev/null @@ -1,2986 +0,0 @@ -/* - * 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 deleted file mode 100644 index 812e5a22d..000000000 --- a/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/IntWrapper.java +++ /dev/null @@ -1,13 +0,0 @@ -/* - * 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 index 2b6cc3a5a..2557004e9 100644 --- a/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/KMathJafama.kt +++ b/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/KMathJafama.kt @@ -1,6 +1,7 @@ package space.kscience.kmath.jafama import space.kscience.kmath.operations.* +import net.jafama.* /** * Advanced Number-like semifield that implements basic operations. @@ -55,7 +56,7 @@ public interface ExtendedField : ExtendedFieldOperations, Field, Numeri * 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 object JafamaDoubleField : ExtendedField, Norm, ScaleOperations { public override inline val zero: Double get() = 0.0 public override inline val one: Double get() = 1.0 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 deleted file mode 100644 index 6fdd9dea5..000000000 --- a/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/NumbersUtils.java +++ /dev/null @@ -1,2647 +0,0 @@ -/* - * 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 deleted file mode 100644 index a61ac9772..000000000 --- a/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/StrictFastMath.java +++ /dev/null @@ -1,2998 +0,0 @@ -/* - * 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; - } -} -- 2.34.1 From abb3d9fe5024f31d72354a38efeb3582b9357f46 Mon Sep 17 00:00:00 2001 From: therealansh Date: Thu, 3 Jun 2021 10:17:13 +0530 Subject: [PATCH 3/7] add: Double using StrictMath --- .../kscience/kmath/jafama/KMathJafama.kt | 103 +++++++++--------- 1 file changed, 53 insertions(+), 50 deletions(-) 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 index 2557004e9..9d4341036 100644 --- a/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/KMathJafama.kt +++ b/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/KMathJafama.kt @@ -4,56 +4,7 @@ import space.kscience.kmath.operations.* import net.jafama.* /** - * 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. + * A field for [Double] (using FastMath) without boxing. Does not produce appropriate field element. */ @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") public object JafamaDoubleField : ExtendedField, Norm, ScaleOperations { @@ -101,3 +52,55 @@ public object JafamaDoubleField : ExtendedField, Norm, S public override inline fun Double.times(b: Double): Double = this * b public override inline fun Double.div(b: Double): Double = this / b } + +/** + * A field for [Double] (using StrictMath) without boxing. Does not produce appropriate field element. + */ +@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") +public object StrictJafamaDoubleField : 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 = StrictFastMath.sin(arg) + public override inline fun cos(arg: Double): Double = StrictFastMath.cos(arg) + public override inline fun tan(arg: Double): Double = StrictFastMath.tan(arg) + public override inline fun acos(arg: Double): Double = StrictFastMath.acos(arg) + public override inline fun asin(arg: Double): Double = StrictFastMath.asin(arg) + public override inline fun atan(arg: Double): Double = StrictFastMath.atan(arg) + + public override inline fun sinh(arg: Double): Double = StrictFastMath.sinh(arg) + public override inline fun cosh(arg: Double): Double = StrictFastMath.cosh(arg) + public override inline fun tanh(arg: Double): Double = StrictFastMath.tanh(arg) + public override inline fun asinh(arg: Double): Double = StrictFastMath.asinh(arg) + public override inline fun acosh(arg: Double): Double = StrictFastMath.acosh(arg) + public override inline fun atanh(arg: Double): Double = StrictFastMath.atanh(arg) + + public override inline fun power(arg: Double, pow: Number): Double = StrictFastMath.pow(arg, pow.toDouble()) + public override inline fun exp(arg: Double): Double = StrictFastMath.exp(arg) + public override inline fun ln(arg: Double): Double = StrictFastMath.log(arg) + + public override inline fun norm(arg: Double): Double = StrictFastMath.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 +} + + -- 2.34.1 From 590910ac2dcb0028046442ad0b740328eaecd356 Mon Sep 17 00:00:00 2001 From: therealansh Date: Sat, 5 Jun 2021 11:14:02 +0530 Subject: [PATCH 4/7] add: example and Readme --- README.md | 6 +++++ examples/build.gradle.kts | 2 ++ .../kscience/kmath/jafama/KMathaJafamaDemo.kt | 17 +++++++++++++ kmath-jafama/README.md | 24 +++++++++++++++++++ 4 files changed, 49 insertions(+) create mode 100644 examples/src/main/kotlin/space/kscience/kmath/jafama/KMathaJafamaDemo.kt create mode 100644 kmath-jafama/README.md diff --git a/README.md b/README.md index 7c6f7ea2b..90ab524e2 100644 --- a/README.md +++ b/README.md @@ -200,6 +200,12 @@ One can still use generic algebras though. > **Maturity**: PROTOTYPE
+* ### [kmath-jafama](kmath-jafama) +> +> +> **Maturity**: PROTOTYPE +
+ * ### [kmath-jupyter](kmath-jupyter) > > diff --git a/examples/build.gradle.kts b/examples/build.gradle.kts index 568bde153..406b8f470 100644 --- a/examples/build.gradle.kts +++ b/examples/build.gradle.kts @@ -44,6 +44,8 @@ dependencies { implementation("org.slf4j:slf4j-simple:1.7.30") // plotting implementation("space.kscience:plotlykt-server:0.4.0") + //jafama + implementation(project(":kmath-jafama")) } kotlin.sourceSets.all { diff --git a/examples/src/main/kotlin/space/kscience/kmath/jafama/KMathaJafamaDemo.kt b/examples/src/main/kotlin/space/kscience/kmath/jafama/KMathaJafamaDemo.kt new file mode 100644 index 000000000..879aab08f --- /dev/null +++ b/examples/src/main/kotlin/space/kscience/kmath/jafama/KMathaJafamaDemo.kt @@ -0,0 +1,17 @@ +/* + * 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 + +import net.jafama.FastMath + + +fun main(){ + val a = JafamaDoubleField.number(2.0) + val b = StrictJafamaDoubleField.power(FastMath.E,a) + + println(JafamaDoubleField.add(b,a)) + println(StrictJafamaDoubleField.ln(b)) +} diff --git a/kmath-jafama/README.md b/kmath-jafama/README.md new file mode 100644 index 000000000..fab777bf4 --- /dev/null +++ b/kmath-jafama/README.md @@ -0,0 +1,24 @@ +# Module kmath-jafama + +Jafama based implementation of DoubleField of kmath-operations. + +- JafamaDoubleField : DoubleField implementation using FastMath +- StrictJafamaDoubleField - DoubleField implementation using StrictFastMath + +## Examples + +Different Operations on DoubleField + +```kotlin +package space.kscience.kmath.jafama + +import net.jafama.FastMath + + +fun main(){ + val a = JafamaDoubleField.number(2.0) + val b = StrictJafamaDoubleField.power(FastMath.E,a) + println(JafamaDoubleField.add(b,a)) + println(StrictJafamaDoubleField.ln(b)) +} +``` -- 2.34.1 From 9a9ebc730453735c5b6c0364d9fdbed1e92b3bc3 Mon Sep 17 00:00:00 2001 From: therealansh Date: Tue, 8 Jun 2021 19:19:55 +0530 Subject: [PATCH 5/7] add: benchmarks for Jafama --- benchmarks/build.gradle.kts | 7 +- .../kmath/benchmarks/JafamaBenchmark.kt | 48 ++++++++ .../kscience/kmath/jafama/KMathJafama.kt | 104 +++++++++--------- 3 files changed, 106 insertions(+), 53 deletions(-) create mode 100644 benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/JafamaBenchmark.kt diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts index 98ffc5a96..182c182d9 100644 --- a/benchmarks/build.gradle.kts +++ b/benchmarks/build.gradle.kts @@ -30,6 +30,7 @@ kotlin { implementation(project(":kmath-stat")) implementation(project(":kmath-dimensions")) implementation(project(":kmath-for-real")) + implementation(project(":kmath-jafama")) implementation("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.3.0") } } @@ -42,7 +43,6 @@ kotlin { implementation(project(":kmath-kotlingrad")) implementation(project(":kmath-viktor")) implementation("org.nd4j:nd4j-native:1.0.0-beta7") - // uncomment if your system supports AVX2 // val os = System.getProperty("os.name") // @@ -95,6 +95,11 @@ benchmark { commonConfiguration() include("BigIntBenchmark") } + + configurations.register("jafamaDouble") { + commonConfiguration() + include("JafamaBenchmark") + } } // Fix kotlinx-benchmarks bug diff --git a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/JafamaBenchmark.kt b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/JafamaBenchmark.kt new file mode 100644 index 000000000..c828de82b --- /dev/null +++ b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/JafamaBenchmark.kt @@ -0,0 +1,48 @@ +/* + * 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.benchmarks + +import kotlinx.benchmark.Blackhole +import org.openjdk.jmh.annotations.Benchmark +import org.openjdk.jmh.annotations.Scope +import org.openjdk.jmh.annotations.State +import space.kscience.kmath.jafama.JafamaDoubleField +import space.kscience.kmath.jafama.StrictJafamaDoubleField +import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.invoke + + +@State(Scope.Benchmark) +internal class JafamaBenchmark { + @Benchmark + fun jafamaBench(blackhole: Blackhole) = invokeBenchmarks(jafama, blackhole) + + @Benchmark + fun coreBench(blackhole: Blackhole) = invokeBenchmarks(core,blackhole) + + @Benchmark + fun strictJafamaBench(blackhole: Blackhole) = invokeBenchmarks(strictJafama,blackhole) + + private fun invokeBenchmarks(expr: Double, blackhole: Blackhole) { + blackhole.consume(expr) + } + + private companion object { + private val x: Double = Double.MAX_VALUE + + private val jafama = JafamaDoubleField{ + x * power(x, 1_000_000) * exp(x) / cos(x) + } + + private val core = DoubleField { + x * power(x, 1_000_000) * exp(x) / cos(x) + } + + private val strictJafama = StrictJafamaDoubleField { + x * power(x, 1_000_000) * exp(x) / cos(x) + } + } +} 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 index 9d4341036..f37d8e7b3 100644 --- a/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/KMathJafama.kt +++ b/kmath-jafama/src/main/kotlin/space/kscience/kmath/jafama/KMathJafama.kt @@ -8,8 +8,8 @@ import net.jafama.* */ @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") public object JafamaDoubleField : ExtendedField, Norm, ScaleOperations { - public override inline val zero: Double get() = 0.0 - public override inline val one: Double get() = 1.0 + public override val zero: Double get() = 0.0 + public override val one: Double get() = 1.0 public override fun number(value: Number): Double = value.toDouble() @@ -19,38 +19,38 @@ public object JafamaDoubleField : ExtendedField, Norm, S else -> super.binaryOperationFunction(operation) } - public override inline fun add(a: Double, b: Double): Double = a + b + public override 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 multiply(a: Double, b: Double): Double = a * b + public override 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 fun sin(arg: Double): Double = FastMath.sin(arg) + public override fun cos(arg: Double): Double = FastMath.cos(arg) + public override fun tan(arg: Double): Double = FastMath.tan(arg) + public override fun acos(arg: Double): Double = FastMath.acos(arg) + public override fun asin(arg: Double): Double = FastMath.asin(arg) + public override 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 fun sinh(arg: Double): Double = FastMath.sinh(arg) + public override fun cosh(arg: Double): Double = FastMath.cosh(arg) + public override fun tanh(arg: Double): Double = FastMath.tanh(arg) + public override fun asinh(arg: Double): Double = FastMath.asinh(arg) + public override fun acosh(arg: Double): Double = FastMath.acosh(arg) + public override 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 fun power(arg: Double, pow: Number): Double = FastMath.pow(arg, pow.toDouble()) + public override fun exp(arg: Double): Double = FastMath.exp(arg) + public override fun ln(arg: Double): Double = FastMath.log(arg) - public override inline fun norm(arg: Double): Double = FastMath.abs(arg) + public override 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 + public override fun Double.unaryMinus(): Double = -this + public override fun Double.plus(b: Double): Double = this + b + public override fun Double.minus(b: Double): Double = this - b + public override fun Double.times(b: Double): Double = this * b + public override fun Double.div(b: Double): Double = this / b } /** @@ -58,8 +58,8 @@ public object JafamaDoubleField : ExtendedField, Norm, S */ @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") public object StrictJafamaDoubleField : ExtendedField, Norm, ScaleOperations { - public override inline val zero: Double get() = 0.0 - public override inline val one: Double get() = 1.0 + public override val zero: Double get() = 0.0 + public override val one: Double get() = 1.0 public override fun number(value: Number): Double = value.toDouble() @@ -69,38 +69,38 @@ public object StrictJafamaDoubleField : ExtendedField, Norm super.binaryOperationFunction(operation) } - public override inline fun add(a: Double, b: Double): Double = a + b + public override 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 multiply(a: Double, b: Double): Double = a * b + public override 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 = StrictFastMath.sin(arg) - public override inline fun cos(arg: Double): Double = StrictFastMath.cos(arg) - public override inline fun tan(arg: Double): Double = StrictFastMath.tan(arg) - public override inline fun acos(arg: Double): Double = StrictFastMath.acos(arg) - public override inline fun asin(arg: Double): Double = StrictFastMath.asin(arg) - public override inline fun atan(arg: Double): Double = StrictFastMath.atan(arg) + public override fun sin(arg: Double): Double = StrictFastMath.sin(arg) + public override fun cos(arg: Double): Double = StrictFastMath.cos(arg) + public override fun tan(arg: Double): Double = StrictFastMath.tan(arg) + public override fun acos(arg: Double): Double = StrictFastMath.acos(arg) + public override fun asin(arg: Double): Double = StrictFastMath.asin(arg) + public override fun atan(arg: Double): Double = StrictFastMath.atan(arg) - public override inline fun sinh(arg: Double): Double = StrictFastMath.sinh(arg) - public override inline fun cosh(arg: Double): Double = StrictFastMath.cosh(arg) - public override inline fun tanh(arg: Double): Double = StrictFastMath.tanh(arg) - public override inline fun asinh(arg: Double): Double = StrictFastMath.asinh(arg) - public override inline fun acosh(arg: Double): Double = StrictFastMath.acosh(arg) - public override inline fun atanh(arg: Double): Double = StrictFastMath.atanh(arg) + public override fun sinh(arg: Double): Double = StrictFastMath.sinh(arg) + public override fun cosh(arg: Double): Double = StrictFastMath.cosh(arg) + public override fun tanh(arg: Double): Double = StrictFastMath.tanh(arg) + public override fun asinh(arg: Double): Double = StrictFastMath.asinh(arg) + public override fun acosh(arg: Double): Double = StrictFastMath.acosh(arg) + public override fun atanh(arg: Double): Double = StrictFastMath.atanh(arg) - public override inline fun power(arg: Double, pow: Number): Double = StrictFastMath.pow(arg, pow.toDouble()) - public override inline fun exp(arg: Double): Double = StrictFastMath.exp(arg) - public override inline fun ln(arg: Double): Double = StrictFastMath.log(arg) + public override fun power(arg: Double, pow: Number): Double = StrictFastMath.pow(arg, pow.toDouble()) + public override fun exp(arg: Double): Double = StrictFastMath.exp(arg) + public override fun ln(arg: Double): Double = StrictFastMath.log(arg) - public override inline fun norm(arg: Double): Double = StrictFastMath.abs(arg) + public override fun norm(arg: Double): Double = StrictFastMath.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 + public override fun Double.unaryMinus(): Double = -this + public override fun Double.plus(b: Double): Double = this + b + public override fun Double.minus(b: Double): Double = this - b + public override fun Double.times(b: Double): Double = this * b + public override fun Double.div(b: Double): Double = this / b } -- 2.34.1 From b99d0b96d82289a738326b7b4a71df6064734519 Mon Sep 17 00:00:00 2001 From: therealansh Date: Tue, 8 Jun 2021 19:28:34 +0530 Subject: [PATCH 6/7] add:benchmarks to readme --- kmath-jafama/README.md | 44 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/kmath-jafama/README.md b/kmath-jafama/README.md index fab777bf4..bc5655a1e 100644 --- a/kmath-jafama/README.md +++ b/kmath-jafama/README.md @@ -22,3 +22,47 @@ fun main(){ println(StrictJafamaDoubleField.ln(b)) } ``` + +## Benchmarks +Comparing Operations on DoubleField and JafamaDoubleField +```bash +jvm: space.kscience.kmath.benchmarks.JafamaBenchmark.coreBench + +Warm-up 1: 242556635.528 ops/s +Iteration 1: 236249893.335 ops/s +Iteration 2: 294526940.968 ops/s +Iteration 3: 295973752.533 ops/s +Iteration 4: 296467676.763 ops/s +Iteration 5: 295929441.421 ops/s + +283829541.004 ±(99.9%) 102456604.440 ops/s [Average] + (min, avg, max) = (236249893.335, 283829541.004, 296467676.763), stdev = 26607654.808 + CI (99.9%): [181372936.564, 386286145.444] (assumes normal distribution) + +jvm: space.kscience.kmath.benchmarks.JafamaBenchmark.jafamaBench + +Warm-up 1: 234737640.196 ops/s +Iteration 1: 231689614.905 ops/s +Iteration 2: 296629612.909 ops/s +Iteration 3: 297456237.453 ops/s +Iteration 4: 296754794.513 ops/s +Iteration 5: 293722557.848 ops/s + +283250563.526 ±(99.9%) 111125582.233 ops/s [Average] + (min, avg, max) = (231689614.905, 283250563.526, 297456237.453), stdev = 28858960.811 + CI (99.9%): [172124981.293, 394376145.759] (assumes normal distribution) + +jvm: space.kscience.kmath.benchmarks.JafamaBenchmark.strictJafamaBench + +Warm-up 1: 234895701.589 ops/s +Iteration 1: 236061284.195 ops/s +Iteration 2: 296894799.416 ops/s +Iteration 3: 286852020.677 ops/s +Iteration 4: 284021863.614 ops/s +Iteration 5: 284404358.656 ops/s + +277646865.312 ±(99.9%) 91748868.927 ops/s [Average] + (min, avg, max) = (236061284.195, 277646865.312, 296894799.416), stdev = 23826889.899 + CI (99.9%): [185897996.385, 369395734.239] (assumes normal distribution) + +``` -- 2.34.1 From 03be37bb4f6843a8fb08b3c44256df4f6bbdb15f Mon Sep 17 00:00:00 2001 From: therealansh Date: Sat, 12 Jun 2021 10:00:36 +0530 Subject: [PATCH 7/7] add: kotlin math benchmarks --- .../kmath/benchmarks/JafamaBenchmark.kt | 8 +++ kmath-jafama/README.md | 68 +++++++++++-------- 2 files changed, 48 insertions(+), 28 deletions(-) diff --git a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/JafamaBenchmark.kt b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/JafamaBenchmark.kt index c828de82b..e998054fb 100644 --- a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/JafamaBenchmark.kt +++ b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/JafamaBenchmark.kt @@ -13,6 +13,9 @@ import space.kscience.kmath.jafama.JafamaDoubleField import space.kscience.kmath.jafama.StrictJafamaDoubleField import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.invoke +import kotlin.math.cos +import kotlin.math.exp +import kotlin.math.pow @State(Scope.Benchmark) @@ -26,6 +29,9 @@ internal class JafamaBenchmark { @Benchmark fun strictJafamaBench(blackhole: Blackhole) = invokeBenchmarks(strictJafama,blackhole) + @Benchmark + fun kotlinMathBench(blackhole: Blackhole) = invokeBenchmarks(kotlinMath, blackhole) + private fun invokeBenchmarks(expr: Double, blackhole: Blackhole) { blackhole.consume(expr) } @@ -37,6 +43,8 @@ internal class JafamaBenchmark { x * power(x, 1_000_000) * exp(x) / cos(x) } + private val kotlinMath = x * x.pow(1_000_000) * exp(x) / cos(x) + private val core = DoubleField { x * power(x, 1_000_000) * exp(x) / cos(x) } diff --git a/kmath-jafama/README.md b/kmath-jafama/README.md index bc5655a1e..d170f6591 100644 --- a/kmath-jafama/README.md +++ b/kmath-jafama/README.md @@ -28,41 +28,53 @@ Comparing Operations on DoubleField and JafamaDoubleField ```bash jvm: space.kscience.kmath.benchmarks.JafamaBenchmark.coreBench -Warm-up 1: 242556635.528 ops/s -Iteration 1: 236249893.335 ops/s -Iteration 2: 294526940.968 ops/s -Iteration 3: 295973752.533 ops/s -Iteration 4: 296467676.763 ops/s -Iteration 5: 295929441.421 ops/s +Warm-up 1: 384414358.081 ops/s +Iteration 1: 374827571.951 ops/s +Iteration 2: 479335182.332 ops/s +Iteration 3: 475483069.577 ops/s +Iteration 4: 478235949.414 ops/s +Iteration 5: 472256385.114 ops/s -283829541.004 ±(99.9%) 102456604.440 ops/s [Average] - (min, avg, max) = (236249893.335, 283829541.004, 296467676.763), stdev = 26607654.808 - CI (99.9%): [181372936.564, 386286145.444] (assumes normal distribution) +456027631.678 ±(99.9%) 175106815.384 ops/s [Average] + (min, avg, max) = (374827571.951, 456027631.678, 479335182.332), stdev = 45474683.880 + CI (99.9%): [280920816.294, 631134447.061] (assumes normal distribution) jvm: space.kscience.kmath.benchmarks.JafamaBenchmark.jafamaBench -Warm-up 1: 234737640.196 ops/s -Iteration 1: 231689614.905 ops/s -Iteration 2: 296629612.909 ops/s -Iteration 3: 297456237.453 ops/s -Iteration 4: 296754794.513 ops/s -Iteration 5: 293722557.848 ops/s +Warm-up 1: 359418665.041 ops/s +Iteration 1: 335704885.798 ops/s +Iteration 2: 427684801.542 ops/s +Iteration 3: 452193034.265 ops/s +Iteration 4: 433855064.931 ops/s +Iteration 5: 453863386.566 ops/s -283250563.526 ±(99.9%) 111125582.233 ops/s [Average] - (min, avg, max) = (231689614.905, 283250563.526, 297456237.453), stdev = 28858960.811 - CI (99.9%): [172124981.293, 394376145.759] (assumes normal distribution) +420660234.620 ±(99.9%) 188028426.875 ops/s [Average] + (min, avg, max) = (335704885.798, 420660234.620, 453863386.566), stdev = 48830385.349 + CI (99.9%): [232631807.746, 608688661.495] (assumes normal distribution) + +jvm: space.kscience.kmath.benchmarks.JafamaBenchmark.kotlinMathBench + +Warm-up 1: 371570418.113 ops/s +Iteration 1: 379281146.127 ops/s +Iteration 2: 465234403.109 ops/s +Iteration 3: 470621634.240 ops/s +Iteration 4: 467074553.006 ops/s +Iteration 5: 466424840.144 ops/s + +449727315.325 ±(99.9%) 151837475.500 ops/s [Average] + (min, avg, max) = (379281146.127, 449727315.325, 470621634.240), stdev = 39431710.207 + CI (99.9%): [297889839.825, 601564790.825] (assumes normal distribution) jvm: space.kscience.kmath.benchmarks.JafamaBenchmark.strictJafamaBench -Warm-up 1: 234895701.589 ops/s -Iteration 1: 236061284.195 ops/s -Iteration 2: 296894799.416 ops/s -Iteration 3: 286852020.677 ops/s -Iteration 4: 284021863.614 ops/s -Iteration 5: 284404358.656 ops/s - -277646865.312 ±(99.9%) 91748868.927 ops/s [Average] - (min, avg, max) = (236061284.195, 277646865.312, 296894799.416), stdev = 23826889.899 - CI (99.9%): [185897996.385, 369395734.239] (assumes normal distribution) +Warm-up 1: 371241281.065 ops/s +Iteration 1: 374490259.387 ops/s +Iteration 2: 464995837.424 ops/s +Iteration 3: 469788706.385 ops/s +Iteration 4: 469528470.682 ops/s +Iteration 5: 456727921.978 ops/s +447106239.171 ±(99.9%) 157629035.980 ops/s [Average] + (min, avg, max) = (374490259.387, 447106239.171, 469788706.385), stdev = 40935760.071 + CI (99.9%): [289477203.192, 604735275.151] (assumes normal distribution) ``` -- 2.34.1