|
36 | 36 | /// https://doi.org/10.1145/2837614.2837654 |
37 | 37 | /// In particular, the Errol paper explored the impact of higher-precision |
38 | 38 | /// fixed-width arithmetic on Grisu2 and showed a way to rapidly test |
39 | | -/// the correctness of such algorithms. |
| 39 | +/// the correctness of Grisu-style algorithms. |
40 | 40 | /// |
41 | 41 | /// A few further improvements were inspired by the Ryu algorithm |
42 | 42 | /// from Ulf Anders; "Ryū: fast float-to-string conversion", 2018. |
|
53 | 53 | /// values on 32-bit processors, and higher-precision values on all |
54 | 54 | /// processors, it is considerably faster. |
55 | 55 | /// |
56 | | -/// * Always Accurate. Converting the decimal form back to binary |
57 | | -/// will always yield exactly the same value. For the IEEE 754 |
58 | | -/// formats, the round-trip will produce exactly the same bit |
59 | | -/// pattern in memory. |
| 56 | +/// * Always Accurate. Except for NaNs, converting the decimal form |
| 57 | +/// back to binary will always yield an equal value. For the IEEE |
| 58 | +/// 754 formats, the round trip will produce exactly the same bit |
| 59 | +/// pattern in memory. This assumes, of course, that the conversion |
| 60 | +/// from text to binary uses a correctly-rounded algorithm such as |
| 61 | +/// Clinger 1990 or Eisel-Lemire 2021. |
60 | 62 | /// |
61 | 63 | /// * Always Short. This always selects an accurate result with the |
62 | 64 | /// minimum number of significant digits. |
|
73 | 75 | /// * When present, a '.' is never the first or last character |
74 | 76 | /// * There is a consecutive range of integer values that can be |
75 | 77 | /// represented in any particular type (-2^54...2^54 for double). |
76 | | -/// Never use exponential form for integral numbers in this range. |
| 78 | +/// We do not use exponential form for integral numbers in this |
| 79 | +/// range. |
77 | 80 | /// * Generally follow existing practice: Don't use use exponential |
78 | 81 | /// form for fractional values bigger than 10^-4; always write at |
79 | 82 | /// least 2 digits for an exponent. |
80 | 83 | /// * Apart from the above, we do prefer shorter output. |
81 | 84 |
|
| 85 | +/// Note: If you want to compare performance of this implementation |
| 86 | +/// versus some others, keep in mind that this implementation does |
| 87 | +/// deliberately sacrifice some performance. Any attempt to compare |
| 88 | +/// the performance of this implementation to others should |
| 89 | +/// try to compensate for the following: |
| 90 | +/// * The output ergonomics described above do take some time. |
| 91 | +/// It would be faster to always emit the form "123456e-78" |
| 92 | +// (See `finishFormatting()`) |
| 93 | +/// * The implementations in published papers generally include |
| 94 | +/// large tables with every power of 10 computed out. We've |
| 95 | +/// factored these tables down to conserve code size, which |
| 96 | +/// requires some additional work to reconstruct the needed power |
| 97 | +/// of 10. (See the `intervalContainingPowerOf10_*` functions) |
| 98 | + |
82 | 99 | /// |
83 | 100 | /// This Swift implementation was ported from an earlier C version; |
84 | 101 | /// the output is exactly the same in all cases. |
|
90 | 107 | /// implementation to ensure that no unsafety actually occurs. For |
91 | 108 | /// Float32, that testing was exhaustive -- we verified all 4 |
92 | 109 | /// billion possible Float32 values. |
93 | | -/// * The Swift code uses an idiom of building up to 8 ASCII characters |
| 110 | +/// * The Swift code uses an idiom of building up to 8 digit characters |
94 | 111 | /// in a UInt64 and then writing the whole block to memory. |
95 | 112 | /// * The Swift version is slightly faster than the C version; |
96 | 113 | /// mostly thanks to various minor algorithmic tweaks that were |
@@ -348,6 +365,7 @@ fileprivate func _Float16ToASCII( |
348 | 365 | // Exhaustive testing shows that the only input value |
349 | 366 | // affected by this is 0.015625 == 2^-6, which |
350 | 367 | // incorrectly prints as "0.01562" without this fix. |
| 368 | + // With this, it prints correctly as "0.01563" |
351 | 369 | if t < lDigit || (t == lDigit && l > 0) { |
352 | 370 | t += 1 |
353 | 371 | } |
@@ -965,10 +983,12 @@ fileprivate func _Float64ToASCII( |
965 | 983 | firstDigit &+= 1 |
966 | 984 |
|
967 | 985 | // >90% of random binary64 values need at least 15 digits. |
968 | | - // We already have seven, try grabbing the next 8 digits all at once. |
| 986 | + // We have seven so there's probably at least 8 more, which |
| 987 | + // we can grab all at once. |
969 | 988 | let TenToTheEighth = 100000000 as UInt128; // 10^(15-bulkFirstDigits) |
970 | 989 | let d0 = delta * TenToTheEighth |
971 | 990 | var t0 = t * TenToTheEighth |
| 991 | + // The integer part of t0 is the next 8 digits |
972 | 992 | let next8Digits = UInt32(truncatingIfNeeded: t0._high >> 32) |
973 | 993 | t0 &= fractionMask |
974 | 994 | if d0 < t0 { |
@@ -1204,8 +1224,9 @@ fileprivate func _Float80ToASCII( |
1204 | 1224 | // ================================================================ |
1205 | 1225 |
|
1206 | 1226 | #if false |
1207 | | -// Note: We don't need _float128ToStringImpl, since that's only for backwards compatibility, |
1208 | | -// and the legacy ABI never supported Float128. |
| 1227 | +// Note: We don't need _float128ToStringImpl, since that's only for |
| 1228 | +// backwards compatibility, and the legacy ABI never supported |
| 1229 | +// Float128. |
1209 | 1230 |
|
1210 | 1231 | internal func Float128ToASCII( |
1211 | 1232 | value d: Float128, |
@@ -1331,13 +1352,12 @@ fileprivate func _backend_256bit( |
1331 | 1352 | as: UInt8.self) |
1332 | 1353 | nextDigit &+= 1 |
1333 | 1354 |
|
1334 | | - // It would be nice to generate 8 digits at a time |
1335 | | - // and take advantage of intToEightDigits, but |
1336 | | - // our integer portion has only 14 bits. We can't make |
1337 | | - // that bigger without either sacrificing too much |
1338 | | - // precision for correct Float128 or folding the first |
1339 | | - // digits into the scaling (as we do with Double) which |
1340 | | - // would require a back-out phase here. |
| 1355 | + // It would be nice to generate 8 digits at a time and take |
| 1356 | + // advantage of intToEightDigits, but our integer portion has only |
| 1357 | + // 14 bits. We can't make that bigger without either sacrificing |
| 1358 | + // too much precision for correct Float128 or folding the first |
| 1359 | + // digits into the scaling (as we do with Double) which would |
| 1360 | + // require a back-out phase here (as we do with Double). |
1341 | 1361 |
|
1342 | 1362 | // If there is at least one more digit possible... |
1343 | 1363 | if delta < t { |
|
0 commit comments