|
149 | 149 | * \value CborPrettyDefaultFlags Default conversion flags.
|
150 | 150 | */
|
151 | 151 |
|
| 152 | +static inline bool convertToUint64(double v, uint64_t *absolute) |
| 153 | +{ |
| 154 | + v = fabs(v); |
| 155 | + |
| 156 | + /* C11 standard section 6.3.1.4 "Real floating and integer" says: |
| 157 | + * |
| 158 | + * 1 When a finite value of real floating type is converted to an integer |
| 159 | + * type other than _Bool, the fractional part is discarded (i.e., the |
| 160 | + * value is truncated toward zero). If the value of the integral part |
| 161 | + * cannot be represented by the integer type, the behavior is undefined. |
| 162 | + * |
| 163 | + * So we must perform a range check that v <= UINT64_MAX, but we can't use |
| 164 | + * UINT64_MAX + 1.0 because the standard continues: |
| 165 | + * |
| 166 | + * 2 When a value of integer type is converted to a real floating type, if |
| 167 | + * the value being converted can be represented exactly in the new type, |
| 168 | + * it is unchanged. If the value being converted is in the range of |
| 169 | + * values that can be represented but cannot be represented exactly, the |
| 170 | + * result is either the nearest higher or nearest lower representable |
| 171 | + * value, chosen in an implementation-defined manner. |
| 172 | + */ |
| 173 | + double supremum = -2.0 * INT64_MIN; /* -2 * (- 2^63) == 2^64 */ |
| 174 | + if (v >= supremum) |
| 175 | + return false; |
| 176 | + |
| 177 | + /* Now we can convert, these two conversions cannot be UB */ |
| 178 | + *absolute = v; |
| 179 | + return *absolute == v; |
| 180 | +} |
| 181 | + |
152 | 182 | static void printRecursionLimit(CborStreamFunction stream, void *out)
|
153 | 183 | {
|
154 | 184 | stream(out, "<nesting too deep, recursion stopped>");
|
@@ -489,8 +519,8 @@ static CborError value_to_pretty(CborStreamFunction stream, void *out, CborValue
|
489 | 519 | suffix = "";
|
490 | 520 | }
|
491 | 521 |
|
492 |
| - uint64_t ival = (uint64_t)fabs(val); |
493 |
| - if (ival == fabs(val)) { |
| 522 | + uint64_t ival; |
| 523 | + if (convertToUint64(val, &ival)) { |
494 | 524 | /* this double value fits in a 64-bit integer, so show it as such
|
495 | 525 | * (followed by a floating point suffix, to disambiguate) */
|
496 | 526 | err = stream(out, "%s%" PRIu64 ".%s", val < 0 ? "-" : "", ival, suffix);
|
|
0 commit comments