/** * Divide the target object by right, and scale the result to newScale. * * This uses HiveDecimal to get a correct answer with the same rounding * behavior as HiveDecimal, but it is expensive. * * In the future, a native implementation could be faster. */ public void divideDestructive(Decimal128 right, short newScale) { HiveDecimal rightHD = HiveDecimal.create(right.toBigDecimal()); HiveDecimal thisHD = HiveDecimal.create(this.toBigDecimal()); HiveDecimal result = thisHD.divide(rightHD); /* If the result is null, throw an exception. This can be caught * by calling code in the vectorized code path and made to yield * a SQL NULL value. */ if (result == null) { throw new ArithmeticException("null divide result"); } this.update(result.bigDecimalValue().toPlainString(), newScale); this.unscaledValue.throwIfExceedsTenToThirtyEight(); }
@Test public void testPiNewton() { // see http://en.wikipedia.org/wiki/Approximations_of_%CF%80 // Below is the simple Newton's equation final int LOOPS = 100; final short SCALE = 33; Decimal128 current = new Decimal128(1, SCALE); Decimal128 multiplier = new Decimal128(); Decimal128 dividor = new Decimal128(); Decimal128 one = new Decimal128(1); for (int i = LOOPS; i > 0; --i) { multiplier.update(i, SCALE); current.multiplyDestructive(multiplier, SCALE); dividor.update(1 + 2 * i, SCALE); current.divideDestructive(dividor, SCALE); current.addDestructive(one, SCALE); } current.multiplyDestructive(new Decimal128(2), SCALE); assertTrue(current.toFormalString().startsWith("3.141592653589793238")); }
/** * Copy the value of given object and assigns a custom scale. * * @param o * object to copy from */ public Decimal128 update(Decimal128 o, short scale) { update(o); this.changeScaleDestructive(scale); return this; }
/** * Multiplies this with this, updating this * * @return self */ public Decimal128 squareDestructive() { this.multiplyDestructive(this, this.getScale()); return this; }
/** * Calculates addition and puts the result into the given object. Both * operands are first scaled up/down to the specified scale, then this method * performs the operation. This method is static and not destructive (except * the result object). * <p> * Unchecked exceptions: ArithmeticException an invalid scale is specified or * overflow. * </p> * * @param left * left operand * @param right * right operand * @param scale * scale of the result. must be 0 or positive. * @param result * object to receive the calculation result */ public static void add(Decimal128 left, Decimal128 right, Decimal128 result, short scale) { result.update(left); result.addDestructive(right, scale); }
/** * For UDAF variance we use the algorithm described by Chan, Golub, and LeVeque in * "Algorithms for computing the sample variance: analysis and recommendations" * The American Statistician, 37 (1983) pp. 242--247. * * variance = variance1 + variance2 + n/(m*(m+n)) * pow(((m/n)*t1 - t2),2) * * where: - variance is sum[x-avg^2] (this is actually n times the variance) * and is updated at every step. - n is the count of elements in chunk1 - m is * the count of elements in chunk2 - t1 = sum of elements in chunk1, t2 = * sum of elements in chunk2. * * This is a helper function doing the intermediate computation: * t = myagg.count*value - myagg.sum; * myagg.variance += (t*t) / ((double)myagg.count*(myagg.count-1)); * * @return self */ public Decimal128 updateVarianceDestructive( Decimal128 scratch, Decimal128 value, Decimal128 sum, long count) { scratch.update(count); scratch.multiplyDestructive(value, value.getScale()); scratch.subtractDestructive(sum, sum.getScale()); scratch.squareDestructive(); scratch.unscaledValue.divideDestructive(count * (count-1)); this.addDestructive(scratch, getScale()); return this; }
/** * Performs decimal modulo * <p> * The definition of modulo (x % p) is: * x - IntegerPart(x / p, resultScale) * p * </p> * * @param left * is x * @param right * is p * @param result * receives the result * @param scale * scale of result */ public static void modulo(Decimal128 left, Decimal128 right, Decimal128 result, short scale) { // set result to x / p (the quotient) Decimal128.divide(left, right, result, scale); // take integer part of it result.zeroFractionPart(); // multiply by p result.multiplyDestructive(right, scale); // negate it result.negateDestructive(); // add x to it result.addDestructive(left, scale); }
HiveDecimal hd = HiveDecimal.create(this.toBigDecimal()); return hd.longValue();
a = new Decimal128(); sA = makeNumericString(aDigits); a.update(sA, (short) 0); b = new Decimal128(); sB = makeNumericString(bDigits); b.update(sB, (short) 0); if (b.isZero()) { r = new Decimal128(); r.addDestructive(a, (short) 0); r.divideDestructive(b, (short) 0); String res1 = r.toFormalString(); String message = "For operation " + a.toFormalString() + " / " + b.toFormalString(); assertEquals(message, res2, res1);
@Override public String toString() { return toFormalString() + "(Decimal128: scale=" + scale + ", signum=" + signum + ", BigDecimal.toString=" + toBigDecimal().toString() + ", unscaledValue=[" + unscaledValue.toString() + "])"; }
@Test public void testMultiply() { Decimal128 result = new Decimal128(); Decimal128.multiply(one, two, result, (short) 2); assertEquals(0, two.compareTo(result)); Decimal128.multiply(two, two, result, (short) 2); assertEquals(0, new Decimal128(4L, (short) 0).compareTo(result)); Decimal128 left = new Decimal128(l1, (short) 0); Decimal128 right = new Decimal128(l2, (short) 0); UnsignedInt128 unscaled = new UnsignedInt128(l1) .multiplyConstructive(new UnsignedInt128(l2)); Decimal128 ans = new Decimal128(unscaled, (short) 0, false); Decimal128.multiply(left, right, result, (short) 0); assertEquals(0, ans.compareTo(result)); Decimal128.multiply(right, left, result, (short) 0); assertEquals(0, ans.compareTo(result)); Decimal128.multiply(new Decimal128(1.123d, (short) 10), new Decimal128( 4.321d, (short) 10), result, (short) 10); assertEquals(1.123d * 4.321d, result.doubleValue(), 0.00001d); assertNotEquals(1.123d * 4.321d, result.doubleValue(), 0.00000000000000001d); Decimal128.multiply(new Decimal128(1.123d, (short) 2), new Decimal128( 4.321d, (short) 2), result, (short) 2); assertEquals(1.123d * 4.321d, result.doubleValue(), 1.0d); assertNotEquals(1.123d * 4.321d, result.doubleValue(), 0.000001d);
this.changeScaleDestructive(scale); if (right.signum == 0) { return this; this.update(right); this.changeScaleDestructive(scale); this.negateDestructive(); return this;
@Test public void testSubtract() { Decimal128 result = new Decimal128(); Decimal128.subtract(one, two, result, (short) 2); assertEquals(0, new Decimal128(-1L, (short) 0).compareTo(result)); Decimal128.subtract(two, one, result, (short) 2); assertEquals(0, new Decimal128(1L, (short) 0).compareTo(result)); Decimal128.subtract(two, two, result, (short) 1); assertEquals(0, zero.compareTo(result)); assertEquals(0, result.getSignum()); long l1 = 123456789012345L; long l2 = 987654321097L; long sub = l1 - l2; Decimal128 left = new Decimal128(l1, (short) 3); Decimal128 right = new Decimal128(l2, (short) 5); Decimal128.subtract(left, right, result, (short) 2); assertEquals(0, new Decimal128(sub, (short) 0).compareTo(result)); Decimal128.subtract(right, left, result, (short) 2); assertEquals(0, new Decimal128(-sub, (short) 0).compareTo(result)); Decimal128 val = new Decimal128("1.123", (short) 3); val.addDestructive(new Decimal128("4.321", (short) 3), (short) 3); assertEquals("5.444", val.toFormalString()); }
/** * Verify that a * b / b == a * for decimal division for scale 0 with integer inputs. * * Not valid if abs(a * b) >= 10**38. */ private void verifyMultiplyDivideInverse(long a, long b) { final short scale = 0; // ignore zero-divide cases if (b == 0) { return; } Decimal128 decA = new Decimal128(a, scale); Decimal128 decB = new Decimal128(b, scale); decA.multiplyDestructive(decB, scale); decA.checkPrecisionOverflow(38); // caller must make sure product of inputs is not too big decA.divideDestructive(decB, scale); assertEquals("Error for a = " + Long.toString(a) + ", b = " + Long.toString(b), new Decimal128(a, scale), decA); }
/** * Update the value of this object with the given {@code long}. The scale of * the {@code Decimal128} is zero. * * @param val * {@code long} value to be set to {@code Decimal128}. */ public Decimal128 update(long val) { return update(val, (short) 0); }
@Test public void testDivide() { Decimal128 quotient = new Decimal128(); Decimal128.divide(two, one, quotient, (short) 2); assertEquals(0, quotient.compareTo(two)); Decimal128.divide(two, two, quotient, (short) 2); assertEquals(0, quotient.compareTo(one)); Decimal128 three = new Decimal128(3); Decimal128 four = new Decimal128(4); Decimal128.divide(three, four, quotient, (short) 2); assertEquals("0.75", quotient.toFormalString()); Decimal128.divide(three, four, quotient, (short) 1); assertEquals("0.8", quotient.toFormalString()); Decimal128.divide(three, four, quotient, (short) 0); assertEquals("1", quotient.toFormalString()); Decimal128 two = new Decimal128(2); Decimal128.divide(two, three, quotient, (short) 4); assertEquals("0.6667", quotient.toFormalString()); }
/** * Calculates subtraction and puts the result into the given object. Both * operands are first scaled up/down to the specified scale, then this method * performs the operation. This method is static and not destructive (except * the result object). * <p> * Unchecked exceptions: ArithmeticException an invalid scale is specified or * overflow. * </p> * * @param left * left operand * @param right * right operand * @param result * object to receive the calculation result * @param scale * scale of the result. must be 0 or positive. */ public static void subtract(Decimal128 left, Decimal128 right, Decimal128 result, short scale) { result.update(left); result.subtractDestructive(right, scale); }
/** * Performs division and puts the result into the given object. Both operands * are first scaled up/down to the specified scale, then this method performs * the operation. This method is static and not destructive (except the result * object). * <p> * Unchecked exceptions: ArithmeticException an invalid scale is specified or * overflow. * </p> * * @param left * left operand * @param right * right operand * @param quotient * result object to receive the calculation result * @param scale * scale of the result. must be 0 or positive. */ public static void divide(Decimal128 left, Decimal128 right, Decimal128 quotient, short scale) { if (quotient == left || quotient == right) { throw new IllegalArgumentException( "result object cannot be left or right operand"); } quotient.update(left); quotient.divideDestructive(right, scale); }
/** * Calculates multiplication and puts the result into the given object. Both * operands are first scaled up/down to the specified scale, then this method * performs the operation. This method is static and not destructive (except * the result object). * <p> * Unchecked exceptions: ArithmeticException an invalid scale is specified or * overflow. * </p> * * @param left * left operand * @param right * right operand * @param result * object to receive the calculation result * @param scale * scale of the result. must be 0 or positive. */ public static void multiply(Decimal128 left, Decimal128 right, Decimal128 result, short scale) { if (result == left || result == right) { throw new IllegalArgumentException( "result object cannot be left or right operand"); } result.update(left); result.multiplyDestructive(right, scale); }
/** * Verify that (a + b) - b == a * for decimal add and subtract for scale 0 with long integer inputs. */ private void verifyAddSubtractInverse(long a, long b) { final short scale = 0; Decimal128 decA = new Decimal128(a, scale); Decimal128 decB = new Decimal128(b, scale); decA.addDestructive(decB, scale); decA.subtractDestructive(decB, scale); assertEquals("Error for a = " + Long.toString(a) + ", b = " + Long.toString(b), new Decimal128(a, scale), decA); }