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]

Reply via email to