From ddfccb01df11089cbc602d8ff100d7f3d0bf6bec Mon Sep 17 00:00:00 2001 From: Seva Safris Date: Tue, 9 Jun 2020 10:24:27 +0700 Subject: [PATCH] Numbers.digits(...) -> Numbers.precision(...), re #4 --- src/main/java/org/libj/math/LongDecimal.java | 111 +++++++++++++----- .../libj/math/LongDecimalArithmeticTest.java | 32 ++++- .../org/libj/math/LongDecimalCodecTest.java | 4 +- .../libj/math/LongDecimalPredicateTest.java | 8 +- 4 files changed, 116 insertions(+), 39 deletions(-) diff --git a/src/main/java/org/libj/math/LongDecimal.java b/src/main/java/org/libj/math/LongDecimal.java index 483c8e43..17392895 100644 --- a/src/main/java/org/libj/math/LongDecimal.java +++ b/src/main/java/org/libj/math/LongDecimal.java @@ -30,13 +30,15 @@ public final class LongDecimal { private static final Logger logger = LoggerFactory.getLogger(LongDecimal.class); static final byte maxScaleBits = 17; private static final long[] pow2 = new long[maxScaleBits]; + static final byte[] minPrecision = new byte[maxScaleBits]; + static final byte[] maxPrecision = new byte[maxScaleBits]; static final short[] minScale = new short[maxScaleBits]; static final short[] maxScale = new short[maxScaleBits]; - static final byte[] minDigits = new byte[maxScaleBits]; - static final byte[] maxDigits = new byte[maxScaleBits]; static { for (byte b = 0; b < maxScaleBits; ++b) { + minPrecision[b] = Numbers.precision(minValue(b)); + maxPrecision[b] = Numbers.precision(maxValue(b)); pow2[b] = (long)Math.pow(2, b); if (b <= 1) { minScale[b] = 0; @@ -47,9 +49,6 @@ public final class LongDecimal { minScale[b] = (short)-scale; maxScale[b] = (short)(scale - 1); } - - minDigits[b] = Numbers.digits(minValue(b)); - maxDigits[b] = Numbers.digits(maxValue(b)); } } @@ -149,32 +148,32 @@ public static String toString(final long encoded, final byte bits) { if (scale == 0) return String.valueOf(value); - final byte digits = Numbers.digits(value); + final byte precision = Numbers.precision(value); int dot; if (scale > 0) { - if (scale >= digits) { + if (scale >= precision) { dot = -1; } else { - dot = digits - scale + 1; + dot = precision - scale + 1; scale = 0; } } else { dot = 2; - scale -= (digits - 1); + scale -= (precision - 1); } final int scaleLen; final int scalePart; final boolean scaleNeg; if (scale > 0) { - scaleLen = Numbers.digits(scale); + scaleLen = Numbers.precision(scale); scalePart = scaleLen + 2; scaleNeg = false; } else if (scale < 0) { - scaleLen = Numbers.digits(scale); + scaleLen = Numbers.precision(scale); scalePart = scaleLen + 1; scaleNeg = true; scale = (short)-scale; @@ -185,7 +184,7 @@ else if (scale < 0) { scaleNeg = false; } - final char[] buf = new char[digits + (dot <= 0 ? 0 : 1) + (value < 0 ? 1 : 0) + scalePart]; + final char[] buf = new char[precision + (dot <= 0 ? 0 : 1) + (value < 0 ? 1 : 0) + scalePart]; final int lim; if (value < 0) { lim = 1; @@ -251,7 +250,7 @@ public static BigDecimal toBigDecimal(final long encoded, final byte bits) { * {@link LongDecimal#encode(long,short,byte,long) encoded} value and * and sign {@code bits}. */ - public static double toDouble(final long encoded, final byte bits) { + public static double doubleValue(final long encoded, final byte bits) { final long value = decodeValue(encoded, bits); final short scale = decodeScale(encoded, bits); return value * StrictMath.pow(10, -scale); @@ -335,7 +334,7 @@ public static long encode(final String value, final byte bits, final long defaul // Make sure we don't overflow the scale bits if (s > maxScale) { int adj = s - maxScale; - if (adj >= Numbers.digits(val)) { + if (adj >= Numbers.precision(val)) { if (logger.isDebugEnabled()) logger.debug("value=" + val + " scale=" + s + " cannot be represented with " + bits + " scale bits"); @@ -359,7 +358,7 @@ public static long encode(final String value, final byte bits, final long defaul final int minScale = LongDecimal.minScale[bits]; if (s < minScale) { int adj = minScale - s; - if (adj >= 20 - Numbers.digits(val)) { + if (adj >= 20 - Numbers.precision(val)) { if (logger.isDebugEnabled()) logger.debug("value=" + val + " scale=" + s + " cannot be represented with " + bits + " scale bits"); @@ -550,12 +549,12 @@ public static long setScale(final long encoded, final short scale, final byte bi if (val == 0) return encode(val, scale, bits, defaultValue); - final byte digits = Numbers.digits(val); - if (scale < s - digits + 1) + final byte precision = Numbers.precision(val); + if (scale < s - precision + 1) return encode(0, scale, bits, defaultValue); final int diff = s - scale; - if (diff > digits || diff < -18 || digits - diff > 19) { + if (diff > precision || diff < -18 || precision - diff > 19) { if (logger.isDebugEnabled()) logger.debug("The scale of " + toString(encoded, bits) + " cannot be set to " + scale + " with " + bits + " scale bits"); @@ -600,7 +599,7 @@ public static long setScale(final long encoded, final short scale, final byte bi * @see #decodeValue(long,byte) */ public static short precision(final long encoded, final byte bits) { - return encoded == 0 ? 1 : Numbers.digits(decodeValue(encoded, bits)); + return encoded == 0 ? 1 : Numbers.precision(decodeValue(encoded, bits)); } /** @@ -703,8 +702,8 @@ public static int compare(final long ld1, final long ld2, final byte bits) { if (s1 == s2 || v1 < 0 != v2 < 0) return v1 < v2 ? -1 : 1; - s1 -= Numbers.digits(v1); - s2 -= Numbers.digits(v2); + s1 -= Numbers.precision(v1); + s2 -= Numbers.precision(v2); if (s1 != s2) return (v1 < 0 ? s1 < s2 : s1 > s2) ? -1 : 1; @@ -831,6 +830,58 @@ public static boolean gte(final long ld1, final long ld2, final byte bits) { * @see #decodeValue(long,byte) * @see #decodeScale(long,byte) */ + /** + * Returns the absolute value of a + * {@link LongDecimal#encode(long,short,byte,long) LongDecimal}-encoded + * {@code long} value. + *

