diff --git a/cpp/src/arrow/compute/expression_test.cc b/cpp/src/arrow/compute/expression_test.cc index 30bd882b2c03..1487e3fa4b1a 100644 --- a/cpp/src/arrow/compute/expression_test.cc +++ b/cpp/src/arrow/compute/expression_test.cc @@ -944,6 +944,41 @@ TEST(Expression, ExecuteDictionaryTransparent) { ])")); } +TEST(Expression, ExecuteDecimalDivide) { + // GH-40911 Decimal divide promotion rule output wrong precision and scale + // in expression Bind when scale1 >= scale2 + // + // scale1 < scale2 + ExpectExecute( + call("divide", {field_ref("a"), field_ref("b")}), + ArrayFromJSON(struct_({field("a", decimal128(6, 1)), field("b", decimal128(7, 2))}), + R"([ + {"a": "0.7", "b": "-2.17"}, + {"a": "2.9", "b": "9.11"}, + {"a": "3.1", "b" : "12.31"} + ])")); + + // scale1 == scale2 + ExpectExecute( + call("divide", {field_ref("a"), field_ref("b")}), + ArrayFromJSON(struct_({field("a", decimal128(6, 1)), field("b", decimal128(6, 1))}), + R"([ + {"a": "0.7", "b": "-2.1"}, + {"a": "2.9", "b": "9.1"}, + {"a": "3.1", "b" : "12.3"} + ])")); + + // scale1 > scale2 + ExpectExecute( + call("divide", {field_ref("a"), field_ref("b")}), + ArrayFromJSON(struct_({field("a", decimal128(6, 2)), field("b", decimal128(6, 1))}), + R"([ + {"a": "0.71", "b": "-2.1"}, + {"a": "2.92", "b": "9.1"}, + {"a": "3.17", "b" : "12.3"} + ])")); +} + void ExpectIdenticalIfUnchanged(Expression modified, Expression original) { if (modified == original) { // no change -> must be identical diff --git a/cpp/src/arrow/compute/kernels/codegen_internal.cc b/cpp/src/arrow/compute/kernels/codegen_internal.cc index 00a833742f95..44131115cc78 100644 --- a/cpp/src/arrow/compute/kernels/codegen_internal.cc +++ b/cpp/src/arrow/compute/kernels/codegen_internal.cc @@ -428,13 +428,9 @@ Status CastBinaryDecimalArgs(DecimalPromotion promotion, std::vector right_scaleup = std::max(s1, s2) - s2; break; } - case DecimalPromotion::kMultiply: { - left_scaleup = right_scaleup = 0; - break; - } + case DecimalPromotion::kMultiply: case DecimalPromotion::kDivide: { - left_scaleup = std::max(4, s1 + p2 - s2 + 1) + s2 - s1; - right_scaleup = 0; + left_scaleup = right_scaleup = 0; break; } default: diff --git a/cpp/src/arrow/compute/kernels/scalar_arithmetic.cc b/cpp/src/arrow/compute/kernels/scalar_arithmetic.cc index efd25a8a20c8..d6f4b8b72690 100644 --- a/cpp/src/arrow/compute/kernels/scalar_arithmetic.cc +++ b/cpp/src/arrow/compute/kernels/scalar_arithmetic.cc @@ -541,13 +541,8 @@ Result ResolveDecimalDivisionOutput(KernelContext*, types, [](int32_t p1, int32_t s1, int32_t p2, int32_t s2) -> Result> { - if (s1 < s2) { - return Status::Invalid("Division of two decimal types scale1 < scale2. ", "(", - s1, s2, ")."); - } - DCHECK_GE(s1, s2); - const int32_t scale = s1 - s2; - const int32_t precision = p1; + const int32_t scale = std::max(4, s1 + p2 - s2 + 1); + const int32_t precision = p1 - s1 + s2 + scale; return std::make_pair(precision, scale); }); }