Repository navigation
reject out-of-range values in the double to float conversions - #1249
Conversation
convertNumber skipped every check for a floating-to-floating conversion, so Any::cast<float> of a double beyond the float range was a bare static_cast that yields an infinity (undefined per [conv.double]). The convertFromString<float> and Any::stringToNumber fallbacks used without the floating-point std::from_chars had the same cast, while the from_chars<float> branch rejects such literals as out of range.
|
Thanks @aysha-afrah26, the bug is real and I'd like to merge this, but there's an off-by-half-ulp at the boundary.
Suggested check, in both const auto narrowed = static_cast<To>(from);
if(std::isinf(narrowed) && !std::isinf(from)) { /* throw */ }(or compare with One more thing: this PR conflicts textually with the already-merged #1238 in |
The check rejected every finite double above FLT_MAX, but values below FLT_MAX + half an ulp still round to FLT_MAX: 3.4028235e38, the usual printout of FLT_MAX, was accepted before and std::from_chars<float> accepts it too. Reject only values from FLT_MAX + half an ulp on, which round to infinity, in both checkTruncation and the macOS convertFromString<float> fallback. Also skip the check for widening conversions (float to double), where casting the limits of To to From would itself overflow, and move it to a helper to keep checkTruncation readable. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
To move this forward I pushed two commits on top of yours (thanks for enabling maintainer edits): a merge of master, which resolves the test conflict with #1238, and a fix for the boundary discussed above. Values are now rejected only from FLT_MAX + ½ ulp on, both in |
|
Good catch on the half-ulp band, I hadn't considered the 8-digit FLT_MAX print. Thanks for fixing it up and merging. |
On master, the new regression test fails with:
convertNumberskipscheckTruncationwhenever both types are floating point, soAny::cast<float>of a double outside the float range is a barestatic_cast<float>. The standard leaves that conversion undefined ([conv.double]) and in practice the caller quietly getsinf, while every other out-of-range narrowing in the same helper throws. Reaching it only takes a blackboard entry that holds a double (a script assignment, a JSON import, another node's output) read throughgetInput<float>orBlackboard::get<float>.The two string paths have the same shape on toolchains without the floating-point
std::from_chars(Apple libc++, the macOS CI target):convertFromString<float>and theAny::stringToNumberfallback parse a double and cast it, so"1e300"becomesinfon macOS where thefrom_chars<float>branch used on Linux reportsresult_out_of_rangeand fails.The range check sits in
checkTruncationnext to the other numeric limits, soAny::cast,Blackboard::getandgetInputall pick it up, and the two fallbacks get the same guard so they agree with thefrom_charsbranch. Rounding between double and float stays accepted. Header-inline code plus one exported function body, no API or ABI change.BasicTypes.FloatConversionLimitsfails before and passes after. The full suite is green under ASan+UBSan (Debug) and in Release on macOS, except for the pre-existingParallel.FailingParalleltimer abort and thecmake_subproject_versionlocal SDK failure, both identical on the untouched tree. clang-format is clean; clang-tidy-21 is not installed on this machine.