raghav-reglobe opened a new pull request, #69216:
URL: https://github.com/apache/doris/pull/69216
### What problem does this PR solve?
Issue Number: close #69215
Related PR: none
Problem Summary:
`CastToFloat::_from_decimalv3` computed `double(unscaled) /
double(10^scale)`, which rounds twice once the unscaled
integer exceeds 2^53: the integer-to-double conversion already rounds, and
the division rounds again, so the result can
be the double next to the correctly rounded one (`DECIMAL(38,12)`
`29201.024999999998` → `29201.025`; 26.4 % of 20M
generated 12-fraction-digit values differ). `CAST(CAST(x AS STRING) AS
DOUBLE)` is exact, and a literal cast is exact
because the FE folds it in BigDecimal, so the defect shows only on column
data. DECIMAL-typed VARIANT elements take the
same cast.
The fix keeps the one-division path only when both operands are exact
(|unscaled| < 2^53, scale ≤ 22, double target)
and otherwise converts through the decimal's digits with
`fast_float::from_chars` straight into the target type.
`StringParser::string_to_float<float>` was not usable for the FLOAT target:
it accumulates in double and narrows
afterwards, which resolves an exact tie the wrong way (`DECIMAL(19,9)`
`-9000000000.000000009` → FLOAT gave
-8999999488 instead of -9000000512).
Cost on 20M values: the fast path is unchanged; the digit path adds roughly
40–60 ns per value, about the cost of the
text-route workaround.
### Release note
Fix `CAST(DECIMAL AS DOUBLE/FLOAT)` returning the neighbouring
floating-point value for decimals whose unscaled value
exceeds 2^53; the conversion is now correctly rounded.
### Check List (For Author)
- Test
- [x] Unit Test — `FunctionCastToFloatTest`: the parametrized
`test_from_decimal` expectations now follow the
decimal's digits (`strtod` / `strtof`) instead of the two-rounding
formula, and a witness test pins wide
`DECIMAL(38,12)` values.
- [x] Manual test — the 20M-row `numbers()` query from the issue:
5,273,258 mismatches before, 0 after.
- Behavior changed:
- [x] Yes. `CAST(decimal AS DOUBLE/FLOAT)` returns the correctly rounded
value where it previously returned the
adjacent one (only for unscaled values beyond 2^53).
- Does this need documentation?
- [x] No.
--
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]