@@ -155,12 +155,24 @@ public func _float16ToStringImpl(
155155 return UInt64 ( truncatingIfNeeded: textLength)
156156}
157157
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+
158169@available ( SwiftStdlib 6 . 2 , * )
159170internal func _Float16ToASCII(
160171 value f: Float16 ,
161172 buffer utf8Buffer: inout MutableSpan < UTF8 . CodeUnit >
162173) -> Range < Int > {
163174 // We need a MutableRawSpan in order to use wide store/load operations
175+ // TODO: Tune this value down to the actual minimum for Float16
164176 precondition ( utf8Buffer. count >= 32 )
165177 var buffer = utf8Buffer. mutableBytes
166178
@@ -338,11 +350,7 @@ internal func _Float16ToASCII(
338350
339351 if fractionPart == 0 {
340352 // 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
346354 nextDigit &+= 1
347355 } else {
348356 // Step 7: Emit the fractional part by repeatedly
@@ -439,6 +447,17 @@ public func _float32ToStringImpl(
439447 }
440448}
441449
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)`
442461@available ( SwiftStdlib 6 . 2 , * )
443462internal func _Float32ToASCII(
444463 value f: Float32 ,
@@ -449,6 +468,8 @@ internal func _Float32ToASCII(
449468 // more detailed comments and explanation.
450469
451470 // 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)
452473 precondition ( utf8Buffer. count >= 32 )
453474 var buffer = utf8Buffer. mutableBytes
454475
@@ -561,12 +582,6 @@ internal func _Float32ToASCII(
561582 var delta = u &- l
562583 let fractionMask : UInt64 = ( 1 << fractionBits) - 1
563584
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-
570585 // Overwrite the first digit at index 7:
571586 let firstDigit = 7
572587 let digit = ( t >> fractionBits) &+ 0x30
@@ -684,6 +699,18 @@ public func _float64ToStringImpl(
684699 }
685700}
686701
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)`
687714@available ( SwiftStdlib 6 . 2 , * )
688715internal func _Float64ToASCII(
689716 value d: Float64 ,
@@ -937,10 +964,6 @@ internal func _Float64ToASCII(
937964
938965 var nextDigit = 5
939966 var firstDigit = nextDigit
940- unsafe buffer. storeBytes (
941- of: 0x3030303030303030 as UInt64 ,
942- toUncheckedByteOffset: 0 ,
943- as: UInt64 . self)
944967
945968 // Our initial scaling gave us the first 7 digits already:
946969 let d12345678 = UInt32 ( truncatingIfNeeded: t. _high >> 32 )
@@ -1015,13 +1038,14 @@ internal func _Float64ToASCII(
10151038 t0 &= ~ 1
10161039 }
10171040 // t0 has t0digits digits. Write them out
1018- let text = _intToEightDigits ( t0) >> ( ( 8 - t0digits ) * 8 )
1041+ let text = _intToEightDigits ( t0)
10191042 buffer. storeBytes (
10201043 of: text,
10211044 toByteOffset: nextDigit,
10221045 as: UInt64 . self)
1023- nextDigit &+= t0digits
1024- firstDigit &+= 1
1046+ nextDigit &+= 8
1047+ // Skip the leading zeros
1048+ firstDigit &+= 9 - t0digits
10251049 } else {
10261050 // Our initial scaling did not produce too many digits. The
10271051 // `d12345678` value holds the first 7 digits (plus a leading
@@ -1182,6 +1206,17 @@ internal func _float80ToStringImpl(
11821206 }
11831207}
11841208
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)`
11851220@available ( SwiftStdlib 6 . 2 , * )
11861221internal func _Float80ToASCII(
11871222 value f: Float80 ,
@@ -1408,13 +1443,7 @@ fileprivate func _backend_256bit(
14081443
14091444 // Step 7: Generate digits
14101445
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
14181447 let firstDigit = 8
14191448 var nextDigit = firstDigit
14201449 buffer. storeBytes (
@@ -1526,7 +1555,7 @@ fileprivate func _backend_256bit(
15261555// inserting decimal points, minus signs, exponents, etc, as
15271556// necessary. To minimize the work here, this assumes that there are
15281557// 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)
15301559// characters.
15311560
15321561@available ( SwiftStdlib 6 . 2 , * )
@@ -1646,25 +1675,8 @@ fileprivate func _finishFormatting(
16461675 // "12345678900.0"
16471676 // Fill trailing zeros, put ".0" at the end
16481677 // 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
16681680 buffer. storeBytes (
16691681 of: 0x2e ,
16701682 toByteOffset: nextDigit &- 2 ,
0 commit comments