From 17e1f7c00b2ae623e792913fb7d8ed53a06d9d74 Mon Sep 17 00:00:00 2001 From: Karman Singh Talwar Date: Sat, 14 Nov 2026 04:50:08 +0530 Subject: [PATCH] Fix CAST(DOUBLE AS INT32) precision loss from narrowing to FLOAT castDoubleToInt rounded (float) value, so in-range doubles above 2^24, or just below a .5 tie, were changed before rounding (123456789.0 became 123456792 and 2.4999999999 became 3). Round the double directly; the existing range check keeps the result within int, so the narrowing from long cannot overflow. Closes #19274 --- .../dag/util/CastFunctionUtils.java | 2 +- .../dag/util/CastFunctionUtilsTest.java | 31 +++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/util/CastFunctionUtils.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/util/CastFunctionUtils.java index df5d41356b0b3..dff2abe6c0e46 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/util/CastFunctionUtils.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/util/CastFunctionUtils.java @@ -67,7 +67,7 @@ public static int castDoubleToInt(double value) { String.format( CalcMessages.EXCEPTION_DOUBLE_VALUE_ARG_OUT_RANGE_INTEGER_VALUE_BAB52E11, value)); } - return Math.round((float) value); + return (int) Math.round(value); } public static long castDoubleToLong(double value) { diff --git a/iotdb-core/calc-commons/src/test/java/org/apache/iotdb/calc/transformation/dag/util/CastFunctionUtilsTest.java b/iotdb-core/calc-commons/src/test/java/org/apache/iotdb/calc/transformation/dag/util/CastFunctionUtilsTest.java index 9bfb0e6177f93..96005f20c23dd 100644 --- a/iotdb-core/calc-commons/src/test/java/org/apache/iotdb/calc/transformation/dag/util/CastFunctionUtilsTest.java +++ b/iotdb-core/calc-commons/src/test/java/org/apache/iotdb/calc/transformation/dag/util/CastFunctionUtilsTest.java @@ -74,11 +74,42 @@ public void castDoubleToIntInRange() { Assert.assertEquals(2, CastFunctionUtils.castDoubleToInt(1.5)); } + @Test + public void castDoubleToIntKeepsDoublePrecision() { + Assert.assertEquals(16777217, CastFunctionUtils.castDoubleToInt(16777217.0)); + Assert.assertEquals(123456789, CastFunctionUtils.castDoubleToInt(123456789.0)); + Assert.assertEquals(-123456789, CastFunctionUtils.castDoubleToInt(-123456789.0)); + Assert.assertEquals(2147483000, CastFunctionUtils.castDoubleToInt(2147483000.0)); + Assert.assertEquals(2147483646, CastFunctionUtils.castDoubleToInt(2147483646.0)); + Assert.assertEquals(-2147483647, CastFunctionUtils.castDoubleToInt(-2147483647.0)); + Assert.assertEquals(Integer.MAX_VALUE, CastFunctionUtils.castDoubleToInt(2147483647.0)); + Assert.assertEquals(Integer.MIN_VALUE, CastFunctionUtils.castDoubleToInt(-2147483648.0)); + Assert.assertEquals(2, CastFunctionUtils.castDoubleToInt(2.4999999999)); + Assert.assertEquals(100000002, CastFunctionUtils.castDoubleToInt(100000001.5)); + } + + @Test + public void castDoubleToIntRoundsHalfUp() { + Assert.assertEquals(3, CastFunctionUtils.castDoubleToInt(2.5)); + Assert.assertEquals(-1, CastFunctionUtils.castDoubleToInt(-1.5)); + Assert.assertEquals(-2, CastFunctionUtils.castDoubleToInt(-2.5)); + } + @Test(expected = SemanticException.class) public void castDoubleToIntOverflow() { CastFunctionUtils.castDoubleToInt(Double.MAX_VALUE); } + @Test(expected = SemanticException.class) + public void castDoubleToIntJustAboveMax() { + CastFunctionUtils.castDoubleToInt(2147483648.0); + } + + @Test(expected = SemanticException.class) + public void castDoubleToIntJustBelowMin() { + CastFunctionUtils.castDoubleToInt(-2147483649.0); + } + // ---- castDoubleToLong ---- @Test public void castDoubleToLongInRange() {