Skip to content

[AURON #2475] Fix float-to-decimal precision loss - #2496

Merged
slfan1989 merged 2 commits into
apache:masterfrom
Sigma-Ma:Auron-2475-fix-float-to-decimal-precision
Sep 4, 2026
Merged

[AURON #2475] Fix float-to-decimal precision loss#2496
slfan1989 merged 2 commits into
apache:masterfrom
Sigma-Ma:Auron-2475-fix-float-to-decimal-precision

Conversation

@Sigma-Ma

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Closes #2475

Rationale for this change

Auron currently delegates Float32/Float64 -> Decimal128 casts to Arrow's native cast kernel. That kernel scales the input by 10^scale using floating-point arithmetic, which introduces incorrect digits at high decimal scales.

Spark instead converts the floating-point value to its shortest round-trip decimal representation before rescaling it exactly.

What changes are included in this PR?

  • Add a Spark-compatible Float32/Float64 -> Decimal128 path in cast_impl.
  • Widen Float32 values to Float64 before formatting, matching Spark behavior.
  • Reuse the existing string-to-decimal path for rounding and precision-overflow handling.
  • Return NULL for NULL, NaN, infinity, and precision-overflow inputs.
  • Add Rust regression coverage for high-scale casts and edge cases.
  • Re-enable the SPARK-22271 regression test for Spark 3.1 through 3.5.

Are there any user-facing changes?

Bug fix only. Casting float or double values to decimal now matches Spark at high decimal scales. There are no public API or configuration changes.

How was this patch tested?

  • cargo test -p datafusion-ext-commons test_float_to_decimal -- --nocapture
  • cargo test -p datafusion-ext-commons
  • ./dev/reformat --check
  • ./auron-build.sh --pre --sparkver 3.5 --scalaver 2.12 --skiptests true

Was this patch authored or co-authored using generative AI tooling?

  • Yes
  • No

Generated-by: OpenAI Codex (GPT-5)

ASF guidance: https://www.apache.org/legal/generative-tooling.html

Signed-off-by: Ma Zhengxuan <1319614897@qq.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Fixes Spark-incompatible float/double → decimal casting by avoiding Arrow’s float scaling logic that can introduce precision loss at high decimal scales.

Changes:

  • Add Spark-compatible Float32/Float64 → Decimal128 casting by formatting floats to strings, then reusing the existing string→decimal path.
  • Treat NULL/NaN/±Inf and precision-overflow as NULL during float→decimal casts.
  • Re-enable the SPARK-22271 regression suite exclusions for Spark 3.1–3.5 and add Rust regression tests.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
native-engine/datafusion-ext-commons/src/arrow/cast.rs Implements the new float→decimal cast path and adds Rust regression coverage.
auron-spark-tests/spark35/src/test/scala/org/apache/auron/utils/AuronSparkTestSettings.scala Re-enables SPARK-22271 by removing the exclusion.
auron-spark-tests/spark34/src/test/scala/org/apache/auron/utils/AuronSparkTestSettings.scala Re-enables SPARK-22271 by removing the exclusion.
auron-spark-tests/spark33/src/test/scala/org/apache/auron/utils/AuronSparkTestSettings.scala Re-enables SPARK-22271 by removing the exclusion.
auron-spark-tests/spark32/src/test/scala/org/apache/auron/utils/AuronSparkTestSettings.scala Re-enables SPARK-22271 by removing the exclusion.
auron-spark-tests/spark31/src/test/scala/org/apache/auron/utils/AuronSparkTestSettings.scala Re-enables SPARK-22271 by removing the exclusion.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +226 to +240
// spark compatible float to decimal
(&DataType::Float32, DataType::Decimal128(..))
| (&DataType::Float64, DataType::Decimal128(..)) => {
let floats = arrow::compute::cast(array, &DataType::Float64)?;
let strings = floats
.as_primitive::<Float64Type>()
.iter()
.map(|value| {
value
.filter(|value| value.is_finite())
.map(|value| value.to_string())
})
.collect::<StringArray>();
cast_impl(&strings, cast_type, match_struct_fields)?
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the comment to explain the shortest round-trip conversion, exact decimal rescaling, and non-finite-to-NULL behavior.

Comment on lines +227 to +240
(&DataType::Float32, DataType::Decimal128(..))
| (&DataType::Float64, DataType::Decimal128(..)) => {
let floats = arrow::compute::cast(array, &DataType::Float64)?;
let strings = floats
.as_primitive::<Float64Type>()
.iter()
.map(|value| {
value
.filter(|value| value.is_finite())
.map(|value| value.to_string())
})
.collect::<StringArray>();
cast_impl(&strings, cast_type, match_struct_fields)?
}
Comment on lines +643 to +652
#[test]
fn test_float_to_decimal() -> Result<()> {
let f64_array: ArrayRef = Arc::new(Float64Array::from_iter(vec![
Some(0.034567890),
None,
Some(f64::NAN),
Some(f64::INFINITY),
Some(f64::NEG_INFINITY),
Some(1e40),
]));

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added focused coverage for half-up rounding with positive and negative values, as well as signed zero.

Signed-off-by: Ma Zhengxuan <1319614897@qq.com>
@slfan1989 slfan1989 self-assigned this Sep 1, 2026
@slfan1989
slfan1989 merged commit f1c769b into apache:master Sep 4, 2026
134 checks passed
@slfan1989

Copy link
Copy Markdown
Contributor

@Sigma-Ma Thanks for the contribution! Merged into the master.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

cast(double/float as decimal) loses precision: the native kernel scales the float by 10^scale

3 participants