davidlghellin opened a new issue, #10508:
URL: https://github.com/apache/arrow-rs/issues/10508

   ### Describe the bug
   
   `cast(decimal, Float64)` and `cast(decimal, Float32)` can return a float 
that is not the one nearest
   to the decimal's exact value. `CAST(1.0 AS DECIMAL(38,37))` comes back as 
`0.9999999999999999`.
   
   Both float targets go through
   
[`single_decimal_to_float_lossy`](https://github.com/apache/arrow-rs/blob/main/arrow-cast/src/cast/mod.rs#L86),
   which computes `f(x) / 10_f64.powi(scale)`. That rounds twice, and there is 
a second, independent
   double rounding on the `Float32` path, which narrows the `f64` result with 
`as f32`. Details in
   *Expected behavior* below.
   
   ### To Reproduce
   
   ```rust
   let array = Decimal128Array::from(vec![10i128.pow(37)])
       .with_precision_and_scale(38, 37)
       .unwrap();
   let out = cast(&array, &DataType::Float64).unwrap();
   assert_eq!(out.as_primitive::<Float64Type>().value(0), 1.0);  // fails: 
0.9999999999999999
   ```
   
   Cast to `Float64`:
   
   | unscaled | scale | got | expected |
   |---|---|---|---|
   | `12345678901234567890` | 2 | `1.2345678901234566e17` | 
`1.2345678901234568e17` |
   | `10^37` | 37 | `0.9999999999999999` | `1.0` |
   | `123456789012345678901` | 20 | `1.234567890123457` | `1.2345678901234567` |
   | `1` | 37 | `9.999999999999999e-38` | `1e-37` |
   
   Cast to `Float32`, a separate defect — see *Expected behavior*:
   
   | unscaled | scale | got | expected |
   |---|---|---|---|
   | `13631072500000000514758830` | 18 | `13631072.0` | `13631073.0` |
   | `72073620000000000000000582908005` | 24 | `72073620.0` | `72073624.0` |
   | `-3273316900000000000957536840` | 20 | `-32733168.0` | `-32733170.0` |
   
   The `expected` column is what parsing the decimal's own text gives. The 
second `Float32` row holds
   the value `72073616`, which `{:?}` prints as `72073620.0` — the shortest 
string that round-trips.
   
   ### Expected behavior
   
   _No response_
   
   ### Additional context
   
   Values with `|unscaled| < 2^53` and `scale <= 22` are unaffected, which is 
why this is not more
   visible: an ordinary `DECIMAL(10,2)` column is fine. It shows up on wide 
decimals.
   
   Happy to send a PR: keep the current arithmetic as a fast path where both 
operands are exactly
   representable, so it rounds once, and use a correctly rounded conversion 
otherwise — with a separate
   helper for `f32`, since its exact range is much smaller (`10^k` is exact in 
an `f32` only up to
   `k = 10`).


-- 
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]

Reply via email to