This is an automated email from the ASF dual-hosted git repository.

deniskuzZ pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hive.git


The following commit(s) were added to refs/heads/master by this push:
     new 32efdcd9e6c HIVE-26731: UDF trunc produces incorrect result for higher 
values (#6878)
32efdcd9e6c is described below

commit 32efdcd9e6cfdfbecd30a4bf454c66f76be6281d
Author: gopigowtham <[email protected]>
AuthorDate: Mon Oct 19 21:19:30 2026 +0530

    HIVE-26731: UDF trunc produces incorrect result for higher values (#6878)
---
 .../ql/exec/vector/expressions/TruncDecimal.java   |  24 +--
 .../ql/exec/vector/expressions/TruncFloat.java     |  24 +--
 .../hive/ql/udf/generic/GenericUDFTrunc.java       |  81 +++------
 .../hadoop/hive/ql/udf/generic/TruncUtils.java     | 101 ++++++++++++
 .../expressions/TestVectorMathFunctions.java       | 101 ++++++++++++
 .../hive/ql/udf/generic/TestGenericUDFTrunc.java   | 182 +++++++++++++++++++++
 .../clientpositive/udf_trunc_large_number.q        |  34 ++++
 .../llap/udf_trunc_large_number.q.out              | 171 +++++++++++++++++++
 .../hive/common/type/FastHiveDecimalImpl.java      |  10 ++
 .../hadoop/hive/common/type/TestHiveDecimal.java   |  11 ++
 10 files changed, 640 insertions(+), 99 deletions(-)

diff --git 
a/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/TruncDecimal.java
 
b/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/TruncDecimal.java
index 8dbd4d8a56c..b16f268cb22 100644
--- 
a/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/TruncDecimal.java
+++ 
b/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/TruncDecimal.java
@@ -19,11 +19,11 @@
 
 package org.apache.hadoop.hive.ql.exec.vector.expressions;
 
-import org.apache.hadoop.hive.common.type.HiveDecimal;
 import org.apache.hadoop.hive.ql.exec.vector.ColumnVector;
 import org.apache.hadoop.hive.ql.exec.vector.DecimalColumnVector;
 import org.apache.hadoop.hive.ql.exec.vector.VectorExpressionDescriptor;
 import 
org.apache.hadoop.hive.ql.exec.vector.VectorExpressionDescriptor.ArgumentType;
+import org.apache.hadoop.hive.ql.udf.generic.TruncUtils;
 import org.apache.hadoop.hive.serde2.io.HiveDecimalWritable;
 
 /**
@@ -45,26 +45,8 @@ public TruncDecimal(int colNum, int scale, int 
outputColumnNum) {
 
   @Override
   protected void trunc(ColumnVector inputColVector, ColumnVector 
outputColVector, int i) {
-    HiveDecimal input = ((DecimalColumnVector) 
inputColVector).vector[i].getHiveDecimal();
-
-    HiveDecimal output = trunc(input);
-    ((DecimalColumnVector) outputColVector).vector[i] = new 
HiveDecimalWritable(output);
-  }
-
-  protected HiveDecimal trunc(HiveDecimal input) {
-    HiveDecimal pow = HiveDecimal.create(Math.pow(10, Math.abs(scale)));
-
-    if (scale >= 0) {
-      if (scale != 0) {
-        long longValue = input.multiply(pow).longValue();
-        return HiveDecimal.create(longValue).divide(pow);
-      } else {
-        return HiveDecimal.create(input.longValue());
-      }
-    } else {
-      long longValue2 = input.divide(pow).longValue();
-      return HiveDecimal.create(longValue2).multiply(pow);
-    }
+    HiveDecimalWritable input = ((DecimalColumnVector) 
inputColVector).vector[i];
+    TruncUtils.trunc(input, scale, ((DecimalColumnVector) 
outputColVector).vector[i]);
   }
 
   protected ArgumentType getInputColumnType() {
diff --git 
a/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/TruncFloat.java 
b/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/TruncFloat.java
index 5870b440440..622e0690481 100644
--- 
a/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/TruncFloat.java
+++ 
b/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/expressions/TruncFloat.java
@@ -19,7 +19,6 @@
 
 package org.apache.hadoop.hive.ql.exec.vector.expressions;
 
-import java.math.BigDecimal;
 import java.util.Arrays;
 
 import org.apache.hadoop.hive.ql.exec.vector.ColumnVector;
@@ -29,6 +28,7 @@
 import 
org.apache.hadoop.hive.ql.exec.vector.VectorExpressionDescriptor.Descriptor;
 import org.apache.hadoop.hive.ql.exec.vector.VectorizedRowBatch;
 import org.apache.hadoop.hive.ql.metadata.HiveException;
+import org.apache.hadoop.hive.ql.udf.generic.TruncUtils;
 
 /**
  * Vectorized implementation of trunc(number, scale) function for float/double 
input
@@ -127,26 +127,8 @@ public void evaluate(VectorizedRowBatch batch) throws 
HiveException {
   }
 
   protected void trunc(ColumnVector inputColVector, ColumnVector 
outputColVector, int i) {
-    BigDecimal input = BigDecimal.valueOf(((DoubleColumnVector) 
inputColVector).vector[i]);
-
-    double output = trunc(input).doubleValue();
-    ((DoubleColumnVector) outputColVector).vector[i] = output;
-  }
-
-  protected BigDecimal trunc(BigDecimal input) {
-    BigDecimal pow = BigDecimal.valueOf(Math.pow(10, Math.abs(scale)));
-
-    if (scale >= 0) {
-      if (scale != 0) {
-        long longValue = input.multiply(pow).longValue();
-        return BigDecimal.valueOf(longValue).divide(pow);
-      } else {
-        return BigDecimal.valueOf(input.longValue());
-      }
-    } else {
-      long longValue2 = input.divide(pow).longValue();
-      return BigDecimal.valueOf(longValue2).multiply(pow);
-    }
+    double input = ((DoubleColumnVector) inputColVector).vector[i];
+    ((DoubleColumnVector) outputColVector).vector[i] = TruncUtils.trunc(input, 
scale);
   }
 
   @Override
diff --git 
a/ql/src/java/org/apache/hadoop/hive/ql/udf/generic/GenericUDFTrunc.java 
b/ql/src/java/org/apache/hadoop/hive/ql/udf/generic/GenericUDFTrunc.java
index bccb81b4a1b..0f1e6020c8f 100644
--- a/ql/src/java/org/apache/hadoop/hive/ql/udf/generic/GenericUDFTrunc.java
+++ b/ql/src/java/org/apache/hadoop/hive/ql/udf/generic/GenericUDFTrunc.java
@@ -19,10 +19,7 @@
 
 package org.apache.hadoop.hive.ql.udf.generic;
 
-import java.math.BigDecimal;
-
 import org.apache.hadoop.hive.common.type.Date;
-import org.apache.hadoop.hive.common.type.HiveDecimal;
 import org.apache.hadoop.hive.common.type.Timestamp;
 import org.apache.hadoop.hive.ql.exec.Description;
 import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
@@ -97,6 +94,15 @@ public class GenericUDFTrunc extends GenericUDF {
   private transient PrimitiveCategory inputType2;
   private final Date date = new Date();
   private final Text output = new Text();
+
+  private final ByteWritable byteOutput = new ByteWritable();
+  private final ShortWritable shortOutput = new ShortWritable();
+  private final IntWritable intOutput = new IntWritable();
+  private final LongWritable longOutput = new LongWritable();
+  private final FloatWritable floatOutput = new FloatWritable();
+  private final DoubleWritable doubleOutput = new DoubleWritable();
+  private final HiveDecimalWritable decimalOutput = new HiveDecimalWritable();
+
   private transient String fmtInput;
   private transient PrimitiveObjectInspector inputOI;
   private transient PrimitiveObjectInspector inputScaleOI;
@@ -388,44 +394,44 @@ private Object evaluateNumber(DeferredObject[] arguments)
     case DECIMAL:
       HiveDecimalWritable decimalWritable =
           (HiveDecimalWritable) inputOI.getPrimitiveWritableObject(input);
-      HiveDecimal dec = trunc(decimalWritable.getHiveDecimal(), scale);
-      if (dec == null) {
-        return null;
-      }
-      return new HiveDecimalWritable(dec);
+      TruncUtils.trunc(decimalWritable, scale, decimalOutput);
+      return decimalOutput;
     case BYTE:
       ByteWritable byteWritable = (ByteWritable) 
inputOI.getPrimitiveWritableObject(input);
       if (scale >= 0) {
         return byteWritable;
-      } else {
-        return new ByteWritable((byte) trunc(byteWritable.get(), scale));
       }
+      byteOutput.set((byte) TruncUtils.trunc(byteWritable.get(), scale));
+      return byteOutput;
     case SHORT:
       ShortWritable shortWritable = (ShortWritable) 
inputOI.getPrimitiveWritableObject(input);
       if (scale >= 0) {
         return shortWritable;
-      } else {
-        return new ShortWritable((short) trunc(shortWritable.get(), scale));
       }
+      shortOutput.set((short) TruncUtils.trunc(shortWritable.get(), scale));
+      return shortOutput;
     case INT:
       IntWritable intWritable = (IntWritable) 
inputOI.getPrimitiveWritableObject(input);
       if (scale >= 0) {
         return intWritable;
-      } else {
-        return new IntWritable((int) trunc(intWritable.get(), scale));
       }
+      intOutput.set((int) TruncUtils.trunc(intWritable.get(), scale));
+      return intOutput;
     case LONG:
       LongWritable longWritable = (LongWritable) 
inputOI.getPrimitiveWritableObject(input);
       if (scale >= 0) {
         return longWritable;
-      } else {
-        return new LongWritable(trunc(longWritable.get(), scale));
       }
+      longOutput.set(TruncUtils.trunc(longWritable.get(), scale));
+      return longOutput;
     case FLOAT:
       float f = ((FloatWritable) 
inputOI.getPrimitiveWritableObject(input)).get();
-      return new FloatWritable((float) trunc(f, scale));
+      floatOutput.set((float) TruncUtils.trunc(f, scale));
+      return floatOutput;
     case DOUBLE:
-      return trunc(((DoubleWritable) 
inputOI.getPrimitiveWritableObject(input)), scale);
+      double d = ((DoubleWritable) 
inputOI.getPrimitiveWritableObject(input)).get();
+      doubleOutput.set(TruncUtils.trunc(d, scale));
+      return doubleOutput;
     default:
       throw new UDFArgumentTypeException(0,
           "Only numeric or string group data types are allowed for TRUNC 
function. Got "
@@ -458,43 +464,4 @@ private Date evalDate(Date d) throws UDFArgumentException {
       return null;
     }
   }
-
-  protected HiveDecimal trunc(HiveDecimal input, int scale) {
-    BigDecimal bigDecimal = trunc(input.bigDecimalValue(), scale);
-    return HiveDecimal.create(bigDecimal);
-  }
-
-  protected long trunc(long input, int scale) {
-    return trunc(BigDecimal.valueOf(input), scale).longValue();
-  }
-
-  protected double trunc(double input, int scale) {
-    return trunc(BigDecimal.valueOf(input), scale).doubleValue();
-  }
-
-  protected DoubleWritable trunc(DoubleWritable input, int scale) {
-    BigDecimal bigDecimal = new BigDecimal(input.get());
-    BigDecimal trunc = trunc(bigDecimal, scale);
-    DoubleWritable doubleWritable = new DoubleWritable(trunc.doubleValue());
-    return doubleWritable;
-  }
-
-  protected BigDecimal trunc(BigDecimal input, int scale) {
-    BigDecimal output = new BigDecimal(0);
-    BigDecimal pow = BigDecimal.valueOf(Math.pow(10, Math.abs(scale)));
-    if (scale >= 0) {
-      pow = BigDecimal.valueOf(Math.pow(10, scale));
-      if (scale != 0) {
-        long longValue = input.multiply(pow).longValue();
-        output = BigDecimal.valueOf(longValue).divide(pow);
-      } else {
-        output = BigDecimal.valueOf(input.longValue());
-      }
-    } else {
-      long longValue2 = input.divide(pow).longValue();
-      output = BigDecimal.valueOf(longValue2).multiply(pow);
-    }
-    return output;
-  }
-
 }
diff --git a/ql/src/java/org/apache/hadoop/hive/ql/udf/generic/TruncUtils.java 
b/ql/src/java/org/apache/hadoop/hive/ql/udf/generic/TruncUtils.java
new file mode 100644
index 00000000000..b3774499726
--- /dev/null
+++ b/ql/src/java/org/apache/hadoop/hive/ql/udf/generic/TruncUtils.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.hadoop.hive.ql.udf.generic;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+
+import org.apache.hadoop.hive.common.type.FastHiveDecimalImpl;
+import org.apache.hadoop.hive.common.type.HiveDecimal;
+import org.apache.hadoop.hive.serde2.io.HiveDecimalWritable;
+
+/**
+ * Utility class for generic trunc UDF and its vectorized implementations.
+ * <p>
+ * Truncation drops the digits after the given scale, rounding towards zero. A 
negative scale
+ * zeroes digits left of the decimal point. The input is never scaled into a 
long, so the result
+ * is exact over the whole range of the input type.
+ */
+public final class TruncUtils {
+
+  // HiveDecimal rounding takes the legacy BigDecimal rounding mode constants.
+  @SuppressWarnings("deprecation")
+  private static final int DECIMAL_ROUND_DOWN = BigDecimal.ROUND_DOWN;
+
+  // Every decimal is smaller than 10^38 in magnitude, so lower scales also 
truncate it to zero.
+  private static final int MIN_DECIMAL_SCALE = -HiveDecimal.MAX_PRECISION;
+
+  // Doubles of at least this magnitude have no fractional part.
+  private static final double MIN_INTEGRAL_DOUBLE = 0x1p52;
+
+  // Double.MAX_VALUE is about 1.8E308, so lower scales truncate every double 
to zero.
+  private static final int MIN_DOUBLE_SCALE = -309;
+
+  private TruncUtils() {
+  }
+
+  /**
+   * Truncates the input decimal into the output writable. The input is not 
modified.
+   */
+  public static void trunc(HiveDecimalWritable input, int scale, 
HiveDecimalWritable output) {
+    output.set(input);
+    output.mutateSetScale(Math.max(scale, MIN_DECIMAL_SCALE), 
DECIMAL_ROUND_DOWN);
+  }
+
+  /**
+   * Truncating a double is approximate, as the double value itself is 
approximate. Like
+   * {@link RoundUtils#round(double, int)}, this truncates the shortest 
decimal representation
+   * of the double, so trunc(0.3, 1) is 0.3 even though the double nearest to 
0.3 is below it.
+   *
+   * @param input input value
+   * @param scale decimal place
+   * @return truncated value
+   */
+  public static double trunc(double input, int scale) {
+    if (Double.isNaN(input) || Double.isInfinite(input)) {
+      return input;
+    }
+    if (scale == 0) {
+      // Common trunc(x) case, no BigDecimal needed: the cast truncates 
towards zero.
+      return Math.abs(input) < MIN_INTEGRAL_DOUBLE ? (double) (long) input : 
input;
+    }
+    if (scale < MIN_DOUBLE_SCALE) {
+      return 0.0;
+    }
+    BigDecimal value = BigDecimal.valueOf(input);
+    // Nothing to drop. Checked first since setScale would pad the value up to 
the given scale.
+    if (scale >= value.scale()) {
+      return input;
+    }
+    return value.setScale(scale, RoundingMode.DOWN).doubleValue();
+  }
+
+  public static long trunc(long input, int scale) {
+    if (scale >= 0) {
+      return input;
+    }
+    if (scale < -FastHiveDecimalImpl.MAX_LONG_POWER_OF_TEN) {
+      // Every long is smaller than 10^19 in magnitude.
+      return 0;
+    }
+    // The remainder has the sign of the input, so this truncates towards zero.
+    return input - input % FastHiveDecimalImpl.powerOfTen(-scale);
+  }
+}
diff --git 
a/ql/src/test/org/apache/hadoop/hive/ql/exec/vector/expressions/TestVectorMathFunctions.java
 
b/ql/src/test/org/apache/hadoop/hive/ql/exec/vector/expressions/TestVectorMathFunctions.java
index 0fe2f6ea514..7a152be2fb1 100644
--- 
a/ql/src/test/org/apache/hadoop/hive/ql/exec/vector/expressions/TestVectorMathFunctions.java
+++ 
b/ql/src/test/org/apache/hadoop/hive/ql/exec/vector/expressions/TestVectorMathFunctions.java
@@ -24,12 +24,14 @@
 import java.util.Random;
 
 import org.apache.hadoop.hive.common.type.Date;
+import org.apache.hadoop.hive.common.type.HiveDecimal;
 import org.apache.hadoop.hive.conf.HiveConf;
 import org.junit.Assert;
 
 import org.apache.hadoop.hive.serde2.RandomTypeUtil;
 import org.apache.hadoop.hive.common.type.Timestamp;
 import org.apache.hadoop.hive.ql.exec.vector.BytesColumnVector;
+import org.apache.hadoop.hive.ql.exec.vector.DecimalColumnVector;
 import org.apache.hadoop.hive.ql.exec.vector.DoubleColumnVector;
 import org.apache.hadoop.hive.ql.exec.vector.LongColumnVector;
 import org.apache.hadoop.hive.ql.exec.vector.TimestampColumnVector;
@@ -120,6 +122,105 @@ public void testRoundToDecimalPlaces() throws 
HiveException {
     Assert.assertEquals(1.2346d, resultV.vector[7], Double.MIN_VALUE);
   }
 
+  @Test
+  public void testTruncDecimal() throws HiveException {
+    // HIVE-26731: the values must not be scaled into a long
+    String[] input = { "123456711889.122345897", "-123456711889.122345897",
+        "12345678901234567890123456789.987654321", null };
+    verifyTruncDecimal(new TruncDecimal(0, 8, 1), input, 
"123456711889.12234589",
+        "-123456711889.12234589", "12345678901234567890123456789.98765432", 
null);
+    verifyTruncDecimal(new TruncDecimal(0, 19, 1), input, input);
+    verifyTruncDecimal(new TruncDecimal(0, -4, 1), input, "123456710000", 
"-123456710000",
+        "12345678901234567890123450000", null);
+    verifyTruncDecimal(new TruncDecimal(0, Integer.MIN_VALUE, 1), input, "0", 
"0", "0", null);
+    verifyTruncDecimal(new TruncDecimalNoScale(0, 1), input, "123456711889", 
"-123456711889",
+        "12345678901234567890123456789", null);
+  }
+
+  private static void verifyTruncDecimal(VectorExpression expr, String[] input,
+      String... expected) throws HiveException {
+    VectorizedRowBatch batch = new VectorizedRowBatch(2, input.length);
+    DecimalColumnVector inV = new DecimalColumnVector(input.length, 38, 9);
+    DecimalColumnVector outV = new DecimalColumnVector(input.length, 38, 9);
+    for (int i = 0; i < input.length; i++) {
+      if (input[i] == null) {
+        inV.noNulls = false;
+        inV.isNull[i] = true;
+      } else {
+        inV.vector[i].set(HiveDecimal.create(input[i]));
+      }
+    }
+    batch.cols[0] = inV;
+    batch.cols[1] = outV;
+    batch.size = input.length;
+
+    expr.evaluate(batch);
+
+    for (int i = 0; i < input.length; i++) {
+      Assert.assertEquals(expected[i] == null, outV.isNull[i]);
+      if (expected[i] != null) {
+        Assert.assertEquals(expr + " row " + i, 
HiveDecimal.create(expected[i]),
+            outV.vector[i].getHiveDecimal());
+        Assert.assertEquals("input must not be modified", 
HiveDecimal.create(input[i]),
+            inV.vector[i].getHiveDecimal());
+      }
+    }
+  }
+
+  @Test
+  public void testTruncDouble() throws HiveException {
+    Double[] input = { 123456711889.12234, -123456711889.12234, 0.3, 1.0E300, 
Double.NaN, null };
+    verifyTruncDouble(new TruncFloat(0, 8, 1), input,
+        123456711889.12234, -123456711889.12234, 0.3, 1.0E300, Double.NaN, 
null);
+    verifyTruncDouble(new TruncFloat(0, 3, 1), input,
+        123456711889.122, -123456711889.122, 0.3, 1.0E300, Double.NaN, null);
+    verifyTruncDouble(new TruncFloat(0, -5, 1), input,
+        123456700000.0, -123456700000.0, 0.0, 1.0E300, Double.NaN, null);
+    verifyTruncDouble(new TruncFloatNoScale(0, 1), input,
+        123456711889.0, -123456711889.0, 0.0, 1.0E300, Double.NaN, null);
+  }
+
+  @Test
+  public void testTruncDoubleRepeating() throws HiveException {
+    VectorizedRowBatch batch = getVectorizedRowBatchDoubleInDoubleOut();
+    DoubleColumnVector inV = (DoubleColumnVector) batch.cols[0];
+    inV.vector[0] = 123456711889.12234;
+    inV.isRepeating = true;
+
+    new TruncFloat(0, 3, 1).evaluate(batch);
+
+    DoubleColumnVector outV = (DoubleColumnVector) batch.cols[1];
+    Assert.assertTrue(outV.isRepeating);
+    Assert.assertEquals(123456711889.122, outV.vector[0], 0.0);
+  }
+
+  private static void verifyTruncDouble(VectorExpression expr, Double[] input,
+      Double... expected) throws HiveException {
+    VectorizedRowBatch batch = new VectorizedRowBatch(2, input.length);
+    DoubleColumnVector inV = new DoubleColumnVector(input.length);
+    DoubleColumnVector outV = new DoubleColumnVector(input.length);
+    for (int i = 0; i < input.length; i++) {
+      if (input[i] == null) {
+        inV.noNulls = false;
+        inV.isNull[i] = true;
+      } else {
+        inV.vector[i] = input[i];
+      }
+    }
+    batch.cols[0] = inV;
+    batch.cols[1] = outV;
+    batch.size = input.length;
+
+    expr.evaluate(batch);
+
+    for (int i = 0; i < input.length; i++) {
+      Assert.assertEquals(expected[i] == null, outV.isNull[i]);
+      if (expected[i] != null) {
+        Assert.assertEquals(expr + " row " + i, expected[i], outV.vector[i], 
0.0);
+      }
+    }
+  }
+
   private static final int DAYS_LIMIT = 365 * 9999;
   //approximate, so we get some negative values:
   private static final int SMALLEST_EPOCH_DAY = -365 * 1969;
diff --git 
a/ql/src/test/org/apache/hadoop/hive/ql/udf/generic/TestGenericUDFTrunc.java 
b/ql/src/test/org/apache/hadoop/hive/ql/udf/generic/TestGenericUDFTrunc.java
index a1f21f80d71..14dca9f9681 100644
--- a/ql/src/test/org/apache/hadoop/hive/ql/udf/generic/TestGenericUDFTrunc.java
+++ b/ql/src/test/org/apache/hadoop/hive/ql/udf/generic/TestGenericUDFTrunc.java
@@ -20,18 +20,29 @@
 package org.apache.hadoop.hive.ql.udf.generic;
 
 import org.apache.hadoop.hive.common.type.Date;
+import org.apache.hadoop.hive.common.type.HiveDecimal;
 import org.apache.hadoop.hive.common.type.Timestamp;
 import org.apache.hadoop.hive.ql.metadata.HiveException;
 import org.apache.hadoop.hive.ql.udf.generic.GenericUDF.DeferredJavaObject;
 import org.apache.hadoop.hive.ql.udf.generic.GenericUDF.DeferredObject;
+import org.apache.hadoop.hive.serde2.io.ByteWritable;
 import org.apache.hadoop.hive.serde2.io.DateWritableV2;
+import org.apache.hadoop.hive.serde2.io.DoubleWritable;
+import org.apache.hadoop.hive.serde2.io.HiveDecimalWritable;
+import org.apache.hadoop.hive.serde2.io.ShortWritable;
 import org.apache.hadoop.hive.serde2.io.TimestampWritableV2;
 import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
 import 
org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
+import org.apache.hadoop.hive.serde2.typeinfo.TypeInfoFactory;
+import org.apache.hadoop.io.FloatWritable;
+import org.apache.hadoop.io.IntWritable;
+import org.apache.hadoop.io.LongWritable;
 import org.apache.hadoop.io.Text;
+import org.apache.hadoop.io.Writable;
 
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import org.junit.Test;
 
 /**
@@ -765,6 +776,177 @@ public void testDateWritableToDateWithYearFormat() throws 
HiveException {
     runAndVerify("2016-01-01", udf, initArgs, evalArgs);
   }
 
+  @Test
+  public void testDecimalLargerThanLongWhenScaled() throws HiveException {
+    // HIVE-26731: 123456711889.122345897 * 10^8 does not fit in a long
+    verifyDecimal("123456711889.12234589", "123456711889.122345897", 21, 9, 8);
+    verifyDecimal("-123456711889.12234589", "-123456711889.122345897", 21, 9, 
8);
+    verifyDecimal("123456711889.122345897", "123456711889.122345897", 21, 9, 
19);
+    verifyDecimal("0.0000000001", "0.00000000012345678901234567890123456789", 
38, 38, 10);
+  }
+
+  @Test
+  public void testDecimalIntegerPartLargerThanLong() throws HiveException {
+    String input = "12345678901234567890123456789.987654321";
+    verifyDecimal("12345678901234567890123456789.98765", input, 38, 9, 5);
+    verifyDecimal("12345678901234567890123456789", input, 38, 9, 0);
+    verifyDecimal("12345678901234567890123450000", input, 38, 9, -4);
+    verifyDecimal("-12345678901234567890123450000", "-" + input, 38, 9, -4);
+    verifyDecimal("0", input, 38, 9, -29);
+    verifyDecimal("0", input, 38, 9, Integer.MIN_VALUE);
+
+    String max = "99999999999999999999999999999999999999";
+    verifyDecimal("99999999999999999999999999999999999990", max, 38, 0, -1);
+    verifyDecimal("0", max, 38, 0, -38);
+    verifyDecimal("0", "-" + max, 38, 0, -39);
+    verifyDecimal("0", max, 38, 0, Integer.MIN_VALUE);
+  }
+
+  @Test
+  public void testDecimalWithoutScale() throws HiveException {
+    HiveDecimalWritable input = new 
HiveDecimalWritable("-12345678901234567890123456789.987654321");
+    Object result = evaluate(
+        PrimitiveObjectInspectorFactory.getPrimitiveWritableObjectInspector(
+            TypeInfoFactory.getDecimalTypeInfo(38, 9)), input);
+    assertEquals(new HiveDecimalWritable("-12345678901234567890123456789"), 
result);
+    assertEquals("input must not be modified",
+        new HiveDecimalWritable("-12345678901234567890123456789.987654321"), 
input);
+  }
+
+  @Test
+  public void testDouble() throws HiveException {
+    verifyDouble(123456711889.12234, 123456711889.12234, 8);
+    verifyDouble(123456711889.122, 123456711889.12234, 3);
+    verifyDouble(-123456711889.0, -123456711889.12234, 0);
+    verifyDouble(-123456700000.0, -123456711889.12234, -5);
+    verifyDouble(1.0E300, 1.0E300, 30);
+    verifyDouble(9.9E299, 9.99E299, -298);
+    verifyDouble(0.0, 0.5, -1);
+    // The shortest decimal representation is truncated, not the exact binary 
value
+    verifyDouble(0.3, 0.3, 1);
+    verifyDouble(0.29, 0.29, 2);
+    verifyDouble(0.0, -0.5, 0);
+    verifyDouble(Double.NaN, Double.NaN, 2);
+    verifyDouble(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 2);
+    verifyDouble(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, 0);
+  }
+
+  @Test(timeout = 10000)
+  public void testDoubleExtremeScale() throws HiveException {
+    verifyDouble(Double.MIN_VALUE, Double.MIN_VALUE, 325);
+    verifyDouble(0.0, Double.MIN_VALUE, 323);
+    verifyDouble(1.0E308, 1.7976931348623157E308, -308);
+    verifyDouble(0.0, -Double.MAX_VALUE, -309);
+    // Neither the result nor the work may depend on the magnitude of the scale
+    verifyDouble(12.5, 12.5, Integer.MAX_VALUE);
+    verifyDouble(0.0, 12.5, Integer.MIN_VALUE);
+    for (int i = 0; i < 1000; i++) {
+      verifyDouble(12.5, 12.5, 100000000);
+      verifyDouble(0.0, 12.5, -100000000);
+    }
+  }
+
+  @Test
+  public void testFloat() throws HiveException {
+    verifyFloat(12345.123f, 12345.1234f, 3);
+    verifyFloat(12345.1234f, 12345.1234f, 10);
+    verifyFloat(-12345f, -12345.1234f, 0);
+    verifyFloat(-12300f, -12345.1234f, -2);
+  }
+
+  @Test
+  public void testIntegerTypes() throws HiveException {
+    verifyLong(Long.MAX_VALUE, Long.MAX_VALUE, 2);
+    verifyLong(9223372036854775000L, Long.MAX_VALUE, -3);
+    verifyLong(-9000000000000000000L, Long.MIN_VALUE, -18);
+    verifyLong(0L, Long.MIN_VALUE, -19);
+    verifyLong(0L, Long.MAX_VALUE, Integer.MIN_VALUE);
+
+    assertEquals(new IntWritable(-2147483000), evaluate(
+        PrimitiveObjectInspectorFactory.writableIntObjectInspector, new 
IntWritable(-2147483648), -3));
+    assertEquals(new IntWritable(2147483647), evaluate(
+        PrimitiveObjectInspectorFactory.writableIntObjectInspector, new 
IntWritable(2147483647), 3));
+    assertEquals(new ShortWritable((short) -32700), evaluate(
+        PrimitiveObjectInspectorFactory.writableShortObjectInspector,
+        new ShortWritable((short) -32768), -2));
+    assertEquals(new ByteWritable((byte) 120), evaluate(
+        PrimitiveObjectInspectorFactory.writableByteObjectInspector, new 
ByteWritable((byte) 127), -1));
+  }
+
+  @Test
+  public void testNonConstantScale() throws HiveException {
+    GenericUDFTrunc udf = new GenericUDFTrunc();
+    udf.initialize(new ObjectInspector[] {
+        PrimitiveObjectInspectorFactory.getPrimitiveWritableObjectInspector(
+            TypeInfoFactory.getDecimalTypeInfo(21, 9)),
+        PrimitiveObjectInspectorFactory.writableIntObjectInspector });
+
+    HiveDecimalWritable input = new 
HiveDecimalWritable("123456711889.122345897");
+    int[] scales = { 8, 0, -4, 19 };
+    String[] expected = { "123456711889.12234589", "123456711889", 
"123456710000",
+        "123456711889.122345897" };
+    for (int i = 0; i < scales.length; i++) {
+      Object result = udf.evaluate(new DeferredObject[] {
+          new DeferredJavaObject(input), new DeferredJavaObject(new 
IntWritable(scales[i])) });
+      assertEquals("scale " + scales[i], new HiveDecimalWritable(expected[i]), 
result);
+    }
+    assertEquals("input must not be modified", new 
HiveDecimalWritable("123456711889.122345897"),
+        input);
+  }
+
+  @Test
+  public void testNullInput() throws HiveException {
+    
assertNull(evaluate(PrimitiveObjectInspectorFactory.writableDoubleObjectInspector,
 null, 2));
+  }
+
+  private static void verifyDecimal(String expected, String input, int 
precision, int scale,
+      int truncScale) throws HiveException {
+    Object result = evaluate(
+        PrimitiveObjectInspectorFactory.getPrimitiveWritableObjectInspector(
+            TypeInfoFactory.getDecimalTypeInfo(precision, scale)),
+        new HiveDecimalWritable(input), truncScale);
+    assertEquals("trunc(" + input + ", " + truncScale + ")",
+        HiveDecimal.create(expected), ((HiveDecimalWritable) 
result).getHiveDecimal());
+  }
+
+  private static void verifyDouble(double expected, double input, int scale)
+      throws HiveException {
+    Object result = 
evaluate(PrimitiveObjectInspectorFactory.writableDoubleObjectInspector,
+        new DoubleWritable(input), scale);
+    assertEquals("trunc(" + input + ", " + scale + ")", expected,
+        ((DoubleWritable) result).get(), 0.0);
+  }
+
+  private static void verifyFloat(float expected, float input, int scale) 
throws HiveException {
+    Object result = 
evaluate(PrimitiveObjectInspectorFactory.writableFloatObjectInspector,
+        new FloatWritable(input), scale);
+    assertEquals("trunc(" + input + ", " + scale + ")", expected,
+        ((FloatWritable) result).get(), 0.0f);
+  }
+
+  private static void verifyLong(long expected, long input, int scale) throws 
HiveException {
+    Object result = 
evaluate(PrimitiveObjectInspectorFactory.writableLongObjectInspector,
+        new LongWritable(input), scale);
+    assertEquals("trunc(" + input + ", " + scale + ")", new 
LongWritable(expected), result);
+  }
+
+  private static Object evaluate(ObjectInspector valueOI, Writable value, int 
scale)
+      throws HiveException {
+    GenericUDFTrunc udf = new GenericUDFTrunc();
+    IntWritable scaleWritable = new IntWritable(scale);
+    udf.initialize(new ObjectInspector[] { valueOI,
+        
PrimitiveObjectInspectorFactory.getPrimitiveWritableConstantObjectInspector(
+            TypeInfoFactory.intTypeInfo, scaleWritable) });
+    return udf.evaluate(new DeferredObject[] {
+        new DeferredJavaObject(value), new DeferredJavaObject(scaleWritable) 
});
+  }
+
+  private static Object evaluate(ObjectInspector valueOI, Writable value) 
throws HiveException {
+    GenericUDFTrunc udf = new GenericUDFTrunc();
+    udf.initialize(new ObjectInspector[] { valueOI });
+    return udf.evaluate(new DeferredObject[] { new DeferredJavaObject(value) 
});
+  }
+
   private void runAndVerify(String expResult, GenericUDF udf, 
ObjectInspector[] initArgs,
       DeferredObject[] evalArgs) throws HiveException {
     udf.initialize(initArgs);
diff --git a/ql/src/test/queries/clientpositive/udf_trunc_large_number.q 
b/ql/src/test/queries/clientpositive/udf_trunc_large_number.q
new file mode 100644
index 00000000000..35204ddbbcc
--- /dev/null
+++ b/ql/src/test/queries/clientpositive/udf_trunc_large_number.q
@@ -0,0 +1,34 @@
+-- SORT_QUERY_RESULTS
+-- HIVE-26731: trunc must stay exact when the input, scaled to the requested
+-- number of decimal places, no longer fits in a long.
+
+SELECT trunc(123456711889.122345897, 8), trunc(-123456711889.122345897, 8),
+       trunc(123456711889.122345897, 19), trunc(123456711889.122345897),
+       trunc(123456711889.122345897, -4);
+
+CREATE TABLE trunc_large_number (d DECIMAL(38,9), c DOUBLE, b BIGINT) STORED 
AS ORC;
+
+INSERT INTO trunc_large_number VALUES
+  (123456711889.122345897, 123456711889.12234, 9223372036854775807),
+  (-123456711889.122345897, -123456711889.12234, -9223372036854775807),
+  (12345678901234567890123456789.987654321, 0.3, 12345),
+  (NULL, NULL, NULL);
+
+set hive.fetch.task.conversion=none;
+set hive.vectorized.execution.enabled=true;
+
+EXPLAIN VECTORIZATION ONLY EXPRESSION
+SELECT trunc(d, 8), trunc(d), trunc(c, 8), trunc(c) FROM trunc_large_number;
+
+SELECT d, trunc(d, 8), trunc(d, -4), trunc(d), trunc(d, -30) FROM 
trunc_large_number;
+SELECT c, trunc(c, 8), trunc(c, 1), trunc(c), trunc(c, -5) FROM 
trunc_large_number;
+SELECT b, trunc(b, -3), trunc(b, -19) FROM trunc_large_number;
+
+-- Row mode must give the same results as vectorized execution
+set hive.vectorized.execution.enabled=false;
+
+SELECT d, trunc(d, 8), trunc(d, -4), trunc(d), trunc(d, -30) FROM 
trunc_large_number;
+SELECT c, trunc(c, 8), trunc(c, 1), trunc(c), trunc(c, -5) FROM 
trunc_large_number;
+SELECT b, trunc(b, -3), trunc(b, -19) FROM trunc_large_number;
+
+DROP TABLE trunc_large_number;
diff --git 
a/ql/src/test/results/clientpositive/llap/udf_trunc_large_number.q.out 
b/ql/src/test/results/clientpositive/llap/udf_trunc_large_number.q.out
new file mode 100644
index 00000000000..5aacaae695b
--- /dev/null
+++ b/ql/src/test/results/clientpositive/llap/udf_trunc_large_number.q.out
@@ -0,0 +1,171 @@
+PREHOOK: query: SELECT trunc(123456711889.122345897, 8), 
trunc(-123456711889.122345897, 8),
+       trunc(123456711889.122345897, 19), trunc(123456711889.122345897),
+       trunc(123456711889.122345897, -4)
+PREHOOK: type: QUERY
+PREHOOK: Input: _dummy_database@_dummy_table
+#### A masked pattern was here ####
+POSTHOOK: query: SELECT trunc(123456711889.122345897, 8), 
trunc(-123456711889.122345897, 8),
+       trunc(123456711889.122345897, 19), trunc(123456711889.122345897),
+       trunc(123456711889.122345897, -4)
+POSTHOOK: type: QUERY
+POSTHOOK: Input: _dummy_database@_dummy_table
+#### A masked pattern was here ####
+123456711889.12234589  -123456711889.12234589  
123456711889.1223458970000000000        123456711889    123456710000
+PREHOOK: query: CREATE TABLE trunc_large_number (d DECIMAL(38,9), c DOUBLE, b 
BIGINT) STORED AS ORC
+PREHOOK: type: CREATETABLE
+PREHOOK: Output: database:default
+PREHOOK: Output: default@trunc_large_number
+POSTHOOK: query: CREATE TABLE trunc_large_number (d DECIMAL(38,9), c DOUBLE, b 
BIGINT) STORED AS ORC
+POSTHOOK: type: CREATETABLE
+POSTHOOK: Output: database:default
+POSTHOOK: Output: default@trunc_large_number
+PREHOOK: query: INSERT INTO trunc_large_number VALUES
+  (123456711889.122345897, 123456711889.12234, 9223372036854775807),
+  (-123456711889.122345897, -123456711889.12234, -9223372036854775807),
+  (12345678901234567890123456789.987654321, 0.3, 12345),
+  (NULL, NULL, NULL)
+PREHOOK: type: QUERY
+PREHOOK: Input: _dummy_database@_dummy_table
+PREHOOK: Output: default@trunc_large_number
+POSTHOOK: query: INSERT INTO trunc_large_number VALUES
+  (123456711889.122345897, 123456711889.12234, 9223372036854775807),
+  (-123456711889.122345897, -123456711889.12234, -9223372036854775807),
+  (12345678901234567890123456789.987654321, 0.3, 12345),
+  (NULL, NULL, NULL)
+POSTHOOK: type: QUERY
+POSTHOOK: Input: _dummy_database@_dummy_table
+POSTHOOK: Output: default@trunc_large_number
+POSTHOOK: Lineage: trunc_large_number.b SCRIPT []
+POSTHOOK: Lineage: trunc_large_number.c SCRIPT []
+POSTHOOK: Lineage: trunc_large_number.d SCRIPT []
+PREHOOK: query: EXPLAIN VECTORIZATION ONLY EXPRESSION
+SELECT trunc(d, 8), trunc(d), trunc(c, 8), trunc(c) FROM trunc_large_number
+PREHOOK: type: QUERY
+PREHOOK: Input: default@trunc_large_number
+#### A masked pattern was here ####
+POSTHOOK: query: EXPLAIN VECTORIZATION ONLY EXPRESSION
+SELECT trunc(d, 8), trunc(d), trunc(c, 8), trunc(c) FROM trunc_large_number
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@trunc_large_number
+#### A masked pattern was here ####
+PLAN VECTORIZATION:
+  enabled: true
+  enabledConditionsMet: [hive.vectorized.execution.enabled IS true]
+
+STAGE DEPENDENCIES:
+  Stage-1 is a root stage
+  Stage-0 depends on stages: Stage-1
+
+STAGE PLANS:
+  Stage: Stage-1
+    Tez
+      Vertices:
+        Map 1 
+            Map Operator Tree:
+                  TableScan Vectorization:
+                      native: true
+                    Select Vectorization:
+                        className: VectorSelectOperator
+                        native: true
+                        projectedOutputColumnNums: [5, 6, 7, 8]
+                        selectExpressions: TruncDecimal(col 0, scale 8) -> 
5:decimal(37,8), TruncDecimalNoScale(col 0, scale 0) -> 6:decimal(29,0), 
TruncFloat(col 1, scale 8) -> 7:double, TruncFloatNoScale(col 1, scale 0) -> 
8:double
+                      File Sink Vectorization:
+                          className: VectorFileSinkOperator
+                          native: false
+            Execution mode: vectorized, llap
+            LLAP IO: all inputs
+            Map Vectorization:
+                enabled: true
+                enabledConditionsMet: 
hive.vectorized.use.vectorized.input.format IS true
+                inputFormatFeatureSupport: [DECIMAL_64]
+                featureSupportInUse: [DECIMAL_64]
+                inputFileFormats: 
org.apache.hadoop.hive.ql.io.orc.OrcInputFormat
+                allNative: false
+                usesVectorUDFAdaptor: false
+                vectorized: true
+
+  Stage: Stage-0
+    Fetch Operator
+
+PREHOOK: query: SELECT d, trunc(d, 8), trunc(d, -4), trunc(d), trunc(d, -30) 
FROM trunc_large_number
+PREHOOK: type: QUERY
+PREHOOK: Input: default@trunc_large_number
+#### A masked pattern was here ####
+POSTHOOK: query: SELECT d, trunc(d, 8), trunc(d, -4), trunc(d), trunc(d, -30) 
FROM trunc_large_number
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@trunc_large_number
+#### A masked pattern was here ####
+-123456711889.122345897        -123456711889.12234589  -123456710000   
-123456711889   0
+123456711889.122345897 123456711889.12234589   123456710000    123456711889    0
+12345678901234567890123456789.987654321        
12345678901234567890123456789.98765432  12345678901234567890123450000   
12345678901234567890123456789   0
+NULL   NULL    NULL    NULL    NULL
+PREHOOK: query: SELECT c, trunc(c, 8), trunc(c, 1), trunc(c), trunc(c, -5) 
FROM trunc_large_number
+PREHOOK: type: QUERY
+PREHOOK: Input: default@trunc_large_number
+#### A masked pattern was here ####
+POSTHOOK: query: SELECT c, trunc(c, 8), trunc(c, 1), trunc(c), trunc(c, -5) 
FROM trunc_large_number
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@trunc_large_number
+#### A masked pattern was here ####
+-1.2345671188912234E11 -1.2345671188912234E11  -1.234567118891E11      
-1.23456711889E11       -1.234567E11
+0.3    0.3     0.3     0.0     0.0
+1.2345671188912234E11  1.2345671188912234E11   1.234567118891E11       
1.23456711889E11        1.234567E11
+NULL   NULL    NULL    NULL    NULL
+PREHOOK: query: SELECT b, trunc(b, -3), trunc(b, -19) FROM trunc_large_number
+PREHOOK: type: QUERY
+PREHOOK: Input: default@trunc_large_number
+#### A masked pattern was here ####
+POSTHOOK: query: SELECT b, trunc(b, -3), trunc(b, -19) FROM trunc_large_number
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@trunc_large_number
+#### A masked pattern was here ####
+-9223372036854775807   -9223372036854775000    0
+12345  12000   0
+9223372036854775807    9223372036854775000     0
+NULL   NULL    NULL
+PREHOOK: query: SELECT d, trunc(d, 8), trunc(d, -4), trunc(d), trunc(d, -30) 
FROM trunc_large_number
+PREHOOK: type: QUERY
+PREHOOK: Input: default@trunc_large_number
+#### A masked pattern was here ####
+POSTHOOK: query: SELECT d, trunc(d, 8), trunc(d, -4), trunc(d), trunc(d, -30) 
FROM trunc_large_number
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@trunc_large_number
+#### A masked pattern was here ####
+-123456711889.122345897        -123456711889.12234589  -123456710000   
-123456711889   0
+123456711889.122345897 123456711889.12234589   123456710000    123456711889    0
+12345678901234567890123456789.987654321        
12345678901234567890123456789.98765432  12345678901234567890123450000   
12345678901234567890123456789   0
+NULL   NULL    NULL    NULL    NULL
+PREHOOK: query: SELECT c, trunc(c, 8), trunc(c, 1), trunc(c), trunc(c, -5) 
FROM trunc_large_number
+PREHOOK: type: QUERY
+PREHOOK: Input: default@trunc_large_number
+#### A masked pattern was here ####
+POSTHOOK: query: SELECT c, trunc(c, 8), trunc(c, 1), trunc(c), trunc(c, -5) 
FROM trunc_large_number
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@trunc_large_number
+#### A masked pattern was here ####
+-1.2345671188912234E11 -1.2345671188912234E11  -1.234567118891E11      
-1.23456711889E11       -1.234567E11
+0.3    0.3     0.3     0.0     0.0
+1.2345671188912234E11  1.2345671188912234E11   1.234567118891E11       
1.23456711889E11        1.234567E11
+NULL   NULL    NULL    NULL    NULL
+PREHOOK: query: SELECT b, trunc(b, -3), trunc(b, -19) FROM trunc_large_number
+PREHOOK: type: QUERY
+PREHOOK: Input: default@trunc_large_number
+#### A masked pattern was here ####
+POSTHOOK: query: SELECT b, trunc(b, -3), trunc(b, -19) FROM trunc_large_number
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@trunc_large_number
+#### A masked pattern was here ####
+-9223372036854775807   -9223372036854775000    0
+12345  12000   0
+9223372036854775807    9223372036854775000     0
+NULL   NULL    NULL
+PREHOOK: query: DROP TABLE trunc_large_number
+PREHOOK: type: DROPTABLE
+PREHOOK: Input: default@trunc_large_number
+PREHOOK: Output: database:default
+PREHOOK: Output: default@trunc_large_number
+POSTHOOK: query: DROP TABLE trunc_large_number
+POSTHOOK: type: DROPTABLE
+POSTHOOK: Input: default@trunc_large_number
+POSTHOOK: Output: database:default
+POSTHOOK: Output: default@trunc_large_number
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 23d1e9eaacf..20dbecc9bbc 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
@@ -143,6 +143,15 @@ public class FastHiveDecimalImpl extends FastHiveDecimal {
     1000000000000000000L, // 18
   };
 
+  public static final int MAX_LONG_POWER_OF_TEN = powerOfTenTable.length - 1;
+
+  /**
+   * Returns 10^exponent, for 0 &lt;= exponent &lt;= {@link 
#MAX_LONG_POWER_OF_TEN}.
+   */
+  public static long powerOfTen(int exponent) {
+    return powerOfTenTable[exponent];
+  }
+
   public static final int MAX_DECIMAL_DIGITS = 38;
 
   /**
@@ -5136,6 +5145,7 @@ public static boolean fastRoundIntegerDown(
         fastResult);
 
     if (fastResult.fast0 == 0 && fastResult.fast1 == 0 && fastResult.fast2 == 
0) {
+      fastResult.fastSignum = 0;
       fastResult.fastIntegerDigitCount = 0;
       fastResult.fastScale = 0;
     } else {
diff --git 
a/storage-api/src/test/org/apache/hadoop/hive/common/type/TestHiveDecimal.java 
b/storage-api/src/test/org/apache/hadoop/hive/common/type/TestHiveDecimal.java
index ec567ad4426..4034f66ce56 100644
--- 
a/storage-api/src/test/org/apache/hadoop/hive/common/type/TestHiveDecimal.java
+++ 
b/storage-api/src/test/org/apache/hadoop/hive/common/type/TestHiveDecimal.java
@@ -2953,6 +2953,17 @@ public void testRoundHalfEvenSpecial() {
     }
   }
 
+  @Test
+  public void testRoundDownAllIntegerDigitsInPlace() {
+    String[] values = { "12345.678", "-12345.678", "12345", 
"99999999999999999999999999999999999999" };
+    for (String value : values) {
+      HiveDecimalWritable writable = new HiveDecimalWritable(value);
+      writable.mutateSetScale(-writable.getHiveDecimal().integerDigitCount(), 
BigDecimal.ROUND_DOWN);
+      assertEquals(value, 0, writable.signum());
+      assertEquals(value, HiveDecimal.ZERO, writable.getHiveDecimal());
+    }
+  }
+
   
//------------------------------------------------------------------------------------------------
 
   @Test

Reply via email to