
net.openhft.chronicle.core.Maths Maven / Gradle / Ivy
/*
* Copyright 2016-2020 chronicle.software
*
* https://chronicle.software
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.openhft.chronicle.core;
import net.openhft.chronicle.assertions.AssertUtil;
import net.openhft.chronicle.core.annotation.NonNegative;
import net.openhft.chronicle.core.util.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
public final class Maths {
// Suppresses default constructor, ensuring non-instantiability.
private Maths() {
}
/**
* Numbers larger than this are whole numbers due to representation error.
*/
private static final double WHOLE_NUMBER = 1L << 52;
private static final int M0 = 0x5bc80bad;
private static final int M1 = 0xea7585d7;
private static final long[] TENS = new long[19];
private static final long[] FIVES = new long[28];
private static final String OUT_OF_RANGE = " out of range";
static {
TENS[0] = FIVES[0] = 1;
for (int i = 1; i < TENS.length; i++)
TENS[i] = 10 * TENS[i - 1];
for (int i = 1; i < FIVES.length; i++)
FIVES[i] = 5 * FIVES[i - 1];
}
/**
* Performs a round which is accurate to within 1 ulp. i.e. for values very close to 0.5 it
* might be rounded up or down. This is a pragmatic choice for performance reasons as it is
* assumed you are not working on the edge of the precision of double.
*
* @param d value to round
* @param digits 0 to 18 digits of precision
* @return rounded value
*/
public static double roundN(double d, int digits) {
final long factor = roundingFactor(digits);
return Math.abs(d) < WHOLE_NUMBER / factor
? (double) (long) (d < 0 ? d * factor - 0.5 : d * factor + 0.5) / factor : d;
}
/**
* Performs a round which is accurate to within 1 ulp.
* The value 0.5 should round up however the value one ulp less might round up or down.
* This is a pragmatic choice for performance reasons as it is
* assumed you are not working on the edge of the precision of double.
*
* @param d value to round
* @return rounded value
*/
public static double roundNup(double d, int digits) {
if (d < 0)
return -roundNup(-d, digits);
final long factor = roundingFactor(digits);
if (d >= WHOLE_NUMBER / factor)
return d;
final double df = d * factor;
long ldf = (long) df;
final double residual = df - ldf + Math.ulp(d) * (factor * 0.983);
if (residual >= 0.5)
ldf++;
return ldf / (double) factor;
}
public static long roundingFactor(int digits) {
return tens(digits);
}
public static long roundingFactor(double digits) {
int iDigits = (int) digits;
long ten = tens(iDigits);
switch ((int) ((digits - iDigits) * 10 + 0.5)) {
case 0:
case 1:
case 2:
return ten;
case 3:
case 4:
case 5:
return 2 * ten;
case 6:
return 4 * ten;
case 7:
case 8:
return 5 * ten;
case 9:
return 8 * ten;
default:
return 10 * ten;
}
}
/**
* Rounds up the given double value to the nearest higher number,
* accurate to the specified number of decimal places.
*
* @param d The double value to be rounded up.
* @param digits The number of decimal places to which the value is to be rounded.
* @return The value rounded up to the nearest higher number.
*/
public static double ceilN(double d, int digits) {
final long factor = roundingFactor(digits);
double ulp = Math.ulp(d);
double ulp2 = ulp * factor;
return Math.abs(d) < (double) Long.MAX_VALUE / factor && ulp2 < 1
? Math.ceil((d - ulp) * factor) / factor : d;
}
/**
* Rounds down the given double value to the nearest lower number,
* accurate to the specified number of decimal places.
*
* @param d The double value to be rounded down.
* @param digits The number of decimal places to which the value is to be rounded.
* @return The value rounded down to the nearest lower number.
*/
public static double floorN(double d, int digits) {
final long factor = roundingFactor(digits);
double ulp = Math.ulp(d);
double ulp2 = ulp * factor;
return Math.abs(d) < (double) Long.MAX_VALUE / factor && ulp2 < 1
? Math.floor((d + ulp) * factor) / factor : d;
}
public static double roundN(double d, double digits) {
final long factor = roundingFactor(digits);
return Math.abs(d) < (double) Long.MAX_VALUE / factor
? (double) (long) (d < 0 ? d * factor - 0.5 : d * factor + 0.5) / factor : d;
}
/**
* Rounds up the given value to the nearest higher number with specified decimal places.
*
* @param d the value to ceil
* @param digits number of decimal places
* @return the ceiled value
*/
public static double ceilN(double d, double digits) {
final long factor = roundingFactor(digits + 8);
final long factor2 = roundingFactor(digits);
return Math.abs(d) < WHOLE_NUMBER / factor
? Math.ceil(Math.round(d * factor) / 1e8) / factor2 : d;
}
/**
* Rounds down the given value to the nearest lower number with specified decimal places.
*
* @param d the value to floor
* @param digits number of decimal places
* @return the floored value
*/
public static double floorN(double d, double digits) {
final long factor = roundingFactor(digits + 8);
final long factor2 = roundingFactor(digits);
return Math.abs(d) < WHOLE_NUMBER / factor
? Math.floor(Math.round(d * factor) / 1e8) / factor2 : d;
}
/**
* Performs a round which is accurate to within 1 ulp. i.e. for values very close to 0.5 it
* might be rounded up or down. This is a pragmatic choice for performance reasons as it is
* assumed you are not working on the edge of the precision of double.
*
* @param d value to round
* @return rounded value
*/
public static double round1(double d) {
if (d < 0)
return -round1(-d);
final double factor = 1e1;
if (!(d <= WHOLE_NUMBER / factor)) // to handle NaN
return d;
return (long) (d * factor + 0.5) / factor;
}
/**
* Performs a round which is accurate to within 1 ulp.
* The value 0.5 should round up however the value one ulp less might round up or down.
* This is a pragmatic choice for performance reasons as it is
* assumed you are not working on the edge of the precision of double.
*
* @param d value to round
* @return rounded value
*/
public static double round1up(double d) {
return round1(d);
}
/**
* Performs a round which is accurate to within 1 ulp. i.e. for values very close to 0.5 it
* might be rounded up or down. This is a pragmatic choice for performance reasons as it is
* assumed you are not working on the edge of the precision of double.
*
* @param d value to round
* @return rounded value
*/
public static double round2(double d) {
final double factor = 1e2;
return Math.abs(d) < WHOLE_NUMBER / factor
? (long) (d < 0 ? d * factor - 0.5 : d * factor + 0.5) / factor : d;
}
/**
* Performs a round which is accurate to within 1 ulp.
* The value 0.5 should round up however the value one ulp less might round up or down.
* This is a pragmatic choice for performance reasons as it is
* assumed you are not working on the edge of the precision of double.
*
* @param d value to round
* @return rounded value
*/
public static double round2up(double d) {
if (d < 0)
return -round2(-d);
final double factor = 1e2;
if (!(d <= WHOLE_NUMBER / factor)) // to handle NaN
return d;
final double df = d * factor;
long ldf = (long) df;
final double residual = df - ldf + Math.ulp(d) * (factor * 0.983);
if (residual >= 0.5)
ldf++;
return ldf / factor;
}
/**
* Performs a round which is accurate to within 1 ulp. i.e. for values very close to 0.5 it
* might be rounded up or down. This is a pragmatic choice for performance reasons as it is
* assumed you are not working on the edge of the precision of double.
*
* @param d value to round
* @return rounded value
*/
public static double round3(double d) {
final double factor = 1e3;
return Math.abs(d) < WHOLE_NUMBER / factor
? (long) (d < 0 ? d * factor - 0.5 : d * factor + 0.5) / factor : d;
}
/**
* Performs a round which is accurate to within 1 ulp.
* The value 0.5 should round up however the value one ulp less might round up or down.
* This is a pragmatic choice for performance reasons as it is
* assumed you are not working on the edge of the precision of double.
*
* @param d value to round
* @return rounded value
*/
public static double round3up(double d) {
if (d < 0)
return -round3(-d);
final double factor = 1e3;
if (!(d <= WHOLE_NUMBER / factor)) // to handle NaN
return d;
final double df = d * factor;
long ldf = (long) df;
final double residual = df - ldf + Math.ulp(d) * (factor * 0.983);
if (residual >= 0.5)
ldf++;
return ldf / factor;
}
/**
* Performs a round which is accurate to within 1 ulp. i.e. for values very close to 0.5 it
* might be rounded up or down. This is a pragmatic choice for performance reasons as it is
* assumed you are not working on the edge of the precision of double.
*
* @param d value to round
* @return rounded value
*/
public static double round4(double d) {
final double factor = 1e4;
return Math.abs(d) < WHOLE_NUMBER / factor
? (long) (d < 0 ? d * factor - 0.5 : d * factor + 0.5) / factor : d;
}
/**
* Performs a round which is accurate to within 1 ulp.
* The value 0.5 should round up however the value one ulp less might round up or down.
* This is a pragmatic choice for performance reasons as it is
* assumed you are not working on the edge of the precision of double.
*
* @param d value to round
* @return rounded value
*/
public static double round4up(double d) {
if (d < 0)
return -round4(-d);
final double factor = 1e4;
if (!(d <= WHOLE_NUMBER / factor)) // to handle NaN
return d;
final double df = d * factor;
long ldf = (long) df;
final double residual = df - ldf + Math.ulp(d) * (factor * 0.983);
if (residual >= 0.5)
ldf++;
return ldf / factor;
}
/**
* Performs a round which is accurate to within 1 ulp. i.e. for values very close to 0.5 it
* might be rounded up or down. This is a pragmatic choice for performance reasons as it is
* assumed you are not working on the edge of the precision of double.
*
* @param d value to round
* @return rounded value
*/
public static double round5(double d) {
final double factor = 1e5;
return Math.abs(d) < WHOLE_NUMBER / factor
? (long) (d < 0 ? d * factor - 0.5 : d * factor + 0.5) / factor : d;
}
/**
* Performs a round which is accurate to within 1 ulp.
* The value 0.5 should round up however the value one ulp less might round up or down.
* This is a pragmatic choice for performance reasons as it is
* assumed you are not working on the edge of the precision of double.
*
* @param d value to round
* @return rounded value
*/
public static double round5up(double d) {
if (d < 0)
return -round5(-d);
final double factor = 1e5;
if (!(d <= WHOLE_NUMBER / factor)) // to handle NaN
return d;
final double df = d * factor;
long ldf = (long) df;
final double residual = df - ldf + Math.ulp(d) * (factor * 0.983);
if (residual >= 0.5)
ldf++;
return ldf / factor;
}
/**
* Performs a round which is accurate to within 1 ulp.
* The value 0.5 should round up however the value one ulp less might round up or down.
* This is a pragmatic choice for performance reasons as it is
* assumed you are not working on the edge of the precision of double.
*
* @param d value to round
* @return rounded value
*/
public static double round6(double d) {
final double factor = 1e6;
return Math.abs(d) < WHOLE_NUMBER / factor
? (long) (d < 0 ? d * factor - 0.5 : d * factor + 0.5) / factor : d;
}
/**
* Performs a round which is accurate to within 1 ulp.
* The value 0.5 should round up however the value one ulp less might round up or down.
* This is a pragmatic choice for performance reasons as it is
* assumed you are not working on the edge of the precision of double.
*
* @param d value to round
* @return rounded value
*/
public static double round6up(double d) {
if (d < 0)
return -round6(-d);
final double factor = 1e6;
if (!(d <= WHOLE_NUMBER / factor)) // to handle NaN
return d;
final double df = d * factor;
long ldf = (long) df;
final double residual = df - ldf + Math.ulp(d) * (factor * 0.983);
if (residual >= 0.5)
ldf++;
return ldf / factor;
}
/**
* Performs a round which is accurate to within 1 ulp. i.e. for values very close to 0.5 it
* might be rounded up or down. This is a pragmatic choice for performance reasons as it is
* assumed you are not working on the edge of the precision of double.
*
* @param d value to round
* @return rounded value
*/
public static double round7(double d) {
final double factor = 1e7;
return Math.abs(d) < WHOLE_NUMBER / factor
? (long) (d < 0 ? d * factor - 0.5 : d * factor + 0.5) / factor : d;
}
/**
* Performs a round which is accurate to within 1 ulp.
* The value 0.5 should round up however the value one ulp less might round up or down.
* This is a pragmatic choice for performance reasons as it is
* assumed you are not working on the edge of the precision of double.
*
* @param d value to round
* @return rounded value
*/
public static double round7up(double d) {
if (d < 0)
return -round7(-d);
final double factor = 1e7;
if (!(d <= WHOLE_NUMBER / factor)) // to handle NaN
return d;
final double df = d * factor;
long ldf = (long) df;
final double residual = df - ldf + Math.ulp(d) * (factor * 0.983);
if (residual >= 0.5)
ldf++;
return ldf / factor;
}
/**
* Performs a round which is accurate to within 1 ulp. i.e. for values very close to 0.5 it
* might be rounded up or down. This is a pragmatic choice for performance reasons as it is
* assumed you are not working on the edge of the precision of double.
*
* @param d value to round
* @return rounded value
*/
public static double round8(double d) {
final double factor = 1e8;
return Math.abs(d) < WHOLE_NUMBER / factor
? (long) (d < 0 ? d * factor - 0.5 : d * factor + 0.5) / factor : d;
}
/**
* Performs a round which is accurate to within 1 ulp.
* The value 0.5 should round up however the value one ulp less might round up or down.
* This is a pragmatic choice for performance reasons as it is
* assumed you are not working on the edge of the precision of double.
*
* @param d value to round
* @return rounded value
*/
public static double round8up(double d) {
if (d < 0)
return -round8(-d);
final double factor = 1e8;
if (!(d <= WHOLE_NUMBER / factor)) // to handle NaN
return d;
final double df = d * factor;
long ldf = (long) df;
final double residual = df - ldf + Math.ulp(d) * (factor * 0.983);
if (residual >= 0.5)
ldf++;
return ldf / factor;
}
/**
* Returns the next power of two greater than or equal to the given number, with a specified minimum.
*
* @param n the number to find the next power of two for
* @param min the minimum power of two to return if n is less than min
* @return the next power of two greater than or equal to n, but at least min
* @throws IllegalArgumentException if the provided {@code min} value is not a positive power of two
*/
public static int nextPower2(int n, int min) throws IllegalArgumentException {
if (min <= 0 || (min & (min - 1)) != 0)
throw new IllegalArgumentException("min (" + min + ") must be a positive power of 2");
if (n <= min) return min;
if ((n & (n - 1)) == 0)
return n;
n--;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
n++;
// Limit to the maximum power of two that fits in an int (2^30)
if (n <= 0 || n > (1 << 30)) {
return 1 << 30;
}
return n;
}
/**
* Returns the next power of two greater than or equal to the given number, with a specified minimum.
*
* @param n the number to find the next power of two for
* @param min the minimum power of two to return if n is less than min
* @return the next power of two greater than or equal to n, but at least min
* @throws IllegalArgumentException if the provided {@code min} value is not a positive power of two
*/
public static long nextPower2(long n, long min) throws IllegalArgumentException {
if (min <= 0 || (min & (min - 1)) != 0)
throw new IllegalArgumentException("min (" + min + ") must be a positive power of 2");
if (n <= min) return min;
if ((n & (n - 1)) == 0)
return n;
n--;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
n |= n >>> 32;
n++;
// Handle potential overflow
if (n <= 0) {
return 1L << 62;
}
return n;
}
/**
* Checks if a number is a power of two.
*
* @param n the number to check
* @return true if n is a positive power of two, false otherwise
*/
public static boolean isPowerOf2(long n) {
return n > 0 && (n & (n - 1)) == 0;
}
public static long hash64(@Nullable CharSequence cs) {
if (cs == null || cs.length() == 0)
return 0;
if (cs instanceof String)
return hash64((String) cs);
long hash = 0;
for (int i = 0, len = cs.length(); i < len; i++)
hash = hash * 0x32246e3d + cs.charAt(i);
return agitate(hash);
}
/**
* Computes a 64-bit hash value for the given string.
* The hash function used is dependent on the Java version and the internal representation of the string.
* For Java 9 and later, if the string is represented as a byte array, each byte is incorporated into the hash.
* Otherwise, each character is incorporated into the hash.
* For Java 8 and earlier, each character is incorporated into the hash.
* The hash value is agitated using a function not shown in this code snippet.
*
* @param s the string to compute the hash for
* @return the 64-bit hash value
* @throws IllegalArgumentException if {@code s} is {@code null}
*/
public static long hash64(@NotNull String s) {
//noinspection ConstantValue
if (s == null) throw new IllegalArgumentException();
long hash = 0;
if (Jvm.isJava9Plus()) {
if (StringUtils.getStringCoder(s) == 0) {
final byte[] bytes = StringUtils.extractBytes(s);
for (int i = 0, len = s.length(); i < len; i++)
hash = hash * 0x32246e3d + bytes[i];
} else {
for (int i = 0, len = s.length(); i < len; i++)
hash = hash * 0x32246e3d + s.charAt(i);
}
} else {
final char[] chars = StringUtils.extractChars(s);
for (int i = 0, len = s.length(); i < len; i++)
hash = hash * 0x32246e3d + chars[i];
}
return agitate(hash);
}
/**
* Computes a 64-bit hash value for the given StringBuilder.
* The hash function used is dependent on the Java version and the internal representation of the string.
* For Java 9 and later, if the string is represented as a byte array, each byte is incorporated into the hash.
* Otherwise, each character is incorporated into the hash.
* For Java 8 and earlier, each character is incorporated into the hash.
* The hash is then agitated using a function not shown in this code snippet.
*
* @param s the string to compute the hash for
* @return the 64-bit hash value
* @throws IllegalArgumentException if {@code s} is {@code null}
*/
public static long hash64(@NotNull StringBuilder s) {
long hash = 0;
if (Jvm.isJava9Plus()) {
if (StringUtils.getStringCoder(s) == 0) {
final byte[] bytes = StringUtils.extractBytes(s);
for (int i = 0, len = s.length(); i < len; i++)
hash = hash * 0x32246e3d + bytes[i];
} else {
final byte[] bytes = StringUtils.extractBytes(s);
for (int i = 0, len = s.length(); i < len; i++)
hash = hash * 0x32246e3d + s.charAt(i);
}
} else {
final char[] chars = StringUtils.extractChars(s);
for (int i = 0, len = s.length(); i < len; i++)
hash = hash * 0x32246e3d + chars[i];
}
return agitate(hash);
}
/**
* Returns rounded down log2{@code num}, e. g.: {@code intLog2(1) == 0},
* {@code intLog2(2) == 1}, {@code intLog2(7) == 2}, {@code intLog2(8) == 3}, etc.
*
* @throws IllegalArgumentException if the given number <= 0
*/
public static int intLog2(long num) throws IllegalArgumentException {
if (num <= 0)
throw new IllegalArgumentException("positive argument expected, " + num + " given");
return 63 - Long.numberOfLeadingZeros(num);
}
/**
* Returns the value of the {@code long} argument;
* throwing an exception if the value overflows a {@code byte}.
*
* @param value the long value
* @return the argument as a byte
* @throws ArithmeticException if the {@code argument} overflows a byte
*/
public static byte toInt8(long value) throws ArithmeticException {
if ((byte) value == value)
return (byte) value;
throw new ArithmeticException("Byte " + value + OUT_OF_RANGE);
}
/**
* Returns the value of the {@code long} argument;
* throwing an exception if the value overflows a {@code short}.
*
* @param value the long value
* @return the argument as a short
* @throws ArithmeticException if the {@code argument} overflows a short
*/
public static short toInt16(long value) throws ArithmeticException {
if ((short) value == value)
return (short) value;
throw new ArithmeticException("Short " + value + OUT_OF_RANGE);
}
/**
* Returns the value of the {@code long} argument;
* throwing an exception if the value overflows an {@code int}.
*
* @param value the long value
* @param msg to use in a potential exception message
* @return the argument as an int
* @throws ArithmeticException if the {@code argument} overflows an int
*/
public static int toInt32(long value, @NotNull String msg) throws ArithmeticException {
if ((int) value == value)
return (int) value;
throw new ArithmeticException(String.format(msg, value));
}
/**
* Returns the value of the {@code long} argument;
* throwing an exception if the value overflows an {@code int}.
*
* @param value the long value
* @return the argument as an int
* @throws ArithmeticException if the {@code argument} overflows an int
*/
public static int toInt32(long value) throws ArithmeticException {
if ((int) value == value)
return (int) value;
throw new ArithmeticException("Int " + value + OUT_OF_RANGE);
}
/**
* Returns the value of the {@code long} argument;
* throwing an exception if the value overflows an unsigned byte (0xFF).
*
* @param value the long value
* @return the argument as a short
* @throws ArithmeticException if the {@code argument} overflows an unsigned byte
*/
public static short toUInt8(long value) throws ArithmeticException {
if ((value & 0xFF) == value)
return (short) value;
throw new ArithmeticException("Unsigned Byte " + value + OUT_OF_RANGE);
}
/**
* Returns the value of the {@code long} argument;
* throwing an exception if the value overflows an unsigned short (0xFFFF).
*
* @param value the long value
* @return the argument as an int
* @throws ArithmeticException if the {@code argument} overflows an unsigned short
*/
public static int toUInt16(long value) throws ArithmeticException {
if ((value & 0xFFFF) == value)
return (int) value;
throw new ArithmeticException("Unsigned Short " + value + OUT_OF_RANGE);
}
/**
* Returns the value of the {@code long} argument;
* throwing an exception if the value overflows an unsigned 31 bit value (0x7FFFFFFFL).
*
* @param value the long value
* @return the argument as a long
* @throws ArithmeticException if the {@code argument} overflows an unsigned int
*/
public static int toUInt31(long value) throws ArithmeticException {
if ((value & 0x7FFFFFFFL) == value)
return (int) value;
throw new ArithmeticException("Unsigned Int 31-bit " + value + OUT_OF_RANGE);
}
/**
* Returns the value of the {@code long} argument;
* throwing an exception if the value overflows an unsigned int (0xFFFFFFFFL).
*
* @param value the long value
* @return the argument as a long
* @throws ArithmeticException if the {@code argument} overflows an unsigned int
*/
public static long toUInt32(long value) throws ArithmeticException {
if ((value & 0xFFFFFFFFL) == value)
return value;
throw new ArithmeticException("Unsigned Int " + value + OUT_OF_RANGE);
}
/**
* Performs a series of bit operations on a long value to "agitate" the bits.
* This includes a right shift, addition, and a right rotation with a bitwise XOR operation.
* The purpose of this method is to create a more evenly distributed set of bit patterns
* which can be useful in certain hashing or random number generation scenarios.
*
* @param l the long value to be agitated
* @return the agitated long value
*/
public static long agitate(long l) {
l += l >>> 22;
l ^= Long.rotateRight(l, 17);
return l;
}
/**
* A simple hashing algorithm for a 64-bit value
*
* @param l0 to hash
* @return hash value.
*/
public static long hash64(long l0) {
int l0a = (int) (l0 >> 32);
long h0 = l0 * M0 + (long) l0a * M1;
return agitate(h0);
}
/**
* Divide {@code dividend} by divisor, if division is not integral the result is rounded up.
* Examples: {@code divideRoundUp(10, 5) == 2}, {@code divideRoundUp(11, 5) == 3},
* {@code divideRoundUp(-10, 5) == -2}, {@code divideRoundUp(-11, 5) == -3}.
*
* @return the rounded up quotient
*/
public static long divideRoundUp(long dividend, long divisor) {
int sign = (dividend > 0 ? 1 : -1) * (divisor > 0 ? 1 : -1);
return sign * (Math.abs(dividend) + Math.abs(divisor) - 1) / Math.abs(divisor);
}
/**
* Returns the power of ten for the given decimal places.
*
* @param decimalPlaces the number of decimal places
* @return the power of ten corresponding to the decimal places
* @throws IllegalArgumentException if the decimal places are less than 0 or greater than or equal to 19
*/
public static long tens(int decimalPlaces) {
if (decimalPlaces < 0 || decimalPlaces >= 19)
throw decimalPlacesIAE(decimalPlaces);
return TENS[decimalPlaces];
}
@NotNull
private static IllegalArgumentException decimalPlacesIAE(int decimalPlaces) {
return new IllegalArgumentException("decimalPlaces=" + decimalPlaces);
}
/**
* Returns the number of digits in the given number.
*
* @param num the number to count the digits of
* @return the number of digits in the given number
*/
public static int digits(long num) {
int index = Arrays.binarySearch(TENS, num);
return index < -1 ? -1 - index : index >= 0 ? index + 1 : 1;
}
/**
* Returns the power of five for the given decimal places.
*
* @param decimalPlaces the number of decimal places
* @return the power of five corresponding to the decimal places
*/
public static long fives(int decimalPlaces) {
return FIVES[decimalPlaces];
}
/**
* Checks if two double values are the same. Considers NaN values as equal.
*
* @param a the first double value
* @param b the second double value
* @return true if both values are the same or both are NaN, false otherwise
*/
public static boolean same(double a, double b) {
return Double.isNaN(a) ? Double.isNaN(b) : a == b;
}
/**
* Checks if two float values are the same. Considers NaN values as equal.
*
* @param a the first float value
* @param b the second float value
* @return true if both values are the same or both are NaN, false otherwise
*/
public static boolean same(float a, float b) {
return Float.isNaN(a) ? Float.isNaN(b) : a == b;
}
public static int hash(Object o) {
return o == null ? 0 : o.hashCode();
}
public static int hash(Object o1, Object o2) {
return hash(o1) * M0 + hash(o2);
}
public static int hash(Object o1, Object o2, Object o3) {
return hash(o1, o2) * M0 + hash(o3);
}
public static int hash(Object o1, Object o2, Object o3, Object o4) {
return hash(o1, o2, o3) * M0 + hash(o4);
}
public static int hash(Object o1, Object o2, Object o3, Object o4, Object o5) {
return hash(o1, o2, o3, o4) * M0 + hash(o5);
}
/**
* Convert components to a double value
*
* @param value The integer value
* @param exponent The exponent
* @param negative Whether it is negative or not
* @param decimalPlaces The number of decimal places
* @return The value as a double
*/
public static double asDouble(@NonNegative long value, int exponent, boolean negative, int decimalPlaces) {
assert AssertUtil.SKIP_ASSERTIONS || value >= 0;
// these numbers were determined empirically.
int leading =
Long.numberOfLeadingZeros(value) - 1;
int scale2 = 0;
if (leading > 0) {
scale2 = leading > 10 ? (10 + leading) >> 1 : leading;
value <<= scale2;
}
double d;
if (decimalPlaces > 0) {
if (decimalPlaces < 28) {
long fives = Maths.fives(decimalPlaces);
long whole = value / fives;
long rem = value % fives;
int wholeZero = Long.numberOfLeadingZeros(whole) - 10;
if (wholeZero <= 0) {
d = roundUpOneUlp(whole, rem, wholeZero);
} else {
int remZero = Long.numberOfLeadingZeros(rem) - 1;
int leading2 = Math.min(wholeZero, remZero);
d = addRemainingFraction(fives, whole, rem, leading2);
scale2 += leading2;
}
} else {
d = value / Math.pow(5, decimalPlaces);
}
} else if (decimalPlaces < -27) {
d = value * Math.pow(5, -decimalPlaces);
} else if (decimalPlaces < 0) {
double fives = Maths.fives(-decimalPlaces);
d = value * fives;
} else {
d = value;
}
double scalb = Math.scalb(d, exponent - decimalPlaces - scale2);
return negative ? -scalb : scalb;
}
private static double addRemainingFraction(long fives, long whole, long rem, int leading2) {
double d;
rem <<= leading2;
long rem2 = rem / fives;
d = add((whole << leading2) + rem2, rem % fives, fives);
return d;
}
private static double roundUpOneUlp(long whole, long rem, int wholeZero) {
double d;
d = whole;
if (wholeZero == 0) {
long l = (long) d;
if (rem > 0 && whole > l)
d += Math.ulp(d);
}
return d;
}
/**
* return a + b / c as a double
*/
public static double add(long a, long b, long c) {
double d = a;
long l = (long) d;
long diff = a - l;
double div = diff + (double) b / c;
return d + div;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy