diff --git common/src/java/org/apache/hadoop/hive/common/type/Decimal128.java common/src/java/org/apache/hadoop/hive/common/type/Decimal128.java index a5d7399..84bfcbe 100644 --- common/src/java/org/apache/hadoop/hive/common/type/Decimal128.java +++ common/src/java/org/apache/hadoop/hive/common/type/Decimal128.java @@ -1576,23 +1576,58 @@ public int hashCode() { */ @Override public long longValue() { + + // Avoid allocating temporary variables for special cases: signum or scale is zero if (signum == 0) { return 0L; } - - long ret; - UnsignedInt128 tmp; if (scale == 0) { + long ret; ret = this.unscaledValue.getV1(); ret <<= 32L; ret |= SqlMathUtil.LONG_MASK & this.unscaledValue.getV0(); + if (signum >= 0) { + return ret; + } else { + return -ret; + } } else { - tmp = new UnsignedInt128(this.unscaledValue); - tmp.scaleDownTenDestructive(scale); - ret = tmp.getV1(); - ret <<= 32L; - ret |= SqlMathUtil.LONG_MASK & tmp.getV0(); + Decimal128 dec = new Decimal128(this); + UnsignedInt128 scratch = new UnsignedInt128(); + return dec.longValueDestructive(scratch); + } + } + + /** + * Converts this {@code Decimal128} to a {@code long}. This conversion is + * analogous to the narrowing primitive conversion from {@code double} + * to {@code short} as defined in section 5.1.3 of The Java™ + * Language Specification: any fractional part of this + * {@code Decimal128} will be discarded, and if the resulting " + * {@code UnsignedInt128}" is too big to fit in a {@code long}, only the + * low-order 64 bits are returned. Note that this conversion can lose + * information about the overall magnitude and precision of this + * {@code Decimal128} value. + * + * @return this {@code Decimal128} converted to a {@code long}. + */ + public long longValueDestructive(UnsignedInt128 scratchUnsignedInt128) { + if (signum == 0) { + return 0L; + } + + long ret; + if (scale > 0) { + /* + * Divide by a power of 10 equal to 10**scale to logically shift the digits + * places right by "scale" positions to discard them. + */ + UnsignedInt128 powerTenDivisor = SqlMathUtil.POWER_TENS_INT128[scale]; + this.getUnscaledValue().divideDestructive(powerTenDivisor, scratchUnsignedInt128); } + ret = this.unscaledValue.getV1(); + ret <<= 32L; + ret |= SqlMathUtil.LONG_MASK & this.unscaledValue.getV0(); if (signum >= 0) { return ret; diff --git common/src/test/org/apache/hadoop/hive/common/type/TestDecimal128.java common/src/test/org/apache/hadoop/hive/common/type/TestDecimal128.java index 426c03d..91bb8b5 100644 --- common/src/test/org/apache/hadoop/hive/common/type/TestDecimal128.java +++ common/src/test/org/apache/hadoop/hive/common/type/TestDecimal128.java @@ -786,5 +786,11 @@ public void testToLong() { assertEquals(4294967295L, d.longValue()); d.update("4294967296.01", (short) 2); // 2^32 + .01 assertEquals(4294967296L, d.longValue()); + + // Compare long value with HiveDecimal#longValue + d.update(37.678, (short)5); + HiveDecimal hd = HiveDecimal.create(BigDecimal.valueOf(37.678)); + assertEquals(hd.longValue(), d.longValue()); } + } diff --git ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/CastDecimalToLong.java ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/CastDecimalToLong.java index d5f34d5..d591dcc 100644 --- ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/CastDecimalToLong.java +++ ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/CastDecimalToLong.java @@ -18,6 +18,8 @@ package org.apache.hadoop.hive.ql.exec.vector.expressions; +import org.apache.hadoop.hive.common.type.Decimal128; +import org.apache.hadoop.hive.common.type.UnsignedInt128; import org.apache.hadoop.hive.ql.exec.vector.DecimalColumnVector; import org.apache.hadoop.hive.ql.exec.vector.LongColumnVector; @@ -27,6 +29,9 @@ public class CastDecimalToLong extends FuncDecimalToLong { private static final long serialVersionUID = 1L; + private Decimal128 scratchDecimal = new Decimal128(); + private UnsignedInt128 scratchUnsigned = new UnsignedInt128(); + public CastDecimalToLong() { super(); } @@ -37,6 +42,7 @@ public CastDecimalToLong(int inputColumn, int outputColumn) { @Override protected void func(LongColumnVector outV, DecimalColumnVector inV, int i) { - outV.vector[i] = inV.vector[i].longValue(); + scratchDecimal.update(inV.vector[i]); + outV.vector[i] = scratchDecimal.longValueDestructive(scratchUnsigned); } } diff --git ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/CastDecimalToTimestamp.java ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/CastDecimalToTimestamp.java index df7e1ee..0a4e9b5 100644 --- ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/CastDecimalToTimestamp.java +++ ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/CastDecimalToTimestamp.java @@ -20,6 +20,7 @@ import org.apache.hadoop.hive.common.type.Decimal128; import org.apache.hadoop.hive.common.type.SqlMathUtil; +import org.apache.hadoop.hive.common.type.UnsignedInt128; import org.apache.hadoop.hive.ql.exec.vector.DecimalColumnVector; import org.apache.hadoop.hive.ql.exec.vector.LongColumnVector; @@ -37,6 +38,7 @@ * its own copy of the variable. */ private transient Decimal128 tmp = null; + private transient UnsignedInt128 scratcUint128 = new UnsignedInt128(); private static transient Decimal128 tenE9 = new Decimal128(1000000000); public CastDecimalToTimestamp(int inputColumn, int outputColumn) { @@ -56,6 +58,6 @@ protected void func(LongColumnVector outV, DecimalColumnVector inV, int i) { tmp.multiplyDestructive(tenE9, (short) 0); // set output - outV.vector[i] = tmp.longValue(); + outV.vector[i] = tmp.longValueDestructive(scratcUint128); } }