@@ -155,12 +155,24 @@ public func _float16ToStringImpl(
155
155
return UInt64 ( truncatingIfNeeded: textLength)
156
156
}
157
157
158
+ // Convert a Float16 to an optimal ASCII representation.
159
+ // See notes above for comments on the output format here.
160
+ // Inputs:
161
+ // * `value`: Float16 input
162
+ // * `buffer`: Buffer to place the result
163
+ // Returns: Range of bytes within `buffer` that contain the result
164
+ //
165
+ // Buffer must be at least 32 bytes long and must be pre-filled
166
+ // with "0" characters, e.g., via
167
+ // `InlineArray<32,UTF8.CodeUnit>(repeating:0x30)`
168
+
158
169
@available ( SwiftStdlib 6 . 2 , * )
159
170
internal func _Float16ToASCII(
160
171
value f: Float16 ,
161
172
buffer utf8Buffer: inout MutableSpan < UTF8 . CodeUnit >
162
173
) -> Range < Int > {
163
174
// We need a MutableRawSpan in order to use wide store/load operations
175
+ // TODO: Tune this value down to the actual minimum for Float16
164
176
precondition ( utf8Buffer. count >= 32 )
165
177
var buffer = utf8Buffer. mutableBytes
166
178
@@ -338,11 +350,7 @@ internal func _Float16ToASCII(
338
350
339
351
if fractionPart == 0 {
340
352
// Step 6: No fraction, so ".0" and we're done
341
- // Last write on this branch, so use a checked store
342
- buffer. storeBytes (
343
- of: 0x30 ,
344
- toByteOffset: nextDigit,
345
- as: UInt8 . self)
353
+ // "0" write is free since buffer is pre-initialized
346
354
nextDigit &+= 1
347
355
} else {
348
356
// Step 7: Emit the fractional part by repeatedly
@@ -439,6 +447,17 @@ public func _float32ToStringImpl(
439
447
}
440
448
}
441
449
450
+ // Convert a Float32 to an optimal ASCII representation.
451
+ // See notes above for comments on the output format here.
452
+ // See _Float64ToASCII for comments on the algorithm.
453
+ // Inputs:
454
+ // * `value`: Float32 input
455
+ // * `buffer`: Buffer to place the result
456
+ // Returns: Range of bytes within `buffer` that contain the result
457
+ //
458
+ // Buffer must be at least 32 bytes long and must be pre-filled
459
+ // with "0" characters, e.g., via
460
+ // `InlineArray<32,UTF8.CodeUnit>(repeating:0x30)`
442
461
@available ( SwiftStdlib 6 . 2 , * )
443
462
internal func _Float32ToASCII(
444
463
value f: Float32 ,
@@ -449,6 +468,8 @@ internal func _Float32ToASCII(
449
468
// more detailed comments and explanation.
450
469
451
470
// We need a MutableRawSpan in order to use wide store/load operations
471
+ // TODO: Tune this limit down to the actual minimum we need here
472
+ // TODO: `assert` that the buffer is filled with 0x30 bytes (in debug builds)
452
473
precondition ( utf8Buffer. count >= 32 )
453
474
var buffer = utf8Buffer. mutableBytes
454
475
@@ -561,12 +582,6 @@ internal func _Float32ToASCII(
561
582
var delta = u &- l
562
583
let fractionMask : UInt64 = ( 1 << fractionBits) - 1
563
584
564
- // Write 8 leading zeros to the beginning of the buffer:
565
- unsafe buffer. storeBytes (
566
- of: 0x3030303030303030 ,
567
- toUncheckedByteOffset: 0 ,
568
- as: UInt64 . self)
569
-
570
585
// Overwrite the first digit at index 7:
571
586
let firstDigit = 7
572
587
let digit = ( t >> fractionBits) &+ 0x30
@@ -684,6 +699,18 @@ public func _float64ToStringImpl(
684
699
}
685
700
}
686
701
702
+ // Convert a Float64 to an optimal ASCII representation.
703
+ // See notes above for comments on the output format here.
704
+ // The algorithm is extensively commented inline; the comments
705
+ // at the top of this source file give additional context.
706
+ // Inputs:
707
+ // * `value`: Float64 input
708
+ // * `buffer`: Buffer to place the result
709
+ // Returns: Range of bytes within `buffer` that contain the result
710
+ //
711
+ // Buffer must be at least 32 bytes long and must be pre-filled
712
+ // with "0" characters, e.g., via
713
+ // `InlineArray<32,UTF8.CodeUnit>(repeating:0x30)`
687
714
@available ( SwiftStdlib 6 . 2 , * )
688
715
internal func _Float64ToASCII(
689
716
value d: Float64 ,
@@ -937,10 +964,6 @@ internal func _Float64ToASCII(
937
964
938
965
var nextDigit = 5
939
966
var firstDigit = nextDigit
940
- unsafe buffer. storeBytes (
941
- of: 0x3030303030303030 as UInt64 ,
942
- toUncheckedByteOffset: 0 ,
943
- as: UInt64 . self)
944
967
945
968
// Our initial scaling gave us the first 7 digits already:
946
969
let d12345678 = UInt32 ( truncatingIfNeeded: t. _high >> 32 )
@@ -1015,13 +1038,14 @@ internal func _Float64ToASCII(
1015
1038
t0 &= ~ 1
1016
1039
}
1017
1040
// t0 has t0digits digits. Write them out
1018
- let text = _intToEightDigits ( t0) >> ( ( 8 - t0digits ) * 8 )
1041
+ let text = _intToEightDigits ( t0)
1019
1042
buffer. storeBytes (
1020
1043
of: text,
1021
1044
toByteOffset: nextDigit,
1022
1045
as: UInt64 . self)
1023
- nextDigit &+= t0digits
1024
- firstDigit &+= 1
1046
+ nextDigit &+= 8
1047
+ // Skip the leading zeros
1048
+ firstDigit &+= 9 - t0digits
1025
1049
} else {
1026
1050
// Our initial scaling did not produce too many digits. The
1027
1051
// `d12345678` value holds the first 7 digits (plus a leading
@@ -1182,6 +1206,17 @@ internal func _float80ToStringImpl(
1182
1206
}
1183
1207
}
1184
1208
1209
+ // Convert a Float80 to an optimal ASCII representation.
1210
+ // See notes above for comments on the output format here.
1211
+ // See _Float64ToASCII for comments on the algorithm.
1212
+ // Inputs:
1213
+ // * `value`: Float80 input
1214
+ // * `buffer`: Buffer to place the result
1215
+ // Returns: Range of bytes within `buffer` that contain the result
1216
+ //
1217
+ // Buffer must be at least 32 bytes long and must be pre-filled
1218
+ // with "0" characters, e.g., via
1219
+ // `InlineArray<32,UTF8.CodeUnit>(repeating:0x30)`
1185
1220
@available ( SwiftStdlib 6 . 2 , * )
1186
1221
internal func _Float80ToASCII(
1187
1222
value f: Float80 ,
@@ -1408,13 +1443,7 @@ fileprivate func _backend_256bit(
1408
1443
1409
1444
// Step 7: Generate digits
1410
1445
1411
- // Include 8 "0" characters at the beginning of the buffer
1412
- // for finishFormatting to use
1413
- buffer. storeBytes (
1414
- of: 0x3030303030303030 ,
1415
- toByteOffset: 0 ,
1416
- as: UInt64 . self)
1417
- // Start writing digits just after that
1446
+ // Leave 8 bytes at the beginning for finishFormatting to use
1418
1447
let firstDigit = 8
1419
1448
var nextDigit = firstDigit
1420
1449
buffer. storeBytes (
@@ -1526,7 +1555,7 @@ fileprivate func _backend_256bit(
1526
1555
// inserting decimal points, minus signs, exponents, etc, as
1527
1556
// necessary. To minimize the work here, this assumes that there are
1528
1557
// at least 5 unused bytes at the beginning of `buffer` before
1529
- // `firstDigit` and that those bytes are filled with `"0"` (0x30)
1558
+ // `firstDigit` and that all unused bytes are filled with `"0"` (0x30)
1530
1559
// characters.
1531
1560
1532
1561
@available ( SwiftStdlib 6 . 2 , * )
@@ -1646,25 +1675,8 @@ fileprivate func _finishFormatting(
1646
1675
// "12345678900.0"
1647
1676
// Fill trailing zeros, put ".0" at the end
1648
1677
// so the result is obviously floating-point.
1649
- let zeroEnd = firstDigit &+ base10Exponent &+ 3
1650
- // TODO: Find out how to use C memset() here:
1651
- // Blast 8 "0" digits into the buffer
1652
- buffer. storeBytes (
1653
- of: 0x3030303030303030 as UInt64 ,
1654
- toByteOffset: nextDigit,
1655
- as: UInt64 . self)
1656
- // Add more "0" digits if needed...
1657
- // (Note: Can't use a standard range loop because nextDigit+8
1658
- // can legitimately be larger than zeroEnd here.)
1659
- var i = nextDigit + 8
1660
- while i < zeroEnd {
1661
- unsafe buffer. storeBytes (
1662
- of: 0x30 ,
1663
- toUncheckedByteOffset: i,
1664
- as: UInt8 . self)
1665
- i &+= 1
1666
- }
1667
- nextDigit = zeroEnd
1678
+ // Remember buffer was initialized with "0"
1679
+ nextDigit = firstDigit &+ base10Exponent &+ 3
1668
1680
buffer. storeBytes (
1669
1681
of: 0x2e ,
1670
1682
toByteOffset: nextDigit &- 2 ,
0 commit comments