+ * If the argument is not negative, the argument is returned. + *

+ * If the argument is negative, the negation of the argument is returned. + *

+ * Note: If the argument is equal to the most negative representable + * value for the specified {@code bits} (i.e. + * {@link LongDecimal#minValue(byte) minValue(bits)}), {@code defaultValue} is + * returned. + * + * @param d The {@link LongDecimal#encode(long,short,byte,long) + * LongDecimal}-encoded value. + * @param bits The number of bits in the encoded {@code long} values reserved + * for the scale. + * @param defaultValue The value to be returned if the result cannot be + * represented in {@link LongDecimal} encoding with the provided + * {@code bits}. + * @return The absolute value of the argument. + */ + public static long abs(final long d, final byte bits, final long defaultValue) { + final long value = decodeValue(d, bits); + return value >= 0 ? d : encode(-value, decodeScale(d, bits), bits, defaultValue); + } + + /** + * Returns the result of the negation of {@code d}, i.e.: + * + *

+   * {@code result = -d}
+   * 
+ * + * Note: If the argument is equal to the most negative representable + * value for the specified {@code bits} (i.e. + * {@link LongDecimal#minValue(byte) minValue(bits)}), {@code defaultValue} is + * returned. + * + * @param d The {@link LongDecimal#encode(long,short,byte,long) + * LongDecimal}-encoded value to negate. + * @param bits The number of bits in the encoded {@code long} values reserved + * for the scale. + * @param defaultValue The value to be returned if the result cannot be + * represented in {@link LongDecimal} encoding with the provided + * {@code bits}. + * @return The result of the negation of {@code d}, i.e.: {@code - d} + * @see #encode(long,short,byte,long) + * @see #decodeValue(long,byte) + * @see #decodeScale(long,byte) + */ public static long neg(final long d, final byte bits, final long defaultValue) { return encode(-decodeValue(d, bits), decodeScale(d, bits), bits, defaultValue); } @@ -924,13 +975,13 @@ private static long add0(long ld1, long ld2, final byte bits, final long default } int diff = s2 - s1; - byte avail = (byte)(maxDigits[bits] - Numbers.digits(v1)); + byte avail = (byte)(maxPrecision[bits] - Numbers.precision(v1)); // Test for overflow (exclude if avail already == 0?) final long test = v1 * FastMath.e10[avail]; if (v1 > 0 ? 0 > test || test > maxValue : 0 < test || test < minValue) --avail; - if (diff > Numbers.digits(maxValue)) + if (diff > Numbers.precision(maxValue)) return ld1; if (diff > avail) { @@ -1080,8 +1131,8 @@ else if (pow > 63) // 32 + 31 // How many digits are available before overflow? // If not enough, then degrade precision of each // value, degrading higher precision first. - final byte l1 = Numbers.digits(v1); - final byte l2 = Numbers.digits(v2); + final byte l1 = Numbers.precision(v1); + final byte l2 = Numbers.precision(v2); byte avail = (byte)(19 - l1 - l2); if (avail >= 0) { while (expectedPositive != (val = v1 * v2) > 0) { @@ -1176,7 +1227,7 @@ else if (pow > 63) // 32 + 31 // Make sure we don't overflow the scale bits if (s > maxScale) { int adj = s - maxScale; - if (adj >= Numbers.digits(val)) { + if (adj >= Numbers.precision(val)) { if (logger.isDebugEnabled()) logger.debug("value=" + val + " scale=" + s + " cannot be represented with " + bits + " scale bits"); @@ -1200,7 +1251,7 @@ else if (pow > 63) // 32 + 31 final int minScale = LongDecimal.minScale[bits]; if (s < minScale) { int adj = minScale - s; - if (adj >= 20 - Numbers.digits(val)) { + if (adj >= 20 - Numbers.precision(val)) { if (logger.isDebugEnabled()) logger.debug("value=" + val + " scale=" + s + " cannot be represented with " + bits + " scale bits"); @@ -1270,7 +1321,7 @@ public static long div(final long ld1, final long ld2, final byte bits, final lo short s2 = decodeScale(ld2, bits); // How many decimals do we have until overflow of long? - byte l1 = (byte)(19 - Numbers.digits(v1)); + byte l1 = (byte)(19 - Numbers.precision(v1)); // Expand the value to max precision available if (l1 > 0) { long test = v1 * FastMath.e10[l1]; @@ -1305,7 +1356,7 @@ public static long div(final long ld1, final long ld2, final byte bits, final lo // Make sure we don't overflow the scale bits if (s > maxScale) { int adj = s - maxScale; - if (adj >= Numbers.digits(val)) { + if (adj >= Numbers.precision(val)) { if (logger.isDebugEnabled()) logger.debug("value=" + val + " scale=" + s + " cannot be represented with " + bits + " scale bits"); @@ -1329,7 +1380,7 @@ public static long div(final long ld1, final long ld2, final byte bits, final lo final int minScale = LongDecimal.minScale[bits]; if (s < minScale) { int adj = minScale - s; - if (adj >= 20 - Numbers.digits(val)) { + if (adj >= 20 - Numbers.precision(val)) { if (logger.isDebugEnabled()) logger.debug("value=" + val + " scale=" + s + " cannot be represented with " + bits + " scale bits"); diff --git a/src/test/java/org/libj/math/LongDecimalArithmeticTest.java b/src/test/java/org/libj/math/LongDecimalArithmeticTest.java index a8dd6908..093cebb1 100644 --- a/src/test/java/org/libj/math/LongDecimalArithmeticTest.java +++ b/src/test/java/org/libj/math/LongDecimalArithmeticTest.java @@ -38,7 +38,7 @@ public class LongDecimalArithmeticTest extends LongDecimalTest { D("7E-13"), D("1E-11"), D("2E-11"), - D("5E-13"), + D("1E-12"), D("2E-11"), D("6E-12"), D("7E-12"), @@ -164,7 +164,7 @@ BigDecimal run(final BigDecimal bd1, final BigDecimal bd2, final BigDecimal expe int scale = expected.scale(); int len; if (!lockScale()) { - len = Numbers.digits(unscaled); + len = Numbers.precision(unscaled); if (len > 19) { final int negOffset = unscaled.signum() < 0 ? 1 : 0; unscaled = new BigInteger(unscaled.toString().substring(negOffset, 19 + negOffset)); @@ -192,7 +192,7 @@ BigDecimal run(final BigDecimal bd1, final BigDecimal bd2, final BigDecimal expe expectDefaultValue = false; } else { - len = Numbers.digits(unscaled); + len = Numbers.precision(unscaled); final int diffScale = scale - (scale < 0 ? minScale : maxScale); expectDefaultValue = diffScale < 0 || len - diffScale <= 0; } @@ -282,6 +282,27 @@ BigDecimal control(final BigDecimal bd1, final BigDecimal bd2, final long[] time } }; + private static final ArithmeticOperation abs = new FunctionOperation("abs", long.class) { + @Override + Long test(final long ld1, final long ld2, final BigDecimal bd1, final BigDecimal bd2, final byte bits, final long defaultValue, final long[] time) { + long ts = System.nanoTime(); + final long result = abs(ld1, bits, defaultValue); + ts = System.nanoTime() - ts; + if (result != defaultValue) + time[0] += ts; + + return result; + } + + @Override + BigDecimal control(final BigDecimal bd1, final BigDecimal bd2, final long[] time) { + final long ts = System.nanoTime(); + final BigDecimal result = bd1.abs(); + time[1] += System.nanoTime() - ts; + return result; + } + }; + private static final ArithmeticOperation encodeBigDecimal = new FunctionOperation("encode", BigDecimal.class) { @Override Long test(final long ld1, final long ld2, final BigDecimal bd1, final BigDecimal bd2, final byte bits, final long defaultValue, final long[] time) { @@ -412,6 +433,11 @@ public void testNeg() { test(neg); } + @Test + public void testAbs() { + test(abs); + } + @Test public void testSetScale() { test(setScale); diff --git a/src/test/java/org/libj/math/LongDecimalCodecTest.java b/src/test/java/org/libj/math/LongDecimalCodecTest.java index 4c5497f2..489c48fe 100644 --- a/src/test/java/org/libj/math/LongDecimalCodecTest.java +++ b/src/test/java/org/libj/math/LongDecimalCodecTest.java @@ -198,7 +198,7 @@ public void testMaxValue() { public void testMaxDigits() { final byte[] maxDigits = new byte[LongDecimal.minScale.length]; for (byte b = 0; b < maxValue.length; ++b) - maxDigits[b] = Numbers.digits(LongDecimal.maxValue(b)); + maxDigits[b] = Numbers.precision(LongDecimal.maxValue(b)); long ts; byte tmp = 0; @@ -217,7 +217,7 @@ public void testMaxDigits() { final byte b = (byte)(i % maxValue.length); final long maxValue = LongDecimal.maxValue(b); ts = System.nanoTime(); - tmp = Numbers.digits(maxValue); + tmp = Numbers.precision(maxValue); time2 += System.nanoTime() - ts; } } diff --git a/src/test/java/org/libj/math/LongDecimalPredicateTest.java b/src/test/java/org/libj/math/LongDecimalPredicateTest.java index 7551f12c..390318f9 100644 --- a/src/test/java/org/libj/math/LongDecimalPredicateTest.java +++ b/src/test/java/org/libj/math/LongDecimalPredicateTest.java @@ -70,7 +70,7 @@ BigDecimal control(final BigDecimal bd1, final BigDecimal bd2, final long[] time } }; - private static final Operation toDouble = new Operation("toDouble", long.class, "~") { + private static final Operation doubleValue = new Operation("doubleValue", long.class, "~") { @Override BigDecimal run(final BigDecimal bd1, final BigDecimal bd2, final Double expected, final Double actual, final byte bits, final long defaultValue, final BigDecimal[] errors, final boolean[] failures) { return expected.compareTo(actual) == 0 ? null : BigDecimal.ONE; @@ -79,7 +79,7 @@ BigDecimal run(final BigDecimal bd1, final BigDecimal bd2, final Double expected @Override Double test(final long ld1, final long ld2, final BigDecimal bd1, final BigDecimal bd2, final byte bits, final long defaultValue, final long[] time) { final long ts = System.nanoTime(); - final double result = LongDecimal.toDouble(ld1, bits); + final double result = LongDecimal.doubleValue(ld1, bits); time[0] += System.nanoTime() - ts; return result; } @@ -338,8 +338,8 @@ public void testToBigDecimal() { } @Test - public void testToDouble() { - test(toDouble); + public void testDoubleValue() { + test(doubleValue); } @Test