diff --git a/storage-api/src/java/org/apache/hadoop/hive/common/type/FastHiveDecimalImpl.java b/storage-api/src/java/org/apache/hadoop/hive/common/type/FastHiveDecimalImpl.java index f733c1ecb5..e41d7ba039 100644 --- a/storage-api/src/java/org/apache/hadoop/hive/common/type/FastHiveDecimalImpl.java +++ b/storage-api/src/java/org/apache/hadoop/hive/common/type/FastHiveDecimalImpl.java @@ -425,7 +425,7 @@ public static boolean fastSetFromBytes(byte[] bytes, int offset, int length, boo int integerDigitCount = precision; - int nonTrailingZeroScale = 0; + int scaleCount = 0; boolean roundingNecessary = false; if (sawDot) { @@ -478,9 +478,7 @@ public static boolean fastSetFromBytes(byte[] bytes, int offset, int length, boo multiplier /= 10; digitNum--; precision++; - if (digitValue != 0) { - nonTrailingZeroScale = precision - integerDigitCount; - } + scaleCount = precision - integerDigitCount; if (++index >= end) { break; } @@ -511,12 +509,12 @@ public static boolean fastSetFromBytes(byte[] bytes, int offset, int length, boo } int trailingZeroesScale = precision - integerDigitCount; - if (integerDigitCount == 0 && nonTrailingZeroScale == 0) { + if (integerDigitCount == 0 && scaleCount == 0) { // Zero(es). } else { fastResult.fastSignum = (isNegative ? -1 : 1); fastResult.fastIntegerDigitCount = integerDigitCount; - fastResult.fastScale = nonTrailingZeroScale; + fastResult.fastScale = scaleCount; final int trailingZeroCount = trailingZeroesScale - fastResult.fastScale; final int scaleDown = HiveDecimal.MAX_PRECISION - precision + trailingZeroCount; if (scaleDown > 0) { @@ -617,7 +615,7 @@ public static boolean fastSetFromBytes(byte[] bytes, int offset, int length, boo } - if (integerDigitCount == 0 && nonTrailingZeroScale == 0) { + if (integerDigitCount == 0 && scaleCount == 0) { // Zero(es). return true; } @@ -863,6 +861,7 @@ public static boolean fastSetFromBigDecimal( // System.out.println("FAST_SET_FROM_BIG_DECIMAL adjusted for zeroes/scale " + bigDecimal + " scale " + bigDecimal.scale()); BigInteger unscaledValue = bigDecimal.unscaledValue(); + int precision = bigDecimal.precision(); // System.out.println("FAST_SET_FROM_BIG_DECIMAL unscaledValue " + unscaledValue + " length " + unscaledValue.toString().length()); final int scale = bigDecimal.scale(); @@ -881,7 +880,7 @@ public static boolean fastSetFromBigDecimal( return true; } // This method will scale down and round to fit, if necessary. - if (!fastSetFromBigInteger(unscaledValue, scale, fastResult)) { + if (!fastSetFromBigInteger(unscaledValue, scale, precision, fastResult)) { return false; } return true; @@ -1128,6 +1127,24 @@ public static boolean fastSetFromBigInteger( */ public static boolean fastSetFromBigInteger( BigInteger bigInteger, int scale, FastHiveDecimal fastResult) { + // Poor performance, because the precision will be calculated by bigInteger.toString() + return fastSetFromBigInteger(bigInteger, scale, -1, fastResult); + } + + /** + * Creates a fast decimal from a BigInteger with a specified scale. + * + * NOTE: The fastSetFromBigInteger method requires the caller to pass a fastResult + * parameter has been reset for better performance. + * + * @param bigInteger the value to set as an integer + * @param scale the scale to use + * @param precision the precision to use + * @param fastResult an object to reuse + * @return True if the BigInteger and scale were successfully converted to a decimal. + */ + public static boolean fastSetFromBigInteger( + BigInteger bigInteger, int scale, int precision, FastHiveDecimal fastResult) { if (scale < 0) { @@ -1150,8 +1167,10 @@ public static boolean fastSetFromBigInteger( bigInteger = bigInteger.negate(); } - // A slow way to get the number of decimal digits. - int precision = bigInteger.toString().length(); + if (precision < 0) { + // A slow way to get the number of decimal digits. + precision = bigInteger.toString().length(); + } // System.out.println("FAST_SET_FROM_BIG_INTEGER adjusted bigInteger " + bigInteger + " precision " + precision); @@ -8802,8 +8821,14 @@ public static boolean fastDivide( BigDecimal divisor = fastBigDecimalValue( rightSignum, rightFast0, rightFast1, rightFast2, rightIntegerDigitCount, rightScale); + // the formula to evaluate the scale of divide operation is according to the original design: + // https://cwiki.apache.org/confluence/download/attachments/27362075/Hive_Decimal_Precision_Scale_Support.pdf + int scale = Math.max(6, leftScale + rightIntegerDigitCount + 1); + if (scale > HiveDecimal.MAX_SCALE) { + scale = HiveDecimal.MAX_SCALE; + } BigDecimal quotient = - denominator.divide(divisor, HiveDecimal.MAX_SCALE, BigDecimal.ROUND_HALF_UP); + denominator.divide(divisor, scale, BigDecimal.ROUND_HALF_UP); if (!fastSetFromBigDecimal( quotient,