peterxcli opened a new issue, #5670:
URL: https://github.com/apache/datafusion-comet/issues/5670

   ### Describe the bug
   
   `CAST(decimal AS DOUBLE)` / `CAST(decimal AS FLOAT)` is delegated to 
arrow-cast, which computes `(unscaled as f64) / 10^scale` — two roundings for 
DOUBLE and three for FLOAT (via an f64 intermediate). Spark uses 
`BigDecimal.doubleValue` / `floatValue`, which round the exact decimal once. 
Whenever `|unscaled| >= 2^53` and `scale >= 1` the two can differ in the last 
ulp; for `DECIMAL(38,18)` that is essentially every `|value| >= 0.009` (about 
26% of random values differ in a model of arrow's formula). The wrong double is 
visible in string output, equality filters/joins on the cast value, and sums.
   
   Location: `native/spark-expr/src/conversion_funcs/numeric.rs:64-85` — 
`is_df_cast_from_decimal_spark_compatible` lists `Float32`/`Float64`, so 
`cast.rs:402-407` falls through to arrow's cast. Affects LEGACY, ANSI and TRY 
alike.
   
   ### Steps to reproduce
   
   ```scala
   val p = "/tmp/dec_to_double"
   Seq("12345.6789", "123456.789012", "76543.21").toDF("s")
     .selectExpr("CAST(s AS DECIMAL(38,18)) AS 
c").write.mode("overwrite").parquet(p)
   spark.read.parquet(p).selectExpr("CAST(c AS DOUBLE)").show(false)
   ```
   
   | value | Spark | Comet |
   |---|---|---|
   | 12345.6789 | 12345.6789 | 12345.678899999999 |
   | 123456.789012 | 123456.789012 | 123456.78901200001 |
   | 76543.21 | 76543.21 | 76543.20999999999 |
   
   FLOAT: `SELECT CAST(CAST('16777217.0000000001' AS DECIMAL(38,10)) AS FLOAT)` 
→ Spark `1.6777218E7`, Comet `1.6777216E7` (the f64 intermediate is exactly 
16777217.0, then ties-to-even).
   
   ### Expected behavior
   
   Match Spark, i.e. a correctly rounded conversion of the exact decimal value 
(`BigDecimal.doubleValue` / `floatValue`).
   
   ### Proposed solution
   
   Remove `Float32`/`Float64` from `is_df_cast_from_decimal_spark_compatible` 
and add a correctly rounded `Decimal128 -> f64/f32` kernel in `numeric.rs`:
   
   - fast path when `|unscaled| < 2^53` and `scale <= 22`: `unscaled as f64 / 
10^scale` is then a single correctly rounded operation (exactly Java's fast 
path);
   - otherwise format `<unscaled>e-<scale>` into a stack buffer and parse with 
Rust's correctly rounded `f64::from_str`; for FLOAT parse the same string with 
`f32::from_str` directly (never through f64).
   
   Add `DECIMAL(38,18)` values such as `12345.6789` and `76543.21` to 
`CometNativeCastSuite`; the existing DECIMAL(38,18) cases only use values whose 
double happens to coincide, which is why CI is green.
   
   ### Additional context
   
   PR #4278 excluded DECIMAL(38,18) from the nested cast matrix citing "the 
same known decimal-to-floating ULP mismatch", but no issue was filed. #3948 
added the DECIMAL(38,18) → Float/Double scalar tests with non-diverging values. 
#286 listed "Decimal to Floating-point — seems correct but needs tests to 
confirm".
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to