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]
