raghav-reglobe opened a new issue, #69215:
URL: https://github.com/apache/doris/issues/69215
**Search before asking:** yes.
**Version:** master (reproduced on a build from 2026-09-28 lineage; the code
is unchanged on master today).
**What's wrong**
`CastToFloat::_from_decimalv3`
(be/src/exprs/function/cast/cast_to_basic_number_common.h) computes
`double(unscaled) / double(10^scale)`. Once the unscaled integer is above
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. The text route
is exact, so the two disagree:
```sql
-- the value is built as a string so the FE does not fold the cast (FE
folding runs in BigDecimal and is exact)
SELECT SUM(CAST(x AS DOUBLE) <> CAST(CAST(x AS STRING) AS DOUBLE)) AS
mismatches, COUNT(*) AS n
FROM (
SELECT CAST(CONCAT(CAST(9007 + number % 20000 AS STRING), '.',
LPAD(CAST((number * 7919) % 1000000000000 AS STRING),
12, '0')) AS DECIMAL(38,12)) AS x
FROM numbers('number' = '20000000')
) t;
-- master: 5273258 20000000 (26.4 % of the values)
-- fixed: 0 20000000
```
One concrete value: `DECIMAL(38,12)` `29201.024999999998` reads back as
`29201.025` through `CAST(... AS DOUBLE)` and as
`29201.024999999998` through `CAST(CAST(... AS STRING) AS DOUBLE)`. The same
cast serves DECIMAL-typed VARIANT elements
(`cast_variant_to_scalar.cpp` groups them into a Decimal column and runs
this cast), which is where we noticed it: 56,313 of
849,165 rows of a production table read differently depending on the route.
A `FLOAT` target has a second problem: parsing the digits with
`StringParser::string_to_float<float>` accumulates in
double and narrows afterwards, which resolves an exact tie the wrong way
(`DECIMAL(19,9)` `-9000000000.000000009` → FLOAT
gives -8999999488 instead of -9000000512).
**What you expected**
`CAST(decimal AS DOUBLE/FLOAT)` returns the correctly rounded value, the
same as parsing the decimal's text.
**Fix**
Keep the one-division path only when both operands are exact (|unscaled| <
2^53, scale ≤ 22, double target); otherwise
convert through the decimal's digits with `fast_float::from_chars` straight
into the target type. The parametrized
`cast_to_float_double` test derived its expectations with the same
two-rounding formula; they now follow the digits, and a
witness test pins wide DECIMAL(38,12) values. 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). PR follows.
--
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]