Files
swift-mirror/stdlib/public/core/FloatingPointToString.swift
Tim Kientzle e943878f9a Prefill the work buffer with '0' characters
For example, this avoids the need to do any explicit byte-by-byte
writes when expanding "123" out to "123000000.0".

This also required reworking the "back out extra digits" process
for Float64 to ensure the unused digits get written as '0' characters
instead of null bytes.
2025-09-22 12:40:12 -07:00

2590 lines
103 KiB
Swift

//===--- FloatingPointToString.swift -------------------------*- Swift -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2018-2025 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===---------------------------------------------------------------------===//
//
// Converts floating-point types to "optimal" text formats.
//
// The "optimal" form is one with a minimum number of significant
// digits which will parse to exactly the original value. This form
// is ideal for JSON serialization and general printing where you
// don't have specific requirements on the number of significant
// digits.
//
//===---------------------------------------------------------------------===//
///
/// For binary16, this code uses a simple approach that is normally
/// implemented with variable-length arithmetic. However, due to the
/// limited range of binary16, this can be implemented with only
/// 32-bit integer arithmetic.
///
/// For other formats, we use a modified form of the Grisu2
/// algorithm from Florian Loitsch; "Printing Floating-Point Numbers
/// Quickly and Accurately with Integers", 2010.
/// https://doi.org/10.1145/1806596.1806623
///
/// Some of the Grisu2 modifications were suggested by the "Errol
/// paper": Marc Andrysco, Ranjit Jhala, Sorin Lerner; "Printing
/// Floating-Point Numbers: A Faster, Always Correct Method", 2016.
/// https://doi.org/10.1145/2837614.2837654
/// In particular, the Errol paper explored the impact of higher-precision
/// fixed-width arithmetic on Grisu2 and showed a way to rapidly test
/// the correctness of Grisu-style algorithms.
///
/// A few further improvements were inspired by the Ryu algorithm
/// from Ulf Anders; "Ryū: fast float-to-string conversion", 2018.
/// https://doi.org/10.1145/3296979.3192369
///
/// The full algorithm is extensively commented in the Float64 version
/// below; refer to that for details.
///
/// In summary, this implementation is:
///
/// * Fast. It uses only fixed-width integer arithmetic and has
/// constant memory requirements. For double-precision values on
/// 64-bit processors, it is competitive with Ryu. For double-precision
/// values on 32-bit processors, and higher-precision values on all
/// processors, it is considerably faster.
///
/// * Always Accurate. Except for NaNs, converting the decimal form
/// back to binary will always yield an equal value. For the IEEE
/// 754 formats, the round trip will produce exactly the same bit
/// pattern in memory. This assumes, of course, that the conversion
/// from text to binary uses a correctly-rounded algorithm such as
/// Clinger 1990 or Eisel-Lemire 2021.
///
/// * Always Short. This always selects an accurate result with the
/// minimum number of significant digits.
///
/// * Always Close. Among all accurate, short results, this always
/// chooses the result that is closest to the exact floating-point
/// value. (In case of an exact tie, it rounds the last digit even.)
///
/// Beyond the requirements above, the precise text form has been
/// tuned to try to maximize readability:
/// * Always include a '.' or an 'e' so the result is obviously
/// a floating-point value
/// * Exponential form always has 1 digit before the decimal point
/// * When present, a '.' is never the first or last character
/// * There is a consecutive range of integer values that can be
/// represented in any particular type (-2^54...2^54 for double).
/// We do not use exponential form for integral numbers in this
/// range.
/// * Generally follow existing practice: Don't use use exponential
/// form for fractional values bigger than 10^-4; always write at
/// least 2 digits for an exponent.
/// * Apart from the above, we do prefer shorter output.
/// Note: If you want to compare performance of this implementation
/// versus some others, keep in mind that this implementation does
/// deliberately sacrifice some performance. Any attempt to compare
/// the performance of this implementation to others should
/// try to compensate for the following:
/// * The output ergonomics described above do take some time.
/// It would be faster to always emit the form "123456e-78"
// (See `finishFormatting()`)
/// * The implementations in published papers generally include
/// large tables with every power of 10 computed out. We've
/// factored these tables down to conserve code size, which
/// requires some additional work to reconstruct the needed power
/// of 10. (See the `intervalContainingPowerOf10_*` functions)
///
/// This Swift implementation was ported from an earlier C version;
/// the output is exactly the same in all cases.
/// A few notes on the Swift transcription:
/// * We use MutableSpan<UTF8.CodeUnit> and MutableRawSpan to
/// identify blocks of working memory.
/// * We use unsafe/unchecked operations extensively, supported by
/// several years of analysis and testing of the original C
/// implementation to ensure that no unsafety actually occurs. For
/// Float32, that testing was exhaustive -- we verified all 4
/// billion possible Float32 values.
/// * The Swift code uses an idiom of building up to 8 digit characters
/// in a UInt64 and then writing the whole block to memory.
/// * The Swift version is slightly faster than the C version;
/// mostly thanks to various minor algorithmic tweaks that were
/// found during the translation process.
/// * Most of this file is annotated for SwiftStdlib 6.2
/// because it relies on UInt128, MutableSpan, and InlineArray.
///
// ----------------------------------------------------------------------------
// ================================================================
//
// Float16
//
// ================================================================
// Float16 is not currently supported on Intel macOS.
// (This will change once there's a fully-stable Float16
// ABI on that platform.)
#if !((os(macOS) || targetEnvironment(macCatalyst)) && arch(x86_64))
// Support Legacy ABI on top of new implementation
@available(SwiftStdlib 6.2, *)
@_silgen_name("swift_float16ToString")
public func _float16ToStringImpl(
_ textBuffer: UnsafeMutablePointer<UTF8.CodeUnit>,
_ bufferLength: UInt,
_ value: Float16,
_ debug: Bool
) -> UInt64 {
// Code below works with raw memory.
var buffer = unsafe MutableSpan<UTF8.CodeUnit>(
_unchecked: textBuffer,
count: Int(bufferLength))
let textRange = _Float16ToASCII(value: value, buffer: &buffer)
let textLength = textRange.upperBound - textRange.lowerBound
// Move the text to the start of the buffer
if textRange.lowerBound != 0 {
unsafe _memmove(
dest: textBuffer,
src: textBuffer + textRange.lowerBound,
size: UInt(truncatingIfNeeded: textLength))
}
return UInt64(truncatingIfNeeded: textLength)
}
// Convert a Float16 to an optimal ASCII representation.
// See notes above for comments on the output format here.
// Inputs:
// * `value`: Float16 input
// * `buffer`: Buffer to place the result
// Returns: Range of bytes within `buffer` that contain the result
//
// Buffer must be at least 32 bytes long and must be pre-filled
// with "0" characters, e.g., via
// `InlineArray<32,UTF8.CodeUnit>(repeating:0x30)`
@available(SwiftStdlib 6.2, *)
internal func _Float16ToASCII(
value f: Float16,
buffer utf8Buffer: inout MutableSpan<UTF8.CodeUnit>
) -> Range<Int> {
// We need a MutableRawSpan in order to use wide store/load operations
// TODO: Tune this value down to the actual minimum for Float16
precondition(utf8Buffer.count >= 32)
var buffer = utf8Buffer.mutableBytes
// Step 1: Handle various input cases:
let binaryExponent: Int
let significand: Float16.RawSignificand
let exponentBias = (1 << (Float16.exponentBitCount - 1)) - 2 // 14
if (f.exponentBitPattern == 0x1f) { // NaN or Infinity
if (f.isInfinite) {
return _infinity(buffer: &buffer, sign: f.sign)
} else { // f.isNaN
let quietBit =
(f.significandBitPattern >> (Float16.significandBitCount - 1)) & 1
let payloadMask = UInt16(1 &<< (Float16.significandBitCount - 2)) - 1
let payload16 = f.significandBitPattern & payloadMask
return nan_details(
buffer: &buffer,
sign: f.sign,
quiet: quietBit != 0,
payloadHigh: 0,
payloadLow: UInt64(truncatingIfNeeded:payload16))
}
} else if (f.exponentBitPattern == 0) {
if (f.isZero) {
return _zero(buffer: &buffer, sign: f.sign)
} else { // Subnormal
binaryExponent = 1 - exponentBias
significand = f.significandBitPattern &<< 2
}
} else { // normal
binaryExponent = Int(f.exponentBitPattern) &- exponentBias
let hiddenBit = Float16.RawSignificand(1) << Float16.significandBitCount
significand = (f.significandBitPattern &+ hiddenBit) &<< 2
}
// Step 2: Determine the exact target interval
let halfUlp: Float16.RawSignificand = 2
let quarterUlp = halfUlp >> 1
let upperMidpointExact =
significand &+ halfUlp
let lowerMidpointExact =
significand &- ((f.significandBitPattern == 0) ? quarterUlp : halfUlp)
var firstDigit = 1
var nextDigit = firstDigit
// Emit the text form differently depending on what range it's in.
// We use `storeBytes(of:toUncheckedByteOffset:as:)` for most of
// the output, but are careful to use the checked/safe form
// `storeBytes(of:toByteOffset:as:)` for the last byte so that we
// reliably crash if we overflow the provided buffer.
// Step 3: If it's < 10^-5, format as exponential form
if binaryExponent < -13 || (binaryExponent == -13 && significand < 0x1a38) {
var decimalExponent = -5
var u =
(UInt32(upperMidpointExact) << (28 - 13 &+ binaryExponent)) &* 100000
var l =
(UInt32(lowerMidpointExact) << (28 - 13 &+ binaryExponent)) &* 100000
var t =
(UInt32(significand) << (28 - 13 &+ binaryExponent)) &* 100000
let mask = (UInt32(1) << 28) - 1
if t < ((1 << 28) / 10) {
u &*= 100
l &*= 100
t &*= 100
decimalExponent &-= 2
}
if t < (1 << 28) {
u &*= 10
l &*= 10
t &*= 10
decimalExponent &-= 1
}
let uDigit = u >> 28
if uDigit == (l >> 28) {
// More than one digit, so write first digit, ".", then the rest
unsafe buffer.storeBytes(
of: 0x30 + UInt8(truncatingIfNeeded: uDigit),
toUncheckedByteOffset: nextDigit,
as: UInt8.self)
nextDigit &+= 1
unsafe buffer.storeBytes(
of: 0x2e,
toUncheckedByteOffset: nextDigit,
as: UInt8.self)
nextDigit &+= 1
while true {
u = (u & mask) &* 10
l = (l & mask) &* 10
t = (t & mask) &* 10
let uDigit = u >> 28
if uDigit != (l >> 28) {
// Stop before emitting the last digit
break
}
unsafe buffer.storeBytes(
of: 0x30 &+ UInt8(truncatingIfNeeded: uDigit),
toUncheckedByteOffset: nextDigit,
as: UInt8.self)
nextDigit &+= 1
}
}
let digit = 0x30 &+ (t &+ (1 << 27)) >> 28
unsafe buffer.storeBytes(
of: UInt8(truncatingIfNeeded: digit),
toUncheckedByteOffset: nextDigit,
as: UInt8.self)
nextDigit &+= 1
unsafe buffer.storeBytes(
of: 0x65, // "e"
toUncheckedByteOffset: nextDigit,
as: UInt8.self)
nextDigit &+= 1
unsafe buffer.storeBytes(
of: 0x2d, // "-"
toUncheckedByteOffset: nextDigit,
as: UInt8.self)
nextDigit &+= 1
unsafe buffer.storeBytes(
of: UInt8(truncatingIfNeeded: -decimalExponent / 10 &+ 0x30),
toUncheckedByteOffset: nextDigit,
as: UInt8.self)
nextDigit &+= 1
// Last write on this branch, so use a safe checked store
buffer.storeBytes(
of: UInt8(truncatingIfNeeded: -decimalExponent % 10 &+ 0x30),
toByteOffset: nextDigit,
as: UInt8.self)
nextDigit &+= 1
} else {
// Step 4: Greater than 10^-5, so use decimal format "123.45"
// (Note: Float16 is never big enough to need exponential for
// positive exponents)
// First, split into integer and fractional parts:
let intPart : Float16.RawSignificand
let fractionPart : Float16.RawSignificand
if binaryExponent < 13 {
intPart = significand >> (13 &- binaryExponent)
fractionPart = significand &- (intPart &<< (13 &- binaryExponent))
} else {
intPart = significand &<< (binaryExponent &- 13)
fractionPart = significand &- (intPart >> (binaryExponent &- 13))
}
// Step 5: Emit the integer part
let text = _intToEightDigits(UInt32(intPart))
unsafe buffer.storeBytes(
of: text,
toUncheckedByteOffset: nextDigit,
as: UInt64.self)
nextDigit &+= 8
// Skip leading zeros
if intPart < 10 {
firstDigit &+= 7
} else if intPart < 100 {
firstDigit &+= 6
} else if intPart < 1000 {
firstDigit &+= 5
} else if intPart < 10000 {
firstDigit &+= 4
} else {
firstDigit &+= 3
}
// After the integer part comes a period...
unsafe buffer.storeBytes(
of: 0x2e,
toUncheckedByteOffset: nextDigit,
as: UInt8.self)
nextDigit &+= 1
if fractionPart == 0 {
// Step 6: No fraction, so ".0" and we're done
// "0" write is free since buffer is pre-initialized
nextDigit &+= 1
} else {
// Step 7: Emit the fractional part by repeatedly
// multiplying by 10 to produce successive digits:
var u = UInt32(upperMidpointExact) &<< (28 - 13 &+ binaryExponent)
var l = UInt32(lowerMidpointExact) &<< (28 - 13 &+ binaryExponent)
var t = UInt32(fractionPart) &<< (28 - 13 &+ binaryExponent)
let mask = (UInt32(1) << 28) - 1
var uDigit: UInt8 = 0
var lDigit: UInt8 = 0
while true {
u = (u & mask) &* 10
l = (l & mask) &* 10
uDigit = UInt8(truncatingIfNeeded: u >> 28)
lDigit = UInt8(truncatingIfNeeded: l >> 28)
if uDigit != lDigit {
t = (t & mask) &* 10
break
}
// This overflows, but we don't care at this point.
t &*= 10
unsafe buffer.storeBytes(
of: 0x30 &+ uDigit,
toUncheckedByteOffset: nextDigit,
as: UInt8.self)
nextDigit &+= 1
}
t &+= 1 << 27
if (t & mask) == 0 { // Exactly 1/2
t = (t >> 28) & ~1 // Round last digit even
// Rounding `t` even can end up moving `t` below
// `l`. Detect and correct for this possibility.
// Exhaustive testing shows that the only input value
// affected by this is 0.015625 == 2^-6, which
// incorrectly prints as "0.01562" without this fix.
// With this, it prints correctly as "0.01563"
if t < lDigit || (t == lDigit && l > 0) {
t += 1
}
} else {
t >>= 28
}
// Last write on this branch, so use a checked store
buffer.storeBytes(
of: UInt8(truncatingIfNeeded: 0x30 + t),
toByteOffset: nextDigit,
as: UInt8.self)
nextDigit &+= 1
}
}
if f.sign == .minus {
buffer.storeBytes(
of: 0x2d,
toByteOffset: firstDigit &- 1,
as: UInt8.self) // "-"
firstDigit &-= 1
}
return firstDigit..<nextDigit
}
#endif
// ================================================================
//
// Float32
//
// ================================================================
// Support Legacy ABI on top of new implementation
@_silgen_name("swift_float32ToString")
public func _float32ToStringImpl(
_ textBuffer: UnsafeMutablePointer<UTF8.CodeUnit>,
_ bufferLength: UInt,
_ value: Float32,
_ debug: Bool
) -> UInt64 {
if #available(SwiftStdlib 6.2, *) {
// Code below works with raw memory.
var buffer = unsafe MutableSpan<UTF8.CodeUnit>(
_unchecked: textBuffer,
count: Int(bufferLength))
let textRange = _Float32ToASCII(value: value, buffer: &buffer)
let textLength = textRange.upperBound - textRange.lowerBound
// Move the text to the start of the buffer
if textRange.lowerBound != 0 {
unsafe _memmove(
dest: textBuffer,
src: textBuffer + textRange.lowerBound,
size: UInt(truncatingIfNeeded: textLength))
}
return UInt64(truncatingIfNeeded: textLength)
} else {
fatalError()
}
}
// Convert a Float32 to an optimal ASCII representation.
// See notes above for comments on the output format here.
// See _Float64ToASCII for comments on the algorithm.
// Inputs:
// * `value`: Float32 input
// * `buffer`: Buffer to place the result
// Returns: Range of bytes within `buffer` that contain the result
//
// Buffer must be at least 32 bytes long and must be pre-filled
// with "0" characters, e.g., via
// `InlineArray<32,UTF8.CodeUnit>(repeating:0x30)`
@available(SwiftStdlib 6.2, *)
internal func _Float32ToASCII(
value f: Float32,
buffer utf8Buffer: inout MutableSpan<UTF8.CodeUnit>
) -> Range<Int> {
// Note: The algorithm here is the same as for Float64, only
// with narrower arithmetic. Refer to `_Float64ToASCII` for
// more detailed comments and explanation.
// We need a MutableRawSpan in order to use wide store/load operations
// TODO: Tune this limit down to the actual minimum we need here
// TODO: `assert` that the buffer is filled with 0x30 bytes (in debug builds)
precondition(utf8Buffer.count >= 32)
var buffer = utf8Buffer.mutableBytes
// Step 1: Handle the special cases, decompose the input
let binaryExponent: Int
let significand: Float.RawSignificand
let exponentBias = (1 << (Float.exponentBitCount - 1)) - 2 // 126
if (f.exponentBitPattern == 0xff) {
if (f.isInfinite) {
return _infinity(buffer: &buffer, sign: f.sign)
} else { // f.isNaN
let quietBit =
(f.significandBitPattern >> (Float.significandBitCount - 1)) & 1
let payloadMask = UInt32(1 << (Float.significandBitCount - 2)) - 1
let payload32 = f.significandBitPattern & payloadMask
return nan_details(
buffer: &buffer,
sign: f.sign,
quiet: quietBit != 0,
payloadHigh: 0,
payloadLow: UInt64(truncatingIfNeeded:payload32))
}
} else if (f.exponentBitPattern == 0) {
if (f.isZero) {
return _zero(buffer: &buffer, sign: f.sign)
} else { // f.isSubnormal
binaryExponent = 1 - exponentBias
significand = f.significandBitPattern &<< Float.exponentBitCount
}
} else {
binaryExponent = Int(f.exponentBitPattern) &- exponentBias
significand =
((f.significandBitPattern &+ (1 << Float.significandBitCount))
&<< Float.exponentBitCount)
}
// Step 2: Determine the exact unscaled target interval
let halfUlp: Float.RawSignificand = 1 << (Float.exponentBitCount - 1)
let quarterUlp = halfUlp >> 1
let upperMidpointExact =
significand &+ halfUlp
let lowerMidpointExact =
significand &- ((f.significandBitPattern == 0) ? quarterUlp : halfUlp)
let isOddSignificand = ((f.significandBitPattern & 1) != 0)
// Step 3: Estimate the base 10 exponent
var base10Exponent = decimalExponentFor2ToThe(binaryExponent)
// Step 4: Compute power-of-10 scale factor
var powerOfTenRoundedDown: UInt64 = 0
var powerOfTenRoundedUp: UInt64 = 0
let bulkFirstDigits = 1
let powerOfTenExponent = _intervalContainingPowerOf10_Binary32(
p: -base10Exponent &+ bulkFirstDigits &- 1,
lower: &powerOfTenRoundedDown,
upper: &powerOfTenRoundedUp)
let extraBits = binaryExponent &+ powerOfTenExponent
// Step 5: Scale the interval (with rounding)
// Experimentally, 11 is as large as we can go here without
// introducing errors.
// We need 7 to generate 2 digits at a time below.
// 11 should allow us to generate 3 digits at a time, but
// that doesn't seem to be any faster.
let integerBits = 11
let fractionBits = 64 - integerBits
var u: UInt64
var l: UInt64
if isOddSignificand {
// Narrow the interval (odd significand)
let u1 = _multiply64x32RoundingDown(
powerOfTenRoundedDown,
upperMidpointExact)
u = u1 >> (integerBits - extraBits)
let l1 = _multiply64x32RoundingUp(
powerOfTenRoundedUp,
lowerMidpointExact)
let bias = UInt64((1 &<< (integerBits &- extraBits)) &- 1)
l = (l1 &+ bias) >> (integerBits &- extraBits)
} else {
// Widen the interval (even significand)
let u1 = _multiply64x32RoundingUp(
powerOfTenRoundedUp,
upperMidpointExact)
let bias = UInt64((1 &<< (integerBits &- extraBits)) &- 1)
u = (u1 &+ bias) >> (integerBits &- extraBits)
let l1 = _multiply64x32RoundingDown(
powerOfTenRoundedDown,
lowerMidpointExact)
l = l1 >> (integerBits &- extraBits)
}
// Step 6: Align first digit, adjust exponent
while u < (UInt64(1) &<< fractionBits) {
base10Exponent &-= 1
l &*= 10
u &*= 10
}
// Step 7: Generate decimal digits into the destination buffer
var t = u
var delta = u &- l
let fractionMask: UInt64 = (1 << fractionBits) - 1
// Overwrite the first digit at index 7:
let firstDigit = 7
let digit = (t >> fractionBits) &+ 0x30
t &= fractionMask
unsafe buffer.storeBytes(
of: UInt8(truncatingIfNeeded: digit),
toUncheckedByteOffset: firstDigit,
as: UInt8.self)
var nextDigit = firstDigit &+ 1
// Generate 2 digits at a time...
while (delta &* 10) < ((t &* 10) & fractionMask) {
delta &*= 100
t &*= 100
let d12 = Int(truncatingIfNeeded: t >> fractionBits)
let text = unsafe asciiDigitTable[unchecked: d12]
unsafe buffer.storeBytes(
of: text,
toUncheckedByteOffset: nextDigit,
as: UInt16.self)
nextDigit &+= 2
t &= fractionMask
}
// ... and a final single digit, if necessary
if delta < t {
delta &*= 10
t &*= 10
let text = 0x30 + UInt8(truncatingIfNeeded: t >> fractionBits)
unsafe buffer.storeBytes(
of: text,
toUncheckedByteOffset: nextDigit,
as: UInt8.self)
nextDigit &+= 1
t &= fractionMask
}
// Adjust the final digit to be closer to the original value
let isBoundary = (f.significandBitPattern == 0)
if delta > t &+ (1 << fractionBits) {
let skew: UInt64
if isBoundary {
skew = delta &- delta / 3 &- t
} else {
skew = delta / 2 &- t
}
let one = UInt64(1) << (64 - integerBits)
let lastAccurateBit = UInt64(1) << 24
let fractionMask = (one - 1) & ~(lastAccurateBit - 1)
let oneHalf = one >> 1
var lastDigit = unsafe buffer.unsafeLoad(
fromUncheckedByteOffset: nextDigit &- 1,
as: UInt8.self)
if ((skew &+ (lastAccurateBit >> 1)) & fractionMask) == oneHalf {
// Skew is integer + 1/2, round even after adjustment
let adjust = skew >> (64 - integerBits)
lastDigit &-= UInt8(truncatingIfNeeded: adjust)
lastDigit &= ~1
} else {
// Round nearest
let adjust = (skew &+ oneHalf) >> (64 - integerBits)
lastDigit &-= UInt8(truncatingIfNeeded: adjust)
}
unsafe buffer.storeBytes(
of: lastDigit,
toUncheckedByteOffset: nextDigit &- 1,
as: UInt8.self)
}
// Step 8: Finish formatting
let forceExponential =
((binaryExponent > 25)
|| (binaryExponent == 25 && !isBoundary))
return _finishFormatting(
buffer: &buffer,
sign: f.sign,
firstDigit: firstDigit,
nextDigit: nextDigit,
forceExponential: forceExponential,
base10Exponent: base10Exponent)
}
// ================================================================
//
// Float64
//
// ================================================================
// Support Legacy ABI on top of new implementation
@_silgen_name("swift_float64ToString")
public func _float64ToStringImpl(
_ textBuffer: UnsafeMutablePointer<UTF8.CodeUnit>,
_ bufferLength: UInt,
_ value: Float64,
_ debug: Bool
) -> UInt64 {
if #available(SwiftStdlib 6.2, *) {
// Code below works with raw memory.
var buffer = unsafe MutableSpan<UTF8.CodeUnit>(
_unchecked: textBuffer,
count: Int(bufferLength))
let textRange = _Float64ToASCII(value: value, buffer: &buffer)
let textLength = textRange.upperBound - textRange.lowerBound
// Move the text to the start of the buffer
if textRange.lowerBound != 0 {
unsafe _memmove(
dest: textBuffer,
src: textBuffer + textRange.lowerBound,
size: UInt(truncatingIfNeeded: textLength))
}
return UInt64(truncatingIfNeeded: textLength)
} else {
fatalError()
}
}
// Convert a Float64 to an optimal ASCII representation.
// See notes above for comments on the output format here.
// The algorithm is extensively commented inline; the comments
// at the top of this source file give additional context.
// Inputs:
// * `value`: Float64 input
// * `buffer`: Buffer to place the result
// Returns: Range of bytes within `buffer` that contain the result
//
// Buffer must be at least 32 bytes long and must be pre-filled
// with "0" characters, e.g., via
// `InlineArray<32,UTF8.CodeUnit>(repeating:0x30)`
@available(SwiftStdlib 6.2, *)
internal func _Float64ToASCII(
value d: Float64,
buffer utf8Buffer: inout MutableSpan<UTF8.CodeUnit>
) -> Range<Int> {
// We need a MutableRawSpan in order to use wide store/load operations
precondition(utf8Buffer.count >= 32)
var buffer = utf8Buffer.mutableBytes
//
// Step 1: Handle the special cases, decompose the input
//
let binaryExponent: Int
let significand: Double.RawSignificand
let exponentBias = 1022 // (1 << (Double.exponentBitCount - 1)) - 2
if (d.exponentBitPattern == 0x7ff) {
if (d.isInfinite) {
return _infinity(buffer: &buffer, sign: d.sign)
} else { // d.isNaN
let quietBit =
(d.significandBitPattern >> (Double.significandBitCount - 1)) & 1
let payloadMask = (UInt64(1) &<< (Double.significandBitCount - 2)) - 1
let payload64 = d.significandBitPattern & payloadMask
return nan_details(
buffer: &buffer,
sign: d.sign,
quiet: quietBit != 0,
payloadHigh: 0,
payloadLow: UInt64(truncatingIfNeeded:payload64))
}
} else if (d.exponentBitPattern == 0) {
if (d.isZero) {
return _zero(buffer: &buffer, sign: d.sign)
} else { // d.isSubnormal
binaryExponent = 1 - exponentBias
significand = d.significandBitPattern &<< Double.exponentBitCount
}
} else {
binaryExponent = Int(d.exponentBitPattern) &- exponentBias
significand =
((d.significandBitPattern &+ (1 << Double.significandBitCount))
&<< Double.exponentBitCount)
}
// The input has been decomposed as significand * 2^binaryExponent,
// where `significand` is a 64-bit fraction with the binary
// point at the far left.
// Step 2: Determine the exact unscaled target interval
// Grisu-style algorithms construct the shortest decimal digit
// sequence within a specific interval. To build the appropriate
// interval, we start by computing the midpoints between this
// floating-point value and the adjacent ones. Note that this
// step is an exact computation.
let halfUlp: Double.RawSignificand = 1 << (Double.exponentBitCount - 1)
let quarterUlp = halfUlp >> 1
let upperMidpointExact = significand &+ halfUlp
let lowerMidpointExact =
significand &- ((d.significandBitPattern == 0) ? quarterUlp : halfUlp)
let isOddSignificand = ((d.significandBitPattern & 1) != 0)
// Step 3: Estimate the base 10 exponent
// Grisu algorithms are based in part on a simple technique for
// generating a base-10 form for a binary floating-point number.
// Start with a binary floating-point number `f * 2^e` and then
// estimate the decimal exponent `p`. You can then rewrite your
// original number as:
//
// ```
// f * 2^e * 10^-p * 10^p
// ```
//
// The last term is part of our output, and a good estimate for
// `p` will ensure that `2^e * 10^-p` is close to 1. Multiplying
// the first three terms then yields a fraction suitable for
// producing the decimal digits. Here we use a very fast estimate
// of `p` that is never off by more than 1; we'll have
// opportunities later to correct any error.
var base10Exponent = decimalExponentFor2ToThe(binaryExponent)
// Step 4: Compute power-of-10 scale factor
// Compute `10^-p` to 128-bit precision. We generate
// both over- and under-estimates to ensure we can exactly
// bound the later use of these values.
// The `powerOfTenRounded{Up,Down}` values are 128-bit
// pure fractions with the decimal point at the far left.
var powerOfTenRoundedDown: UInt128 = 0
var powerOfTenRoundedUp: UInt128 = 0
// Note the extra factor of 10^bulkFirstDigits -- that will give
// us a headstart on digit generation later on. (In contrast, Ryu
// uses an extra factor of 10^17 here to get all the digits up
// front, but then has to back out any extra digits. Doing that
// with a 17-digit value requires 64-bit division, which is the
// root cause of Ryu's poor performance on 32-bit processors. We
// also might have to back out extra digits if 7 is too many, but
// will only need 32-bit division in that case.)
let bulkFirstDigits = 7
let bulkFirstDigitFactor: UInt32 = 1000000 // 10^(bulkFirstDigits - 1)
let powerOfTenExponent = _intervalContainingPowerOf10_Binary64(
p: -base10Exponent &+ bulkFirstDigits &- 1,
lower: &powerOfTenRoundedDown,
upper: &powerOfTenRoundedUp)
let extraBits = binaryExponent + powerOfTenExponent
// Step 5: Scale the interval (with rounding)
// As mentioned above, the final digit generation works
// with an interval, so we actually apply the scaling
// to the upper and lower midpoint values separately.
// As part of the scaling here, we'll switch from a pure
// fraction with zero bit integer portion and 128-bit fraction
// to a fixed-point form with 32 bits in the integer portion.
let integerBits = 32
let roundingBias =
UInt128((1 &<< UInt64(truncatingIfNeeded: integerBits &- extraBits)) &- 1)
var u: UInt128
var l: UInt128
if isOddSignificand {
// Case A: Narrow the interval (odd significand)
// Loitsch' original Grisu2 always rounds so as to narrow the
// interval. Since our digit generation will select a value
// within the scaled interval, narrowing the interval
// guarantees that we will find a digit sequence that converts
// back to the original value.
// This ensures accuracy but, as explained in Loitsch' paper,
// this carries a risk that there will be a shorter digit
// sequence outside of our narrowed interval that we will
// miss. This risk obviously gets lower with increased
// precision, but it wasn't until the Errol paper that anyone
// had a good way to test whether a particular implementation
// had sufficient precision. That paper shows a way to enumerate
// the worst-case numbers; those numbers that are extremely close
// to the mid-points between adjacent floating-point values.
// These are the values that might sit just outside of the
// narrowed interval. By testing these values, we can verify
// the correctness of our implementation.
// Multiply out the upper midpoint, rounding down...
let u1 = _multiply128x64RoundingDown(
powerOfTenRoundedDown,
upperMidpointExact)
// Account for residual binary exponent and adjust
// to the fixed-point format
u = u1 >> (integerBits - extraBits)
// Conversely for the lower midpoint...
let l1 = _multiply128x64RoundingUp(
powerOfTenRoundedUp,
lowerMidpointExact)
l = (l1 + roundingBias) >> (integerBits - extraBits)
} else {
// Case B: Widen the interval (even significand)
// As explained in Errol Theorem 6, in certain cases there is
// a short decimal representation at the exact boundary of the
// scaled interval. When such a number is converted back to
// binary, it will get rounded to the adjacent even
// significand.
// So when the significand is even, we round so as to widen
// the interval in order to ensure that the exact midpoints
// are considered. Of couse, this ensures that we find a
// short result but carries a risk of selecting a result
// outside of the exact scaled interval (which would be
// inaccurate).
// (This technique of rounding differently for even/odd significands
// seems to be new; I've not seen it described in any of the
// papers on floating-point printing.)
// The same testing approach described above (based on results
// in the Errol paper) also applies
// to this case.
let u1 = _multiply128x64RoundingUp(
powerOfTenRoundedUp,
upperMidpointExact)
u = (u1 &+ roundingBias) >> (integerBits - extraBits)
let l1 = _multiply128x64RoundingDown(
powerOfTenRoundedDown,
lowerMidpointExact)
l = l1 >> (integerBits - extraBits)
}
// Step 6: Align the first digit, adjust exponent
// Calculations above used an estimate for the power-of-ten scale.
// Here, we compensate for any error in that estimate by testing
// whether we have the expected number of digits in the integer
// portion and correcting as necessary. This also serves to
// prune leading zeros from subnormals.
// Except for subnormals, this loop never runs more than once.
// For subnormals, this might run as many as 16 times.
let minimumU = UInt128(bulkFirstDigitFactor) << (128 - integerBits)
while u < minimumU {
base10Exponent -= 1
l &*= 10
u &*= 10
}
// Step 7: Produce decimal digits
// One standard approach generates digits for the scaled upper and
// lower boundaries and stops at the first digit that
// differs. For example, note that 0.1234 is the shortest decimal
// between u = 0.123456 and l = 0.123345.
// Grisu optimizes this by generating digits for the upper bound
// (multiplying by 10 to isolate each digit) while simultaneously
// scaling the interval width `delta`. As we remove each digit
// from the upper bound, the remainder is the difference between
// the base-10 value generated so far and the true upper bound.
// When that remainder is less than the scaled width of the
// interval, we know the current digits specify a value within the
// target interval.
// The logic below actually blends three different digit-generation
// strategies:
// * The first digits are already in the integer portion of the
// fixed-point value, thanks to the `bulkFirstDigits` factor above.
// We can just break those down and write them out.
// * If we generated too many digits, we use a Ryu-inspired technique
// to backtrack.
// * If we generated too few digits (the usual case), we use an
// optimized form of the Grisu2 method to produce the remaining
// values.
//
// Generate digits and build the output.
//
// Generate digits for `t` with interval width `delta = u - l`
// As above, these are fixed-point with 32-bit integer, 96-bit fraction
var t = u
var delta = u &- l
let fractionMask = (UInt128(1) << 96) - 1
var nextDigit = 5
var firstDigit = nextDigit
// Our initial scaling gave us the first 7 digits already:
let d12345678 = UInt32(truncatingIfNeeded: t._high >> 32)
t &= fractionMask
if delta >= t {
// Oops! We have too many digits. Back out the extra ones to
// get the right answer. This is similar to Ryu, but since
// we've only produced seven digits, we only need 32-bit
// arithmetic here. (Ryu needs 64-bit arithmetic to back out
// digits, which severely compromises performance on 32-bit
// processors. The same problem occurs with Ryu for 128-bit
// floats on 64-bit processors.)
// A few notes:
// * Our target hardware always supports 32-bit hardware division,
// so this should be reasonably fast.
// * For small integers (like "2.0"), Ryu would have to back out 16
// digits; we only have to back out 6.
// * Very few double-precision values actually need fewer than 7
// digits. So this is rarely used except in workloads that
// specifically use double for small integers.
// Why this is critical for performance: In order to use the
// 8-digits-at-a-time optimization below, we need at least 30
// bits in the integer part of our fixed-point format above.
// If we only use bulkDigits = 1, that leaves only 128 - 30 =
// 98 bit accuracy for our scaling step, which isn't enough
// (experiments suggest that binary64 needs ~110 bits for
// correctness). So we have to use a large bulkDigits value
// to make full use of the 128-bit scaling above, which forces
// us to have some form of logic to handle the case of too
// many digits. The alternatives are either to use >128 bit
// arithmetic, or to back up and repeat the original scaling
// with bulkDigits = 1.
let uHigh = u._high
let lHigh = (l &+ UInt128(UInt64.max))._high
let tHigh: UInt64
if d.significand == 0 {
tHigh = (uHigh &+ lHigh &* 2) / 3
} else {
tHigh = (uHigh &+ lHigh) / 2
}
var u0 = UInt32(truncatingIfNeeded: uHigh >> (64 - integerBits))
var l0 = UInt32(truncatingIfNeeded: lHigh >> (64 - integerBits))
if lHigh & ((1 << (64 - integerBits)) - 1) != 0 {
l0 &+= 1
}
var t0 = UInt32(truncatingIfNeeded: tHigh >> (64 - integerBits))
var t0digits = 8
var u1 = u0 / 10
var l1 = (l0 &+ 9) / 10
var trailingZeros = (t == 0)
var droppedDigit = UInt32(
truncatingIfNeeded: ((tHigh &* 10) >> (64 - integerBits)) % 10)
while u1 >= l1 && u1 != 0 {
u0 = u1
l0 = l1
trailingZeros = trailingZeros && (droppedDigit == 0)
droppedDigit = t0 % 10
t0 /= 10
t0digits -= 1
u1 = u0 / 10
l1 = (l0 &+ 9) / 10
}
// Correct the final digit
if droppedDigit > 5 || (droppedDigit == 5 && !trailingZeros) { // > 0.5000
t0 &+= 1
} else if droppedDigit == 5 && trailingZeros { // == 0.5000
t0 &+= 1
t0 &= ~1
}
// t0 has t0digits digits. Write them out
let text = _intToEightDigits(t0)
buffer.storeBytes(
of: text,
toByteOffset: nextDigit,
as: UInt64.self)
nextDigit &+= 8
// Skip the leading zeros
firstDigit &+= 9 - t0digits
} else {
// Our initial scaling did not produce too many digits. The
// `d12345678` value holds the first 7 digits (plus a leading
// zero). The remainder of this algorithm is basically just a
// heavily-optimized variation of Grisu2.
// Write out exactly 8 digits, assuming little-endian.
let chars = _intToEightDigits(d12345678)
unsafe buffer.storeBytes(
of: chars,
toUncheckedByteOffset: nextDigit,
as: UInt64.self)
nextDigit &+= 8
firstDigit &+= 1
// >90% of random binary64 values need at least 15 digits.
// We have seven so there's probably at least 8 more, which
// we can grab all at once.
let TenToTheEighth = 100000000 as UInt128 // 10^(15-bulkFirstDigits)
let d0 = delta * TenToTheEighth
var t0 = t * TenToTheEighth
// The integer part of t0 is the next 8 digits
let next8Digits = UInt32(truncatingIfNeeded: t0._high >> 32)
t0 &= fractionMask
if d0 < t0 {
// We got 8 more digits! (So number is at least 15 digits)
// Write them out:
let chars = _intToEightDigits(next8Digits)
unsafe buffer.storeBytes(
of: chars,
toUncheckedByteOffset: nextDigit,
as: UInt64.self)
nextDigit &+= 8
t = t0
delta = d0
}
// Generate remaining digits one at a time, following Grisu:
while (delta < t) {
delta &*= 10
t &*= 10
unsafe buffer.storeBytes(
of: UInt8(truncatingIfNeeded: t._high >> 32) &+ 0x30,
toUncheckedByteOffset: nextDigit,
as: UInt8.self)
nextDigit &+= 1
t &= fractionMask
}
// Adjust the final digit to be closer to the original value.
// This accounts for the fact that sometimes there is more than
// one shortest digit sequence.
// For example, consider how the above would work if you had the
// value 0.1234 and computed u = 0.1257, l = 0.1211. The above
// digit generation works with `u`, so produces 0.125. But the
// values 0.122, 0.123, and 0.124 are just as short and 0.123 is
// therefore the best choice, since it's closest to the original
// value.
// We know delta and t are both less than 10.0 here, so we can
// shed some excess integer bits to simplify the following:
let adjustIntegerBits = 4 // Integer bits for "adjust" phase
let deltaHigh64 = UInt64(
truncatingIfNeeded: delta >> (64 - integerBits + adjustIntegerBits))
let tHigh64 = UInt64(
truncatingIfNeeded: t >> (64 - integerBits + adjustIntegerBits))
let one = UInt64(1) << (64 - adjustIntegerBits)
let adjustFractionMask = one - 1
let oneHalf = one >> 1
if deltaHigh64 >= tHigh64 &+ one {
// The `skew` is the difference between our
// computed digits and the original exact value.
var skew: UInt64
if (d.significandBitPattern == 0) {
skew = deltaHigh64 &- deltaHigh64 / 3 &- tHigh64
} else {
skew = deltaHigh64 / 2 &- tHigh64
}
var lastDigit = unsafe buffer.unsafeLoad(
fromUncheckedByteOffset: nextDigit - 1,
as: UInt8.self)
// We use the `skew` to figure out whether there's
// a better base-10 value than our current one.
if (skew & adjustFractionMask) == oneHalf {
// Difference is an integer + exactly 1/2, so ...
let adjust = skew >> (64 - adjustIntegerBits)
lastDigit &-= UInt8(truncatingIfNeeded: adjust)
// ... we round the last digit even.
lastDigit &= ~1
} else {
let adjust = (skew + oneHalf) >> (64 - adjustIntegerBits)
lastDigit &-= UInt8(truncatingIfNeeded: adjust)
}
buffer.storeBytes(
of: lastDigit,
toByteOffset: nextDigit - 1,
as: UInt8.self)
}
}
// Step 8: Finalize formatting by rearranging
// the digits and filling in decimal points,
// exponents, and zero padding.
let isBoundary = (d.significandBitPattern == 0)
let forceExponential =
((binaryExponent > 54) || (binaryExponent == 54 && !isBoundary))
return _finishFormatting(
buffer: &buffer,
sign: d.sign,
firstDigit: firstDigit,
nextDigit: nextDigit,
forceExponential: forceExponential,
base10Exponent: base10Exponent)
}
// ================================================================
//
// Float80
//
// ================================================================
// Float80 is only available on Intel x86/x86_64 processors on certain operating systems
// This matches the condition for the Float80 type
#if !(os(Windows) || os(Android) || ($Embedded && !os(Linux) && !(os(macOS) || os(iOS) || os(watchOS) || os(tvOS)))) && (arch(i386) || arch(x86_64))
// Support Legacy ABI on top of new implementation
@_silgen_name("swift_float80ToString")
internal func _float80ToStringImpl(
_ textBuffer: UnsafeMutablePointer<UTF8.CodeUnit>,
_ bufferLength: UInt,
_ value: Float80,
_ debug: Bool
) -> UInt64 {
if #available(SwiftStdlib 6.2, *) {
// Code below works with raw memory.
var buffer = unsafe MutableSpan<UTF8.CodeUnit>(
_unchecked: textBuffer,
count: Int(bufferLength))
let textRange = _Float80ToASCII(value: value, buffer: &buffer)
let textLength = textRange.upperBound - textRange.lowerBound
// Move the text to the start of the buffer
if textRange.lowerBound != 0 {
unsafe _memmove(
dest: textBuffer,
src: textBuffer + textRange.lowerBound,
size: UInt(truncatingIfNeeded: textLength))
}
return UInt64(truncatingIfNeeded: textLength)
} else {
fatalError()
}
}
// Convert a Float80 to an optimal ASCII representation.
// See notes above for comments on the output format here.
// See _Float64ToASCII for comments on the algorithm.
// Inputs:
// * `value`: Float80 input
// * `buffer`: Buffer to place the result
// Returns: Range of bytes within `buffer` that contain the result
//
// Buffer must be at least 32 bytes long and must be pre-filled
// with "0" characters, e.g., via
// `InlineArray<32,UTF8.CodeUnit>(repeating:0x30)`
@available(SwiftStdlib 6.2, *)
internal func _Float80ToASCII(
value f: Float80,
buffer utf8Buffer: inout MutableSpan<UTF8.CodeUnit>
) -> Range<Int> {
// We need a MutableRawSpan in order to use wide store/load operations
precondition(utf8Buffer.count >= 32)
var buffer = utf8Buffer.mutableBytes
// Step 1: Handle special cases, decompose the input
// The Intel 80-bit floating point format has some quirks that
// make this a lot more complex than the corresponding logic for
// the IEEE 754 portable formats.
// f.significandBitPattern is processed to try to mimic the
// semantics of IEEE portable formats. But for the following,
// we need the actual raw bits:
let rawSignificand = f._representation.explicitSignificand
let binaryExponent: Int
let significand: Float80.RawSignificand
let exponentBias = (1 << (Float80.exponentBitCount - 1)) - 2 // 16382
let isBoundary = f.significandBitPattern == 0
if f.exponentBitPattern == 0x7fff { // NaN or Infinity
// 80387 semantics and 80287 semantics differ somewhat;
// we follow 80387 semantics here.
// See: Wikipedia.org "Extended Precision"
// See: Intel's "Floating Point Reference Sheet"
// https://software.intel.com/content/dam/develop/external/us/en/documents/floating-point-reference-sheet.pdf
let selector = rawSignificand >> 62
let payload = rawSignificand & ((1 << 62) - 1)
switch selector {
case 0: // or snan on 287, invalid on 387
fallthrough
case 1: // Pseudo-NaN: snan on 287, invalid on 387
// Invalid patterns treated as plain "nan"
return nan_details(
buffer: &buffer,
sign: .plus,
quiet: true,
payloadHigh: 0,
payloadLow: payload)
case 2:
if payload == 0 { // snan on 287, on 387
return _infinity(buffer: &buffer, sign: f.sign)
} else { // snan on 287 and 387
return nan_details(
buffer: &buffer,
sign: f.sign,
quiet: false,
payloadHigh: 0,
payloadLow: payload)
}
case 3:
// Zero payload and sign bit set is "indefinite" (treated as qNaN here),
// otherwise qNaN on 387, sNaN on 287
return nan_details(
buffer: &buffer,
sign: f.sign,
quiet: true,
payloadHigh: 0,
payloadLow: payload)
default:
fatalError()
}
} else if f.exponentBitPattern == 0 {
if rawSignificand == 0 { // Zero
return _zero(buffer: &buffer, sign: f.sign)
} else { // subnormal
binaryExponent = 1 - exponentBias
significand = rawSignificand
}
} else if rawSignificand >> 63 == 1 { // Normal
binaryExponent = Int(bitPattern:f.exponentBitPattern) - exponentBias
significand = rawSignificand
} else {
return nan_details(
buffer: &buffer,
sign: .plus,
quiet: true,
payloadHigh: 0,
payloadLow: 0)
}
// Step 2: Determine the exact unscaled target interval
let halfUlp = UInt64(1) << 63
let quarterUlp = halfUlp >> 1
let threeQuarterUlp = halfUlp + quarterUlp
// Significand is the upper 64 bits of our 128-bit franction
// Upper midpoint adds 1/2 ULP:
let upperMidpointExact = UInt128(_low: halfUlp, _high: significand)
// Lower midpoint subtracts 1 ULP and then adds 1/2 or 3/4 ULP:
let lowerMidpointExact = UInt128(
_low: isBoundary ? threeQuarterUlp : halfUlp,
_high: significand - 1)
let forceExponential =
(binaryExponent > 65
|| (binaryExponent == 65 && !isBoundary))
return _backend_256bit(
buffer: &buffer,
upperMidpointExact: upperMidpointExact,
lowerMidpointExact: lowerMidpointExact,
sign: f.sign,
isBoundary: isBoundary,
isOddSignificand: (f.significandBitPattern & 1) != 0,
binaryExponent: binaryExponent,
forceExponential: forceExponential)
}
#endif
// ================================================================
//
// Float128
//
// ================================================================
#if false
// Note: We don't need _float128ToStringImpl, since that's only for
// backwards compatibility, and the legacy ABI never supported
// Float128.
@available(SwiftStdlib 6.2, *)
internal func _Float128ToASCII(
value d: Float128,
buffer utf8Buffer: inout MutableSpan<UTF8.CodeUnit>
) -> Range<Int> {
// TODO: Write Me!
// Note: All the interesting parts are already implemented in _backend_256bit(...),
// so this can easily be implemented someday by just copyihng _Float80ToASCII
// and making the obvious changes. (See the introductory parts of
// _Float64ToASCII for the structure common to all IEEE 754 formats.)
}
#endif
// ================================================================
//
// Float80/Float128 common backend
//
// This uses 256-bit fixed-width arithmetic to efficiently compute the
// optimal form for a decomposed float80 or binary128 value. It is
// less heavily commented than the 128-bit Double implementation
// above; see that implementation for detailed explanation of the
// logic here.
//
// Float80 could be handled more efficiently with 192-bit fixed-width
// arithmetic. But the code size savings from sharing this logic
// between float80 and binary128 are substantial, and the resulting
// float80 performance is still much better than competing
// implementations.
//
// Also in the interest of code size savings, this eschews some of the
// optimizations used by the 128-bit Double implementation above.
// Those optimizations are simple to reintroduce if you're interested
// in further performance improvements.
//
// If you are interested in extreme code size, you can also use this
// backend for binary32 and binary64, eliminating the separate 128-bit
// implementation. That variation offers surprisingly reasonable
// performance overall.
//
// ================================================================
#if !(os(Windows) || os(Android) || ($Embedded && !os(Linux) && !(os(macOS) || os(iOS) || os(watchOS) || os(tvOS)))) && (arch(i386) || arch(x86_64))
@available(SwiftStdlib 6.2, *)
fileprivate func _backend_256bit(
buffer: inout MutableRawSpan,
upperMidpointExact: UInt128,
lowerMidpointExact: UInt128,
sign: FloatingPointSign,
isBoundary: Bool,
isOddSignificand: Bool,
binaryExponent: Int,
forceExponential: Bool
) -> Range<Int> {
// Step 3: Estimate the base 10 exponent
var base10Exponent = decimalExponentFor2ToThe(binaryExponent)
// Step 4: Compute a power-of-10 scale factor
var powerOfTenRoundedDown = _UInt256()
var powerOfTenRoundedUp = _UInt256()
let powerOfTenExponent = _intervalContainingPowerOf10_Binary128(
p: -base10Exponent,
lower: &powerOfTenRoundedDown,
upper: &powerOfTenRoundedUp)
let extraBits = binaryExponent &+ powerOfTenExponent
// Step 5: Scale the interval (with rounding)
let integerBits = 14
let high64FractionBits = 64 - integerBits
var u: _UInt256
var l: _UInt256
if isOddSignificand {
// Narrow the interval (odd significand)
u = powerOfTenRoundedDown
u.multiplyRoundingDown(by: upperMidpointExact)
u.shiftRightRoundingDown(by: integerBits &- extraBits)
l = powerOfTenRoundedUp
l.multiplyRoundingUp(by: lowerMidpointExact)
l.shiftRightRoundingUp(by: integerBits &- extraBits)
} else {
// Widen the interval (even significand)
u = powerOfTenRoundedUp
u.multiplyRoundingUp(by: upperMidpointExact)
u.shiftRightRoundingUp(by: integerBits &- extraBits)
l = powerOfTenRoundedDown
l.multiplyRoundingDown(by: lowerMidpointExact)
l.shiftRightRoundingDown(by: integerBits &- extraBits)
}
// Step 6: Align first digit, adjust exponent
while u.high._high < (UInt64(1) << high64FractionBits) {
base10Exponent &-= 1
l.multiply(by: UInt32(10))
u.multiply(by: UInt32(10))
}
var t = u
var delta = u &- l
// Step 7: Generate digits
// Leave 8 bytes at the beginning for finishFormatting to use
let firstDigit = 8
var nextDigit = firstDigit
buffer.storeBytes(
of: 0x30 + UInt8(truncatingIfNeeded: t.extractIntegerPart(integerBits)),
toByteOffset: nextDigit,
as: UInt8.self)
nextDigit &+= 1
// It would be nice to generate 8 digits at a time and take
// advantage of intToEightDigits, but our integer portion has only
// 14 bits. We can't make that bigger without either sacrificing
// too much precision for correct Float128 or folding the first
// digits into the scaling (as we do with Double) which would
// require a back-out phase here (as we do with Double).
// If there is at least one more digit possible...
if delta < t {
// Try grabbing four digits at a time
var d0 = delta
var t0 = t
d0.multiply(by: 10000)
t0.multiply(by: 10000)
var d1234 = t0.extractIntegerPart(integerBits)
while d0 < t0 {
let d12 = d1234 / 100
let d34 = d1234 % 100
unsafe buffer.storeBytes(
of: asciiDigitTable[Int(bitPattern:d12)],
toUncheckedByteOffset: nextDigit,
as: UInt16.self)
unsafe buffer.storeBytes(
of: asciiDigitTable[Int(bitPattern:d34)],
toUncheckedByteOffset: nextDigit &+ 2,
as: UInt16.self)
nextDigit &+= 4
t = t0
delta = d0
d0.multiply(by: 10000)
t0.multiply(by: 10000)
d1234 = t0.extractIntegerPart(integerBits)
}
// Finish by generating one digit at a time...
while delta < t {
delta.multiply(by: UInt32(10))
t.multiply(by: UInt32(10))
let digit = UInt8(truncatingIfNeeded: t.extractIntegerPart(integerBits))
unsafe buffer.storeBytes(
of: 0x30 &+ digit,
toUncheckedByteOffset: nextDigit,
as: UInt8.self)
nextDigit &+= 1
}
}
// Adjust the final digit to be closer to the original value
// We've already consumed most of our available precision, and only
// need a couple of integer bits, so we can narrow down to
// 64 bits here.
let deltaHigh64 = delta.high._high
let tHigh64 = t.high._high
if deltaHigh64 >= tHigh64 &+ (UInt64(1) << high64FractionBits) {
let skew: UInt64
if isBoundary {
skew = deltaHigh64 &- deltaHigh64 / 3 &- tHigh64
} else {
skew = deltaHigh64 / 2 &- tHigh64
}
let one = UInt64(1) << high64FractionBits
let fractionMask = one - 1
let oneHalf = one >> 1
var lastDigit = unsafe buffer.unsafeLoad(
fromUncheckedByteOffset: nextDigit &- 1,
as: UInt8.self)
if (skew & fractionMask) == oneHalf {
let adjust = skew >> high64FractionBits
lastDigit &-= UInt8(truncatingIfNeeded: adjust)
lastDigit &= ~1
} else {
let adjust = (skew + oneHalf) >> high64FractionBits
lastDigit &-= UInt8(truncatingIfNeeded: adjust)
}
buffer.storeBytes(
of: lastDigit,
toByteOffset: nextDigit &- 1,
as: UInt8.self)
}
return _finishFormatting(
buffer: &buffer,
sign: sign,
firstDigit: firstDigit,
nextDigit: nextDigit,
forceExponential: forceExponential,
base10Exponent: base10Exponent)
}
#endif
// ================================================================
//
// Common Helper functions
//
// ================================================================
// Code above computes the appropriate significant digits and stores
// them in `buffer` between `firstDigit` and `nextDigit`.
// `finishFormatting` converts this into the final text form,
// inserting decimal points, minus signs, exponents, etc, as
// necessary. To minimize the work here, this assumes that there are
// at least 5 unused bytes at the beginning of `buffer` before
// `firstDigit` and that all unused bytes are filled with `"0"` (0x30)
// characters.
@available(SwiftStdlib 6.2, *)
fileprivate func _finishFormatting(
buffer: inout MutableRawSpan,
sign: FloatingPointSign,
firstDigit: Int,
nextDigit: Int,
forceExponential: Bool,
base10Exponent: Int
) -> Range<Int> {
// Performance note: This could be made noticeably faster by
// writing the output consistently in exponential form with no
// decimal point, e.g., "31415926e-07". But the extra cost seems
// worthwhile to achieve "3.1415926" instead.
var firstDigit = firstDigit
var nextDigit = nextDigit
let digitCount = nextDigit &- firstDigit
if base10Exponent < -4 || forceExponential {
// Exponential form: "-1.23456789e+123"
// Rewrite "123456789" => "1.23456789" by moving the first
// digit to the left one byte and overwriting a period.
// (This is one reason we left empty space to the left of the digits.)
// We don't do this for single-digit significands: "1e+78", "5e-324"
if digitCount > 1 {
let t = unsafe buffer.unsafeLoad(
fromUncheckedByteOffset: firstDigit,
as: UInt8.self)
unsafe buffer.storeBytes(
of: 0x2e,
toUncheckedByteOffset: firstDigit,
as: UInt8.self)
firstDigit &-= 1
unsafe buffer.storeBytes(
of: t,
toUncheckedByteOffset: firstDigit,
as: UInt8.self)
}
// Append the exponent:
unsafe buffer.storeBytes(
of: 0x65, // "e"
toUncheckedByteOffset: nextDigit,
as: UInt8.self)
nextDigit &+= 1
var e = base10Exponent
let expSign: UInt8
if base10Exponent < 0 {
expSign = 0x2d // "-"
e = 0 &- e
} else {
expSign = 0x2b // "+"
}
unsafe buffer.storeBytes(
of: expSign,
toUncheckedByteOffset: nextDigit,
as: UInt8.self)
nextDigit &+= 1
if e > 99 {
if e > 999 {
let d = asciiDigitTable[e / 100]
unsafe buffer.storeBytes(
of: d,
toUncheckedByteOffset: nextDigit,
as: UInt16.self)
nextDigit &+= 2
} else {
let d = 0x30 &+ UInt8(truncatingIfNeeded: (e / 100))
unsafe buffer.storeBytes(
of: d,
toUncheckedByteOffset: nextDigit,
as: UInt8.self)
nextDigit &+= 1
}
e = e % 100
}
let d = unsafe asciiDigitTable[unchecked: e]
buffer.storeBytes(
of: d,
toByteOffset: nextDigit,
as: UInt16.self)
nextDigit &+= 2
} else if base10Exponent < 0 {
// "-0.000123456789"
// We need up to 5 leading characters before the digits.
// Note that the formatters above all insert extra leading "0" characters
// to the beginning of the buffer, so we don't need to memset() here,
// just back up the start to include them...
firstDigit &+= base10Exponent - 1
// ... and then overwrite a decimal point to get "0." at the beginning
buffer.storeBytes(
of: 0x2e, // "."
toByteOffset: firstDigit &+ 1,
as: UInt8.self)
} else if base10Exponent &+ 1 < digitCount {
// "123456.789"
// We move the first digits forward one position
// so we can insert a decimal point in the middle.
// Note: This is the only case where we actually move
// more than one digit around in the buffer.
// TODO: Find out how to use C memmove() here
firstDigit &-= 1
for i in 0...(base10Exponent &+ 1) {
let t = unsafe buffer.unsafeLoad(
fromUncheckedByteOffset: firstDigit &+ i &+ 1,
as: UInt8.self)
unsafe buffer.storeBytes(
of: t,
toUncheckedByteOffset: firstDigit &+ i,
as: UInt8.self)
}
buffer.storeBytes(
of: 0x2e,
toByteOffset: firstDigit &+ base10Exponent &+ 1,
as: UInt8.self)
} else {
// "12345678900.0"
// Fill trailing zeros, put ".0" at the end
// so the result is obviously floating-point.
// Remember buffer was initialized with "0"
nextDigit = firstDigit &+ base10Exponent &+ 3
buffer.storeBytes(
of: 0x2e,
toByteOffset: nextDigit &- 2,
as: UInt8.self)
}
if sign == .minus {
buffer.storeBytes(
of: 0x2d, // "-"
toByteOffset: firstDigit &- 1,
as: UInt8.self)
firstDigit &-= 1
}
return unsafe Range(_uncheckedBounds: (lower: firstDigit, upper: nextDigit))
}
// Table with ASCII strings for all 2-digit decimal numbers.
// Stored as little-endian UInt16s for efficiency
@available(SwiftStdlib 6.2, *)
fileprivate let asciiDigitTable: InlineArray<100, UInt16> = [
0x3030, 0x3130, 0x3230, 0x3330, 0x3430,
0x3530, 0x3630, 0x3730, 0x3830, 0x3930,
0x3031, 0x3131, 0x3231, 0x3331, 0x3431,
0x3531, 0x3631, 0x3731, 0x3831, 0x3931,
0x3032, 0x3132, 0x3232, 0x3332, 0x3432,
0x3532, 0x3632, 0x3732, 0x3832, 0x3932,
0x3033, 0x3133, 0x3233, 0x3333, 0x3433,
0x3533, 0x3633, 0x3733, 0x3833, 0x3933,
0x3034, 0x3134, 0x3234, 0x3334, 0x3434,
0x3534, 0x3634, 0x3734, 0x3834, 0x3934,
0x3035, 0x3135, 0x3235, 0x3335, 0x3435,
0x3535, 0x3635, 0x3735, 0x3835, 0x3935,
0x3036, 0x3136, 0x3236, 0x3336, 0x3436,
0x3536, 0x3636, 0x3736, 0x3836, 0x3936,
0x3037, 0x3137, 0x3237, 0x3337, 0x3437,
0x3537, 0x3637, 0x3737, 0x3837, 0x3937,
0x3038, 0x3138, 0x3238, 0x3338, 0x3438,
0x3538, 0x3638, 0x3738, 0x3838, 0x3938,
0x3039, 0x3139, 0x3239, 0x3339, 0x3439,
0x3539, 0x3639, 0x3739, 0x3839, 0x3939
]
// The constants below assume we're on a little-endian processor
@available(SwiftStdlib 6.2, *)
fileprivate func _infinity(
buffer: inout MutableRawSpan,
sign: FloatingPointSign
) -> Range<Int> {
if sign == .minus {
buffer.storeBytes(
of: 0x666e692d, // "-inf"
toByteOffset: 0,
as: UInt32.self)
return 0..<4
} else {
buffer.storeBytes(
of: 0x00666e69, // "inf\0"
toByteOffset: 0,
as: UInt32.self)
return 0..<3
}
}
@available(SwiftStdlib 6.2, *)
fileprivate func _zero(
buffer: inout MutableRawSpan,
sign: FloatingPointSign
) -> Range<Int> {
if sign == .minus {
buffer.storeBytes(
of: 0x302e302d, // "-0.0"
toByteOffset: 0,
as: UInt32.self)
return 0..<4
} else {
buffer.storeBytes(
of: 0x00302e30, // "0.0\0"
toByteOffset: 0,
as: UInt32.self)
return 0..<3
}
}
@available(SwiftStdlib 6.2, *)
fileprivate let hexdigits: InlineArray<16, UInt8> = [
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66
]
@available(SwiftStdlib 6.2, *)
fileprivate func _hexWithoutLeadingZeros(
buffer: inout MutableRawSpan,
offset: inout Int,
value: UInt64
) {
var shift = 60
while (shift > 0) && ((value >> shift) & 0xf == 0) {
shift -= 4
}
while shift >= 0 {
let d = hexdigits[Int(truncatingIfNeeded: (value >> shift) & 0xf)]
shift -= 4
buffer.storeBytes(
of: d,
toByteOffset: offset,
as: UInt8.self)
offset += 1
}
}
@available(SwiftStdlib 6.2, *)
fileprivate func _hexWithLeadingZeros(
buffer: inout MutableRawSpan,
offset: inout Int,
value: UInt64
) {
var shift = 60
while shift >= 0 {
let d = hexdigits[Int(truncatingIfNeeded: (value >> shift) & 0xf)]
shift -= 4
buffer.storeBytes(
of: d,
toByteOffset: offset,
as: UInt8.self)
offset += 1
}
}
@available(SwiftStdlib 6.2, *)
fileprivate func nan_details(
buffer: inout MutableRawSpan,
sign: FloatingPointSign,
quiet: Bool,
payloadHigh: UInt64,
payloadLow: UInt64
) -> Range<Int> {
// value is a NaN of some sort
var i = 0
if sign == .minus {
buffer.storeBytes(
of: 0x2d, // "-"
toByteOffset: 0,
as: UInt8.self)
i = 1
}
if !quiet {
buffer.storeBytes(
of: 0x73, // "s"
toByteOffset: i,
as: UInt8.self)
i += 1
}
buffer.storeBytes(of: 0x6e, toByteOffset: i, as: UInt8.self) // "n"
buffer.storeBytes(of: 0x61, toByteOffset: i + 1, as: UInt8.self) // "a"
buffer.storeBytes(of: 0x6e, toByteOffset: i + 2, as: UInt8.self) // "n"
i += 3
if payloadHigh != 0 || payloadLow != 0 {
buffer.storeBytes(of: 0x28, toByteOffset: i, as: UInt8.self) // "("
i += 1
buffer.storeBytes(of: 0x30, toByteOffset: i, as: UInt8.self) // "0"
i += 1
buffer.storeBytes(of: 0x78, toByteOffset: i, as: UInt8.self) // "x"
i += 1
if payloadHigh == 0 {
_hexWithoutLeadingZeros(buffer: &buffer, offset: &i, value: payloadLow)
} else {
_hexWithoutLeadingZeros(buffer: &buffer, offset: &i, value: payloadHigh)
_hexWithLeadingZeros(buffer: &buffer, offset: &i, value: payloadLow)
}
buffer.storeBytes(of: 0x29, toByteOffset: i, as: UInt8.self) // ")"
i += 1
}
return 0..<i
}
// Convert an integer less than 10^8 into exactly 8 ASCII digits in a
// UInt64. Assuming little-endian, the resulting UInt64 can be stored
// directly to memory.
//
// This implementation is based on work by Paul Khuong:
// https://pvk.ca/Blog/2017/12/22/appnexus-common-framework-its-out-also-how-to-print-integers-faster/
@available(SwiftStdlib 6.2, *)
@inline(__always)
fileprivate func _intToEightDigits(_ n: UInt32) -> UInt64 {
// Break into two numbers of 4 decimal digits each
let div8 = n / 10000
let mod8 = n &- div8 &* 10000
let fours = UInt64(div8) | (UInt64(mod8) << 32)
// Break into 4 numbers of 2 decimal digits each
let mask100: UInt64 = 0x0000007f0000007f
let div4 = ((fours &* 10486) >> 20) & mask100
let mod4 = fours &- 100 &* div4
let pairs = div4 | (mod4 &<< 16)
// Break into 8 numbers of a single decimal digit each
let mask10: UInt64 = 0x000f000f000f000f
let div2 = ((pairs &* 103) >> 10) & mask10
let mod2 = pairs &- 10 &* div2
let singles = div2 | (mod2 &<< 8)
// Convert 8 digits to ASCII characters
return singles &+ 0x3030303030303030
}
// ================================================================
//
// Arithmetic Helpers
//
// The code above works with fixed-point values. Standard
// addition/subtraction/comparison works fine, but we need rounding
// control when multiplying such values.
//
// For exmaple, `multiply128x64RoundingDown` multiplies a 0.128
// fixed-point value by a 0.64 fixed-point fraction, returning a 0.128
// value that's been rounded down from the exact 192-bit result.
//
// ================================================================
@inline(__always)
fileprivate func _multiply64x32RoundingDown(
_ lhs: UInt64,
_ rhs: UInt32
) -> UInt64 {
let mask32 = UInt64(UInt32.max)
let t = ((lhs & mask32) * UInt64(rhs)) >> 32
return t + (lhs >> 32) * UInt64(rhs)
}
@inline(__always)
fileprivate func _multiply64x32RoundingUp(
_ lhs: UInt64,
_ rhs: UInt32
) -> UInt64 {
let mask32 = UInt64(UInt32.max)
let t = (((lhs & mask32) * UInt64(rhs)) + mask32) >> 32
return t + (lhs >> 32) * UInt64(rhs)
}
@available(SwiftStdlib 6.2, *)
@inline(__always)
fileprivate func _multiply128x64RoundingDown(
_ lhs: UInt128,
_ rhs: UInt64
) -> UInt128 {
let lhsHigh = UInt128(truncatingIfNeeded: lhs._high)
let lhsLow = UInt128(truncatingIfNeeded: lhs._low)
let rhs128 = UInt128(truncatingIfNeeded: rhs)
return (lhsHigh &* rhs128) &+ ((lhsLow &* rhs128) >> 64)
}
@available(SwiftStdlib 6.2, *)
@inline(__always)
fileprivate func _multiply128x64RoundingUp(
_ lhs: UInt128,
_ rhs: UInt64
) -> UInt128 {
let lhsHigh = UInt128(truncatingIfNeeded: lhs._high)
let lhsLow = UInt128(truncatingIfNeeded: lhs._low)
let rhs128 = UInt128(truncatingIfNeeded: rhs)
let h = lhsHigh &* rhs128
let l = lhsLow &* rhs128
let bias = (UInt128(1) << 64) &- 1
return h + ((l &+ bias) &>> 64)
}
// Custom 256-bit unsigned integer type, with various arithmetic
// helpers as methods.
// Used by 80- and 128-bit floating point formatting logic above...
@available(SwiftStdlib 6.2, *)
fileprivate struct _UInt256 {
var high: UInt128
var low: UInt128
init() {
self.high = 0
self.low = 0
}
init(high: UInt64, _ midHigh: UInt64, _ midLow: UInt64, low: UInt64) {
self.high = UInt128(_low: midHigh, _high: high)
self.low = UInt128(_low: low, _high: midLow)
}
init(high: UInt128, low: UInt128) {
self.high = high
self.low = low
}
mutating func shiftRightRoundingDown(by shift: Int) {
assert(shift < 32 && shift >= 0)
var t = UInt128(low._low >> shift)
t |= UInt128(low._high) &<< (64 - shift)
let newlow = t._low
t = UInt128(t._high)
t |= UInt128(high._low) &<< (64 - shift)
low = UInt128(_low: newlow, _high: t._low)
t = UInt128(t._high)
t |= UInt128(high._high) &<< (64 - shift)
high = t
}
mutating func shiftRightRoundingUp(by shift: Int) {
assert(shift < 32 && shift >= 0)
let bias = (UInt64(1) &<< shift) - 1
var t = UInt128((low._low + bias) >> shift)
t |= UInt128(low._high) &<< (64 - shift)
let newlow = t._low
t = UInt128(t._high)
t |= UInt128(high._low) &<< (64 - shift)
low = UInt128(_low: newlow, _high: t._low)
t = UInt128(t._high)
t |= UInt128(high._high) &<< (64 - shift)
high = t
}
mutating func multiply(by rhs: UInt32) {
var t = UInt128(low._low) &* UInt128(rhs)
let newlow = t._low
t = UInt128(t._high) &+ UInt128(low._high) &* UInt128(rhs)
low = UInt128(_low: newlow, _high: t._low)
t = UInt128(t._high) &+ UInt128(high._low) &* UInt128(rhs)
let newmidhigh = t._low
t = UInt128(t._high) &+ UInt128(high._high) &* UInt128(rhs)
high = UInt128(_low: newmidhigh, _high: t._low)
assert(t._high == 0)
}
mutating func multiplyRoundingDown(by rhs: UInt128) {
var current = UInt128(low._low) * UInt128(rhs._low)
current = UInt128(current._high)
var t = UInt128(low._low) &* UInt128(rhs._high)
current += UInt128(t._low)
var next = UInt128(t._high)
t = UInt128(low._high) &* UInt128(rhs._low)
current += UInt128(t._low)
next += UInt128(t._high)
current = next + UInt128(current._high)
t = UInt128(low._high) &* UInt128(rhs._high)
current += UInt128(t._low)
next = UInt128(t._high)
t = UInt128(high._low) &* UInt128(rhs._low)
current += UInt128(t._low)
next += UInt128(t._high)
let newlow = current._low
current = next + UInt128(current._high)
t = UInt128(high._low) &* UInt128(rhs._high)
current += UInt128(t._low)
next = UInt128(t._high)
t = UInt128(high._high) &* UInt128(rhs._low)
current += UInt128(t._low)
next += UInt128(t._high)
low = UInt128(_low: newlow, _high: current._low)
current = next + UInt128(current._high)
t = UInt128(high._high) &* UInt128(rhs._high)
high = current + t
}
mutating func multiplyRoundingUp(by rhs: UInt128) {
var current = UInt128(low._low) &* UInt128(rhs._low)
current += UInt128(UInt64.max)
current = UInt128(current._high)
var t = UInt128(low._low) &* UInt128(rhs._high)
current += UInt128(t._low)
var next = UInt128(t._high)
t = UInt128(low._high) &* UInt128(rhs._low)
current += UInt128(t._low)
next += UInt128(t._high)
current += UInt128(UInt64.max)
current = next + UInt128(current._high)
t = UInt128(low._high) &* UInt128(rhs._high)
current += UInt128(t._low)
next = UInt128(t._high)
t = UInt128(high._low) &* UInt128(rhs._low)
current += UInt128(t._low)
next += UInt128(t._high)
let newlow = current._low
current = next + UInt128(current._high)
t = UInt128(high._low) &* UInt128(rhs._high)
current += UInt128(t._low)
next = UInt128(t._high)
t = UInt128(high._high) &* UInt128(rhs._low)
current += UInt128(t._low)
next += UInt128(t._high)
low = UInt128(_low: newlow, _high: current._low)
current = next + UInt128(current._high)
t = UInt128(high._high) &* UInt128(rhs._high)
high = current + t
}
mutating func extractIntegerPart(_ bits: Int) -> UInt {
assert(bits < 16)
let integral = high._high >> (64 &- bits)
high = UInt128(
_low: high._low,
_high: high._high &- (integral &<< (64 &- bits)))
return UInt(truncatingIfNeeded: integral)
}
static func &- (lhs: _UInt256, rhs: _UInt256) -> _UInt256 {
var t = UInt128(lhs.low._low) &+ UInt128(~rhs.low._low) &+ 1
let newlowlow = t._low
t = UInt128(t._high) &+ UInt128(lhs.low._high) &+ UInt128(~rhs.low._high)
let newlow = UInt128(_low: newlowlow, _high: t._low)
t = UInt128(t._high) &+ UInt128(lhs.high._low) &+ UInt128(~rhs.high._low)
let newhigh = UInt128(
_low: t._low,
_high: t._high &+ lhs.high._high &+ ~rhs.high._high)
return _UInt256(high: newhigh, low: newlow)
}
static func < (lhs: _UInt256, rhs: _UInt256) -> Bool {
return (lhs.high < rhs.high)
|| (lhs.high == rhs.high
&& lhs.low < rhs.low)
}
}
// ================================================================
//
// Powers of 10
//
// ================================================================
@available(SwiftStdlib 6.2, *)
@inline(__always)
fileprivate func _intervalContainingPowerOf10_Binary32(
p: Int,
lower: inout UInt64,
upper: inout UInt64
) -> Int {
if p >= 0 {
let base = powersOf10_Exact128[p &* 2 &+ 1]
lower = base
if p < 28 {
upper = base
} else {
upper = base &+ 1
}
} else {
let base = powersOf10_negativeBinary32[p &+ 40]
lower = base
upper = base &+ 1
}
return binaryExponentFor10ToThe(p)
}
@available(SwiftStdlib 6.2, *)
@inline(__always)
fileprivate func _intervalContainingPowerOf10_Binary64(
p: Int,
lower: inout UInt128,
upper: inout UInt128
) -> Int {
if p >= 0 && p <= 55 {
let upper64 = powersOf10_Exact128[p &* 2 &+ 1]
let lower64 = powersOf10_Exact128[p &* 2]
upper = UInt128(_low: lower64, _high: upper64)
lower = upper
return binaryExponentFor10ToThe(p)
}
let index = p &+ 400
let mainPower = index / 28
let baseHigh = powersOf10_Binary64[mainPower &* 2 &+ 1]
let baseLow = powersOf10_Binary64[mainPower &* 2]
let extraPower = index &- mainPower &* 28
let baseExponent = binaryExponentFor10ToThe(p &- extraPower)
if extraPower == 0 {
lower = UInt128(_low: baseLow, _high: baseHigh)
upper = lower &+ 1
return baseExponent
} else {
let extra = powersOf10_Exact128[extraPower &* 2 &+ 1]
lower = ((UInt128(truncatingIfNeeded:baseHigh)
&* UInt128(truncatingIfNeeded:extra))
&+ ((UInt128(truncatingIfNeeded:baseLow)
&* UInt128(truncatingIfNeeded:extra)) &>> 64))
upper = lower &+ 2
return baseExponent &+ binaryExponentFor10ToThe(extraPower)
}
}
@inline(__always)
fileprivate func binaryExponentFor10ToThe(_ p: Int) -> Int {
return Int(((Int64(p) &* 55732705) >> 24) &+ 1)
}
@inline(__always)
fileprivate func decimalExponentFor2ToThe(_ p: Int) -> Int {
return Int((Int64(p) &* 20201781) >> 26)
}
// Each of the constant values here have an implicit binary point at
// the extreme left and when not exact, are rounded _down_ from the
// exact values. For example, the first row of the first table says
// that:
//
// 0x0.8b61313bbabce2c6 x 2^-132
//
// is the result of rounding down the exact binary value of 10^-40 to
// 64 significant bits. The logic above uses these tables to compute
// bounds for the exact value of the power of 10.
// Note the binary exponent is not stored; it is computed by the
// `binaryExponentFor10ToThe(p)` function.
// This covers the negative powers of 10 for Float32.
// Positive powers of 10 come from the next table below.
// Table size: 320 bytes
@available(SwiftStdlib 6.2, *)
fileprivate let powersOf10_negativeBinary32: InlineArray<_, UInt64> = [
0x8b61313bbabce2c6, // x 2^-132 ~= 10^-40
0xae397d8aa96c1b77, // x 2^-129 ~= 10^-39
0xd9c7dced53c72255, // x 2^-126 ~= 10^-38
0x881cea14545c7575, // x 2^-122 ~= 10^-37
0xaa242499697392d2, // x 2^-119 ~= 10^-36
0xd4ad2dbfc3d07787, // x 2^-116 ~= 10^-35
0x84ec3c97da624ab4, // x 2^-112 ~= 10^-34
0xa6274bbdd0fadd61, // x 2^-109 ~= 10^-33
0xcfb11ead453994ba, // x 2^-106 ~= 10^-32
0x81ceb32c4b43fcf4, // x 2^-102 ~= 10^-31
0xa2425ff75e14fc31, // x 2^-99 ~= 10^-30
0xcad2f7f5359a3b3e, // x 2^-96 ~= 10^-29
0xfd87b5f28300ca0d, // x 2^-93 ~= 10^-28
0x9e74d1b791e07e48, // x 2^-89 ~= 10^-27
0xc612062576589dda, // x 2^-86 ~= 10^-26
0xf79687aed3eec551, // x 2^-83 ~= 10^-25
0x9abe14cd44753b52, // x 2^-79 ~= 10^-24
0xc16d9a0095928a27, // x 2^-76 ~= 10^-23
0xf1c90080baf72cb1, // x 2^-73 ~= 10^-22
0x971da05074da7bee, // x 2^-69 ~= 10^-21
0xbce5086492111aea, // x 2^-66 ~= 10^-20
0xec1e4a7db69561a5, // x 2^-63 ~= 10^-19
0x9392ee8e921d5d07, // x 2^-59 ~= 10^-18
0xb877aa3236a4b449, // x 2^-56 ~= 10^-17
0xe69594bec44de15b, // x 2^-53 ~= 10^-16
0x901d7cf73ab0acd9, // x 2^-49 ~= 10^-15
0xb424dc35095cd80f, // x 2^-46 ~= 10^-14
0xe12e13424bb40e13, // x 2^-43 ~= 10^-13
0x8cbccc096f5088cb, // x 2^-39 ~= 10^-12
0xafebff0bcb24aafe, // x 2^-36 ~= 10^-11
0xdbe6fecebdedd5be, // x 2^-33 ~= 10^-10
0x89705f4136b4a597, // x 2^-29 ~= 10^-9
0xabcc77118461cefc, // x 2^-26 ~= 10^-8
0xd6bf94d5e57a42bc, // x 2^-23 ~= 10^-7
0x8637bd05af6c69b5, // x 2^-19 ~= 10^-6
0xa7c5ac471b478423, // x 2^-16 ~= 10^-5
0xd1b71758e219652b, // x 2^-13 ~= 10^-4
0x83126e978d4fdf3b, // x 2^-9 ~= 10^-3
0xa3d70a3d70a3d70a, // x 2^-6 ~= 10^-2
0xcccccccccccccccc, // x 2^-3 ~= 10^-1
]
// All the powers of 10 that can be represented exactly
// in 128 bits, represented as binary floating-point values
// using the same convention as in the previous table, only
// with 128 bit significands.
// This table is used in four places:
// * The high order 64 bits are used for positive powers of 10
// when converting Float32.
// * The full 128-bit value is used for 10^0 through 10^55 for Float64.
// * The first 28 entries are combined with the next table for
// all other Float64 values.
// * This is combined with the 256-bit table below for Float80/Float128
// support.
// Table size: 896 bytes
@available(SwiftStdlib 6.2, *)
fileprivate let powersOf10_Exact128: InlineArray<_, UInt64> = [
// Low order ... high order
0x0000000000000000, 0x8000000000000000, // x 2^1 == 10^0 exactly
0x0000000000000000, 0xa000000000000000, // x 2^4 == 10^1 exactly
0x0000000000000000, 0xc800000000000000, // x 2^7 == 10^2 exactly
0x0000000000000000, 0xfa00000000000000, // x 2^10 == 10^3 exactly
0x0000000000000000, 0x9c40000000000000, // x 2^14 == 10^4 exactly
0x0000000000000000, 0xc350000000000000, // x 2^17 == 10^5 exactly
0x0000000000000000, 0xf424000000000000, // x 2^20 == 10^6 exactly
0x0000000000000000, 0x9896800000000000, // x 2^24 == 10^7 exactly
0x0000000000000000, 0xbebc200000000000, // x 2^27 == 10^8 exactly
0x0000000000000000, 0xee6b280000000000, // x 2^30 == 10^9 exactly
0x0000000000000000, 0x9502f90000000000, // x 2^34 == 10^10 exactly
0x0000000000000000, 0xba43b74000000000, // x 2^37 == 10^11 exactly
0x0000000000000000, 0xe8d4a51000000000, // x 2^40 == 10^12 exactly
0x0000000000000000, 0x9184e72a00000000, // x 2^44 == 10^13 exactly
0x0000000000000000, 0xb5e620f480000000, // x 2^47 == 10^14 exactly
0x0000000000000000, 0xe35fa931a0000000, // x 2^50 == 10^15 exactly
0x0000000000000000, 0x8e1bc9bf04000000, // x 2^54 == 10^16 exactly
0x0000000000000000, 0xb1a2bc2ec5000000, // x 2^57 == 10^17 exactly
0x0000000000000000, 0xde0b6b3a76400000, // x 2^60 == 10^18 exactly
0x0000000000000000, 0x8ac7230489e80000, // x 2^64 == 10^19 exactly
0x0000000000000000, 0xad78ebc5ac620000, // x 2^67 == 10^20 exactly
0x0000000000000000, 0xd8d726b7177a8000, // x 2^70 == 10^21 exactly
0x0000000000000000, 0x878678326eac9000, // x 2^74 == 10^22 exactly
0x0000000000000000, 0xa968163f0a57b400, // x 2^77 == 10^23 exactly
0x0000000000000000, 0xd3c21bcecceda100, // x 2^80 == 10^24 exactly
0x0000000000000000, 0x84595161401484a0, // x 2^84 == 10^25 exactly
0x0000000000000000, 0xa56fa5b99019a5c8, // x 2^87 == 10^26 exactly
0x0000000000000000, 0xcecb8f27f4200f3a, // x 2^90 == 10^27 exactly
0x4000000000000000, 0x813f3978f8940984, // x 2^94 == 10^28 exactly
0x5000000000000000, 0xa18f07d736b90be5, // x 2^97 == 10^29 exactly
0xa400000000000000, 0xc9f2c9cd04674ede, // x 2^100 == 10^30 exactly
0x4d00000000000000, 0xfc6f7c4045812296, // x 2^103 == 10^31 exactly
0xf020000000000000, 0x9dc5ada82b70b59d, // x 2^107 == 10^32 exactly
0x6c28000000000000, 0xc5371912364ce305, // x 2^110 == 10^33 exactly
0xc732000000000000, 0xf684df56c3e01bc6, // x 2^113 == 10^34 exactly
0x3c7f400000000000, 0x9a130b963a6c115c, // x 2^117 == 10^35 exactly
0x4b9f100000000000, 0xc097ce7bc90715b3, // x 2^120 == 10^36 exactly
0x1e86d40000000000, 0xf0bdc21abb48db20, // x 2^123 == 10^37 exactly
0x1314448000000000, 0x96769950b50d88f4, // x 2^127 == 10^38 exactly
0x17d955a000000000, 0xbc143fa4e250eb31, // x 2^130 == 10^39 exactly
0x5dcfab0800000000, 0xeb194f8e1ae525fd, // x 2^133 == 10^40 exactly
0x5aa1cae500000000, 0x92efd1b8d0cf37be, // x 2^137 == 10^41 exactly
0xf14a3d9e40000000, 0xb7abc627050305ad, // x 2^140 == 10^42 exactly
0x6d9ccd05d0000000, 0xe596b7b0c643c719, // x 2^143 == 10^43 exactly
0xe4820023a2000000, 0x8f7e32ce7bea5c6f, // x 2^147 == 10^44 exactly
0xdda2802c8a800000, 0xb35dbf821ae4f38b, // x 2^150 == 10^45 exactly
0xd50b2037ad200000, 0xe0352f62a19e306e, // x 2^153 == 10^46 exactly
0x4526f422cc340000, 0x8c213d9da502de45, // x 2^157 == 10^47 exactly
0x9670b12b7f410000, 0xaf298d050e4395d6, // x 2^160 == 10^48 exactly
0x3c0cdd765f114000, 0xdaf3f04651d47b4c, // x 2^163 == 10^49 exactly
0xa5880a69fb6ac800, 0x88d8762bf324cd0f, // x 2^167 == 10^50 exactly
0x8eea0d047a457a00, 0xab0e93b6efee0053, // x 2^170 == 10^51 exactly
0x72a4904598d6d880, 0xd5d238a4abe98068, // x 2^173 == 10^52 exactly
0x47a6da2b7f864750, 0x85a36366eb71f041, // x 2^177 == 10^53 exactly
0x999090b65f67d924, 0xa70c3c40a64e6c51, // x 2^180 == 10^54 exactly
0xfff4b4e3f741cf6d, 0xd0cf4b50cfe20765, // x 2^183 == 10^55 exactly
]
// Every 28th power of 10 across the full range of Double.
// Combined with a 64-bit exact power of 10 from the previous
// table, this lets us reconstruct a 128-bit lower bound for
// any power of 10 across the full range of double with a single
// 64-bit by 128-bit multiplication.
// The published algorithms generally use a full table here of
// 800 128-bit values (6400 bytes). Breaking it into two tables
// gives a significant code-size savings for a modest performance
// penalty.
// Table size: 464 bytes
@available(SwiftStdlib 6.2, *)
fileprivate let powersOf10_Binary64: InlineArray<_, UInt64> = [
// low-order half, high-order half
0x3931b850df08e738, 0x95fe7e07c91efafa, // x 2^-1328 ~= 10^-400
0xba954f8e758fecb3, 0x9774919ef68662a3, // x 2^-1235 ~= 10^-372
0x9028bed2939a635c, 0x98ee4a22ecf3188b, // x 2^-1142 ~= 10^-344
0x47b233c92125366e, 0x9a6bb0aa55653b2d, // x 2^-1049 ~= 10^-316
0x4ee367f9430aec32, 0x9becce62836ac577, // x 2^-956 ~= 10^-288
0x6f773fc3603db4a9, 0x9d71ac8fada6c9b5, // x 2^-863 ~= 10^-260
0xc47bc5014a1a6daf, 0x9efa548d26e5a6e1, // x 2^-770 ~= 10^-232
0x80e8a40eccd228a4, 0xa086cfcd97bf97f3, // x 2^-677 ~= 10^-204
0xb8ada00e5a506a7c, 0xa21727db38cb002f, // x 2^-584 ~= 10^-176
0xc13e60d0d2e0ebba, 0xa3ab66580d5fdaf5, // x 2^-491 ~= 10^-148
0xc2974eb4ee658828, 0xa54394fe1eedb8fe, // x 2^-398 ~= 10^-120
0xcb4ccd500f6bb952, 0xa6dfbd9fb8e5b88e, // x 2^-305 ~= 10^-92
0x3f2398d747b36224, 0xa87fea27a539e9a5, // x 2^-212 ~= 10^-64
0xdde50bd1d5d0b9e9, 0xaa242499697392d2, // x 2^-119 ~= 10^-36
0xfdc20d2b36ba7c3d, 0xabcc77118461cefc, // x 2^-26 ~= 10^-8
0x0000000000000000, 0xad78ebc5ac620000, // x 2^67 == 10^20 exactly
0x9670b12b7f410000, 0xaf298d050e4395d6, // x 2^160 == 10^48 exactly
0x3b25a55f43294bcb, 0xb0de65388cc8ada8, // x 2^253 ~= 10^76
0x58edec91ec2cb657, 0xb2977ee300c50fe7, // x 2^346 ~= 10^104
0x29babe4598c311fb, 0xb454e4a179dd1877, // x 2^439 ~= 10^132
0x577b986b314d6009, 0xb616a12b7fe617aa, // x 2^532 ~= 10^160
0x0c11ed6d538aeb2f, 0xb7dcbf5354e9bece, // x 2^625 ~= 10^188
0x6d953e2bd7173692, 0xb9a74a0637ce2ee1, // x 2^718 ~= 10^216
0x9d6d1ad41abe37f1, 0xbb764c4ca7a4440f, // x 2^811 ~= 10^244
0x4b2d8644d8a74e18, 0xbd49d14aa79dbc82, // x 2^904 ~= 10^272
0xe0470a63e6bd56c3, 0xbf21e44003acdd2c, // x 2^997 ~= 10^300
0x505f522e53053ff2, 0xc0fe908895cf3b44, // x 2^1090 ~= 10^328
0xcca845ab2beafa9a, 0xc2dfe19c8c055535, // x 2^1183 ~= 10^356
0x1027fff56784f444, 0xc4c5e310aef8aa17, // x 2^1276 ~= 10^384
]
// Needed by 80- and 128-bit formatters above
// We could cut this in half by keeping only the positive powers and doing
// a single additional 256-bit multiplication by 10^-4984 to recover the negative powers.
// Table size: 5728 bytes
@available(SwiftStdlib 6.2, *)
fileprivate let powersOf10_Binary128: InlineArray<_, UInt64> = [
// Low-order ... high-order
0xaec2e6aff96b46ae, 0xf91044c2eff84750, 0x2b55c9e70e00c557, 0xb6536903bf8f2bda, // x 2^-16556 ~= 10^-4984
0xda1b3c3dd3889587, 0x73a7380aba84a6b1, 0xbddb2dfde3f8a6e3, 0xb9e5428330737362, // x 2^-16370 ~= 10^-4928
0xa2d23c57cfebb9ec, 0x9f165c039ead6d77, 0x88227fdfc13ab53d, 0xbd89006346a9a34d, // x 2^-16184 ~= 10^-4872
0x0333d510cf27e5a5, 0x4e3cc383eaa17b7b, 0xe05fe4207ca3d508, 0xc13efc51ade7df64, // x 2^-15998 ~= 10^-4816
0xff242c569bc1f539, 0x5c67ba58680c4cce, 0x3c55f3f947fef0e9, 0xc50791bd8dd72edb, // x 2^-15812 ~= 10^-4760
0xe4b75ae27bec50bf, 0x25b0419765fdfcdb, 0x0915564d8ab057ee, 0xc8e31de056f89c19, // x 2^-15626 ~= 10^-4704
0x548b1e80a94f3434, 0xe418e9217ce83755, 0x801e38463183fc88, 0xccd1ffc6bba63e21, // x 2^-15440 ~= 10^-4648
0x541950a0fdc2b4d9, 0xeea173da1f0eb7b4, 0xcfadf6b2aa7c4f43, 0xd0d49859d60d40a3, // x 2^-15254 ~= 10^-4592
0x7e64501be95ad76b, 0x451e855d8acef835, 0x9e601e707a2c3488, 0xd4eb4a687c0253e8, // x 2^-15068 ~= 10^-4536
0xdadd9645f360cb51, 0xf290163350ecb3eb, 0xa8edffdccfe4db4b, 0xd9167ab0c1965798, // x 2^-14882 ~= 10^-4480
0x7e447db3018ffbdf, 0x4fa1860c08a85923, 0xb17cd86e7fcece75, 0xdd568fe9ab559344, // x 2^-14696 ~= 10^-4424
0x61cd4655bf64d265, 0xb19fd88fe285b3bc, 0x1151250681d59705, 0xe1abf2cd11206610, // x 2^-14510 ~= 10^-4368
0xa5703f5ce7a619ec, 0x361243a84b55574d, 0x025a8e1e5dbb41d6, 0xe6170e21b2910457, // x 2^-14324 ~= 10^-4312
0xb93897a6cf5d3e61, 0x18746fcc6a190db9, 0x66e849253e5da0c2, 0xea984ec57de69f13, // x 2^-14138 ~= 10^-4256
0x309043d12ab5b0ac, 0x79c93cff11f09319, 0xf5a7800f23ef67b8, 0xef3023b80a732d93, // x 2^-13952 ~= 10^-4200
0xa3baa84c049b52b9, 0xbec466ee1b586342, 0x0e85fc7f4edbd3ca, 0xf3defe25478e074a, // x 2^-13766 ~= 10^-4144
0xd1f4628316b15c7a, 0xae16192410d3135e, 0x4268a54f70bd28c4, 0xf8a551706112897c, // x 2^-13580 ~= 10^-4088
0x9eb9296cc5749dba, 0x48324e275376dfdd, 0x5052e9289f0f2333, 0xfd83933eda772c0b, // x 2^-13394 ~= 10^-4032
0xff6aae669a5a0d8a, 0x24fed95087b9006e, 0x01b02378a405b421, 0x813d1dc1f0c754d6, // x 2^-13207 ~= 10^-3976
0xf993f18de00dc89b, 0x15617da021b89f92, 0xb782db1fc6aba49b, 0x83c4e245ed051dc1, // x 2^-13021 ~= 10^-3920
0xc6a0d64a712172b1, 0x2217669197ac1504, 0x4250be2eeba87d15, 0x86595584116caf3c, // x 2^-12835 ~= 10^-3864
0x0bdc0c67a220687b, 0x44a66a6d6fd6537b, 0x3f1f93f1943ca9b6, 0x88fab70d8b44952a, // x 2^-12649 ~= 10^-3808
0xb60b57164ad28122, 0xde5bd4572c25a830, 0x2c87f18b39478aa2, 0x8ba947b223e5783e, // x 2^-12463 ~= 10^-3752
0xbd59568efdb9bfee, 0x292f8f2c98d7f44c, 0x4054f5360249ebd1, 0x8e6549867da7d11a, // x 2^-12277 ~= 10^-3696
0x9fa0721e66791acc, 0x1789061d717d454c, 0xc1187fa0c18adbbe, 0x912effea7015b2c5, // x 2^-12091 ~= 10^-3640
0x982b64e953ac4e27, 0x45efb05f20cf48b3, 0x4b4de34e0ebc3e06, 0x9406af8f83fd6265, // x 2^-11905 ~= 10^-3584
0xa53f5950eec21dca, 0x3bd8754763bdbca1, 0xac73f0226eff5ea1, 0x96ec9e7f9004839b, // x 2^-11719 ~= 10^-3528
0x320e19f88f1161b7, 0x72e93fe0cce7cfd9, 0x2184706ea46a4c38, 0x99e11423765ec1d0, // x 2^-11533 ~= 10^-3472
0x491aba48dfc0e36e, 0xd3de560ee34022b2, 0xddadb80577b906bd, 0x9ce4594a044e0f1b, // x 2^-11347 ~= 10^-3416
0x06789d038697142f, 0x7a466a75be73db21, 0x60dbd8aa443b560f, 0x9ff6b82ef415d222, // x 2^-11161 ~= 10^-3360
0x40ed8056af76ac43, 0x08251c601e346456, 0x7401c6f091f87727, 0xa3187c82120dace6, // x 2^-10975 ~= 10^-3304
0x8c643ee307bffec6, 0xf369a11c6f66c05a, 0x4d5b32f713d7f476, 0xa649f36e8583e81a, // x 2^-10789 ~= 10^-3248
0xe32f5e080e36b4be, 0x3adf30ff2eb163d4, 0xb4b39dd9ddb8d317, 0xa98b6ba23e2300c7, // x 2^-10603 ~= 10^-3192
0x6b9d538c192cfb1b, 0x1c5af3bd4d2c60b5, 0xec41c1793d69d0d1, 0xacdd3555869159d1, // x 2^-10417 ~= 10^-3136
0x1adadaeedf7d699c, 0x71043692494aa743, 0x3ca5a7540d9d56c9, 0xb03fa252bd05a815, // x 2^-10231 ~= 10^-3080
0xec3e4e5fc6b03617, 0x47c9b16afe8fdf74, 0x92e1bc1fbb33f18d, 0xb3b305fe328e571f, // x 2^-10045 ~= 10^-3024
0x1d42fa68b12bdb23, 0xac46a7b3f2b4b34e, 0xa908fd4a88728b6a, 0xb737b55e31cdde04, // x 2^-9859 ~= 10^-2968
0x887dede507f2b618, 0x359a8fa0d014b9a7, 0x7c4c65d15c614c56, 0xbace07232df1c802, // x 2^-9673 ~= 10^-2912
0x504708e718b4b669, 0xfb4d9440822af452, 0xef84cc99cb4c5d17, 0xbe7653b01aae13e5, // x 2^-9487 ~= 10^-2856
0x5b7977525516bff0, 0x75913092420c9b35, 0xcfc147ade4843a24, 0xc230f522ee0a7fc2, // x 2^-9301 ~= 10^-2800
0xad5d11883cc1302b, 0x860a754894b9a0bc, 0x4668677d5f46c29b, 0xc5fe475d4cd35cff, // x 2^-9115 ~= 10^-2744
0x42032f9f971bfc07, 0x9fb576046ab35018, 0x474b3cb1fe1d6a7f, 0xc9dea80d6283a34c, // x 2^-8929 ~= 10^-2688
0xd3e7fbb72403a4dd, 0x8ca223055819af54, 0xd6ea3b733029ef0b, 0xcdd276b6e582284f, // x 2^-8743 ~= 10^-2632
0xba2431d885f2b7d9, 0xc9879fc42869f610, 0x3736730a9e47fef8, 0xd1da14bc489025ea, // x 2^-8557 ~= 10^-2576
0xa11edbcd65dd1844, 0xcb8edae81a295887, 0x3d24e68dc1027246, 0xd5f5e5681a4b9285, // x 2^-8371 ~= 10^-2520
0xa0f076652f69ad08, 0x9d19c341f5f42f2a, 0x742ab8f3864562c8, 0xda264df693ac3e30, // x 2^-8185 ~= 10^-2464
0x29f760ef115f2824, 0xe0ee47c041c9de0f, 0x8c119f3680212413, 0xde6bb59f56672cda, // x 2^-7999 ~= 10^-2408
0x8b90230b3409c9d3, 0x9d76eef2c1543e65, 0x43190b523f872b9c, 0xe2c6859f5c284230, // x 2^-7813 ~= 10^-2352
0xd44ce9993bc6611e, 0x777c9b2dfbede079, 0x2a0969bf88679396, 0xe7372943179706fc, // x 2^-7627 ~= 10^-2296
0xe8c5f5a63fd0fbd1, 0x0ccc12293f1d7a58, 0x131565be33dda91a, 0xebbe0df0c8201ac5, // x 2^-7441 ~= 10^-2240
0xdb97988dd6b776f4, 0xeb2106f435f7e1d5, 0xccfb1cc2ef1f44de, 0xf05ba3330181c750, // x 2^-7255 ~= 10^-2184
0x2fcbc8df94a1d54b, 0x796d0a8120801513, 0x5f8385b3a882ff4c, 0xf5105ac3681f2716, // x 2^-7069 ~= 10^-2128
0xc8700c11071a40f5, 0x23cb9e9df9331fe4, 0x166c15f456786c27, 0xf9dca895a3226409, // x 2^-6883 ~= 10^-2072
0x9589f4637a50cbb5, 0xea8242b0030e4a51, 0x6c656c3b1f2c9d91, 0xfec102e2857bc1f9, // x 2^-6697 ~= 10^-2016
0xc4be56c83349136c, 0x6188db81ac8e775d, 0xfa70b9a2ca60b004, 0x81def119b76837c8, // x 2^-6510 ~= 10^-1960
0xb85d39054658b363, 0xe7df06bc613fda21, 0x6a22490e8e9ec98b, 0x8469e0b6f2b8bd9b, // x 2^-6324 ~= 10^-1904
0x800b1e1349fef248, 0x469cfd2e6ca32a77, 0x69138459b0fa72d4, 0x87018eefb53c6325, // x 2^-6138 ~= 10^-1848
0xb62593291c768919, 0xc098e6ed0bfbd6f6, 0x6c83ad1260ff20f4, 0x89a63ba4c497b50e, // x 2^-5952 ~= 10^-1792
0x92ee7fce474479d3, 0xe02017175bf040c6, 0xd82ef2860273de8d, 0x8c5827f711735b46, // x 2^-5766 ~= 10^-1736
0x7b0e6375ca8c77d9, 0x5f07e1e10097d47f, 0x416d7f9ab1e67580, 0x8f17964dfc3961f2, // x 2^-5580 ~= 10^-1680
0xc8d869ed561af1ce, 0x8b6648e941de779b, 0x56700866b85d57fe, 0x91e4ca5db93dbfec, // x 2^-5394 ~= 10^-1624
0xfc04df783488a410, 0x64d1f15da2c146b1, 0x43cf71d5c4fd7868, 0x94c0092dd4ef9511, // x 2^-5208 ~= 10^-1568
0xfbaf03b48a965a64, 0x9b6122aa2b72a13c, 0x387898a6e22f821b, 0x97a9991fd8b3afc0, // x 2^-5022 ~= 10^-1512
0x50f7f7c13119aadd, 0xe415d8b25694250a, 0x8f8857e875e7774e, 0x9aa1c1f6110c0dd0, // x 2^-4836 ~= 10^-1456
0xce214403545fd685, 0xf36d1ad779b90e09, 0xa5c58d5f91a476d7, 0x9da8ccda75b341b5, // x 2^-4650 ~= 10^-1400
0x63ddfb68f971b0c5, 0x2822e38faf74b26e, 0x6e1f7f1642ebaac8, 0xa0bf0465b455e921, // x 2^-4464 ~= 10^-1344
0xf0d00cec9daf7444, 0x6bf3eea6f661a32a, 0xfad2be1679765f27, 0xa3e4b4a65e97b76a, // x 2^-4278 ~= 10^-1288
0x463b4ab4bd478f57, 0x6f6583b5b36d5426, 0x800cfab80c4e2eb1, 0xa71a2b283c14fba6, // x 2^-4092 ~= 10^-1232
0xef163df2fa96e983, 0xa825f32bc8f6b080, 0x850b0c5976b21027, 0xaa5fb6fbc115010b, // x 2^-3906 ~= 10^-1176
0x7db1b3f8e100eb43, 0x2862b1f61d64ddc3, 0x61363686961a41e5, 0xadb5a8bdaaa53051, // x 2^-3720 ~= 10^-1120
0xfd349cf00ba1e09a, 0x6d282fe1b7112879, 0xc6f075c4b81fc72d, 0xb11c529ec0d87268, // x 2^-3534 ~= 10^-1064
0xf7221741b221cf6f, 0x3739f15b06ac3c76, 0xb4e4be5b6455ef96, 0xb494086bbfea00c3, // x 2^-3348 ~= 10^-1008
0xc4e5a2f864c403bb, 0x6e33cdcda4367276, 0x24d256c540a50309, 0xb81d1f9569068d8e, // x 2^-3162 ~= 10^-952
0x276e3f0f67f0553b, 0x00de73d9d5be6974, 0x6d4aa5b50bb5dc0d, 0xbbb7ef38bb827f2d, // x 2^-2976 ~= 10^-896
0x51a34a3e674484ed, 0x1fb6069f8b26f840, 0x925624c0d7d93317, 0xbf64d0275747de70, // x 2^-2790 ~= 10^-840
0xcc775c8cb6de1dbc, 0x6d60d02eac6309ee, 0x8e5a2e5116baf191, 0xc3241cf0094a8e70, // x 2^-2604 ~= 10^-784
0x6023c8fa17d7b105, 0x069cf8f51d2e5e65, 0xb0560c246f90e9e8, 0xc6f631e782d57096, // x 2^-2418 ~= 10^-728
0x92c17acb2d08d5fd, 0xc26ffb8e81532725, 0x2ffff1289a804c5a, 0xcadb6d313c8736fc, // x 2^-2232 ~= 10^-672
0x47df78ab9e92897a, 0xc02b302a892b81dc, 0xa855e127113c887b, 0xced42ec885d9dbbe, // x 2^-2046 ~= 10^-616
0xdaf2dec03ec0c322, 0x72db3bc15b0c7014, 0xe00bad8dfc0d8c8e, 0xd2e0d889c213fd60, // x 2^-1860 ~= 10^-560
0xd3a04799e4473ac8, 0xa116409a2fdf1e9e, 0xc654d07271e6c39f, 0xd701ce3bd387bf47, // x 2^-1674 ~= 10^-504
0x5c8a5dc65d745a24, 0x2726c48a85389fa7, 0x84c663cee6b86e7c, 0xdb377599b6074244, // x 2^-1488 ~= 10^-448
0xd7ebc61ba77a9e66, 0x8bf77d4bc59b35b1, 0xcb285ceb2fed040d, 0xdf82365c497b5453, // x 2^-1302 ~= 10^-392
0x744ce999bfed213a, 0x363b1f2c568dc3e2, 0xfd1b1b2308169b25, 0xe3e27a444d8d98b7, // x 2^-1116 ~= 10^-336
0x6a40608fe10de7e7, 0xf910f9f648232f14, 0xd1b3400f8f9cff68, 0xe858ad248f5c22c9, // x 2^-930 ~= 10^-280
0x9bdbfc21260dd1ad, 0x4609ac5c7899ca36, 0xa4f8bf5635246428, 0xece53cec4a314ebd, // x 2^-744 ~= 10^-224
0xd88181aad19d7454, 0xf80f36174730ca34, 0xdc44e6c3cb279ac1, 0xf18899b1bc3f8ca1, // x 2^-558 ~= 10^-168
0xee19bfa6947f8e02, 0xaa09501d5954a559, 0x4d4617b5ff4a16d5, 0xf64335bcf065d37d, // x 2^-372 ~= 10^-112
0xebbc75a03b4d60e6, 0xac2e4f162cfad40a, 0xeed6e2f0f0d56712, 0xfb158592be068d2e, // x 2^-186 ~= 10^-56
0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x8000000000000000, // x 2^1 == 10^0 exactly
0x0000000000000000, 0x2000000000000000, 0xbff8f10e7a8921a4, 0x82818f1281ed449f, // x 2^187 == 10^56 exactly
0x51775f71e92bf2f2, 0x74a7ef0198791097, 0x03e2cf6bc604ddb0, 0x850fadc09923329e, // x 2^373 ~= 10^112
0xb204b3d9686f55b5, 0xfb118fc9c217a1d2, 0x90fb44d2f05d0842, 0x87aa9aff79042286, // x 2^559 ~= 10^168
0xd7924bff833149fa, 0xbc10c5c5cda97c8d, 0x82bd6b70d99aaa6f, 0x8a5296ffe33cc92f, // x 2^745 ~= 10^224
0xa67d072d3c7fa14b, 0x7ec63730f500b406, 0xdb0b487b6423e1e8, 0x8d07e33455637eb2, // x 2^931 ~= 10^280
0x546f2a35dc367e47, 0x949063d8a46f0c0e, 0x213a4f0aa5e8a7b1, 0x8fcac257558ee4e6, // x 2^1117 ~= 10^336
0x50611a621c0ee3ae, 0x202d895116aa96be, 0x1c306f5d1b0b5fdf, 0x929b7871de7f22b9, // x 2^1303 ~= 10^392
0xffa6738a27dcf7a3, 0x3c11d8430d5c4802, 0xa7ea9c8838ce9437, 0x957a4ae1ebf7f3d3, // x 2^1489 ~= 10^448
0x5bf36c0f40bde99d, 0x284ba600ee9f6303, 0xbf1d49cacccd5e68, 0x9867806127ece4f4, // x 2^1675 ~= 10^504
0xa6e937834ed12e58, 0x73f26eb82f6b8066, 0x655494c5c95d77f2, 0x9b63610bb9243e46, // x 2^1861 ~= 10^560
0x0cd4b7660adc6930, 0x8f868688f8eb79eb, 0x02e008393fd60b55, 0x9e6e366733f85561, // x 2^2047 ~= 10^616
0x3efb9807d86d3c6a, 0x84c10a1d22f5adc5, 0x55e04dba4b3bd4dd, 0xa1884b69ade24964, // x 2^2233 ~= 10^672
0xf065089401df33b4, 0x1fc02370c451a755, 0x44b222741eb1ebbf, 0xa4b1ec80f47c84ad, // x 2^2419 ~= 10^728
0xa62d0da836fce7d5, 0x75933380ceb5048c, 0x1cf4a5c3bc09fa6f, 0xa7eb6799e8aec999, // x 2^2605 ~= 10^784
0x7a400df820f096c2, 0x802c4085068d2dd5, 0x3c4a575151b294dc, 0xab350c27feb90acc, // x 2^2791 ~= 10^840
0xf48b51375df06e86, 0x412fe9e72afd355e, 0x870a8d87239d8f35, 0xae8f2b2ce3d5dbe9, // x 2^2977 ~= 10^896
0x881883521930127c, 0xe53fd3fcb5b4df25, 0xdd929f09c3eff5ac, 0xb1fa17404a30e5e8, // x 2^3163 ~= 10^952
0x270cd9f1348eb326, 0x37ed82fe9c75fccf, 0x1931b583a9431d7e, 0xb5762497dbf17a9e, // x 2^3349 ~= 10^1008
0x8919b01a5b3d9ec1, 0x6a7669bdfc6f699c, 0xe30db03e0f8dd286, 0xb903a90f561d25e2, // x 2^3535 ~= 10^1064
0xf0461526b4201aa5, 0x7fe40defe17e55f5, 0x9eb5cb19647508c5, 0xbca2fc30cc19f090, // x 2^3721 ~= 10^1120
0xd67bf35422978bbf, 0x0dbb1c416ebe661f, 0x24bd4c00042ad125, 0xc054773d149bf26b, // x 2^3907 ~= 10^1176
0xdd093192ef5508d0, 0x6eac3085943ccc0f, 0x7ea30dbd7ea479e3, 0xc418753460cdcca9, // x 2^4093 ~= 10^1232
0xfe4ff20db6d25dc2, 0x5d5d5a9519e34a42, 0x764f4cf916b4dece, 0xc7ef52defe87b751, // x 2^4279 ~= 10^1288
0xd8adfb2e00494c5e, 0x72435286baf0e84e, 0xbeb7fbdc1cbe8b37, 0xcbd96ed6466cf081, // x 2^4465 ~= 10^1344
0xe07c1e4384f594af, 0x0c6b90b8874d5189, 0xdce472c619aa3f63, 0xcfd7298db6cb9672, // x 2^4651 ~= 10^1400
0x5dd902c68fa448cf, 0xea8d16bd9544e48e, 0xe47defc14a406e4f, 0xd3e8e55c3c1f43d0, // x 2^4837 ~= 10^1456
0x1223d79357bedca8, 0xeae6c2843752ac35, 0xb7157c60a24a0569, 0xd80f0685a81b2a81, // x 2^5023 ~= 10^1512
0xcff72d64bc79e429, 0xccc52c236decd778, 0xfb0b98f6bbc4f0cb, 0xdc49f3445824e360, // x 2^5209 ~= 10^1568
0x3731f76b905dffbb, 0x5e2bddd7d12a9e42, 0xc6c6c1764e047e15, 0xe09a13d30c2dba62, // x 2^5395 ~= 10^1624
0xeb58d8ef2ada7c09, 0xbc1a3b726b789947, 0x87e8dcfc09dbc33a, 0xe4ffd276eedce658, // x 2^5581 ~= 10^1680
0x249a5c06dc5d5db7, 0xa8f09440be97bfe6, 0xb1a3642a8da3cf4f, 0xe97b9b89d001dab3, // x 2^5767 ~= 10^1736
0xbf34ff7963028cd9, 0xc20578fa3851488b, 0x2d4070f33b21ab7b, 0xee0ddd84924ab88c, // x 2^5953 ~= 10^1792
0x002d0511317361d5, 0xd6919e041129a1a7, 0xa2bf0c63a814e04e, 0xf2b70909cd3fd35c, // x 2^6139 ~= 10^1848
0x1fa87f28acf1dcd2, 0xe7a0a88981d1a0f9, 0x08f13995cf9c2747, 0xf77790f0a48a45ce, // x 2^6325 ~= 10^1904
0x1b6ff8afbe589b72, 0xc851bb3f9aeb1211, 0x7a37993eb21444fa, 0xfc4fea4fd590b40a, // x 2^6511 ~= 10^1960
0xef23a4cbc039f0c2, 0xbb3f8498a972f18e, 0xb7b1ada9cdeba84d, 0x80a046447e3d49f1, // x 2^6698 ~= 10^2016
0x2cc44f2b602b6231, 0xf231f4b7996b7278, 0x0cc6866c5d69b2cb, 0x8324f8aa08d7d411, // x 2^6884 ~= 10^2072
0x822c97629a3a4c69, 0x8a9afcdbc940e6f9, 0x7fe2b4308dcbf1a3, 0x85b64a659077660e, // x 2^7070 ~= 10^2128
0xf66cfcf42d4896b0, 0x1f11852a20ed33c5, 0x1d73ef3eaac3c964, 0x88547abb1d8e5bd9, // x 2^7256 ~= 10^2184
0x63093ad0caadb06c, 0x31be1482014cdaf0, 0x1e34291b1ef566c7, 0x8affca2bd1f88549, // x 2^7442 ~= 10^2240
0xab50f69048738e9a, 0xa126c32ff4882be8, 0x9e9383d73d486881, 0x8db87a7c1e56d873, // x 2^7628 ~= 10^2296
0xe57e659432b0a73e, 0x47a0e15dfc7986b8, 0x9cc5ee51962c011a, 0x907eceba168949b3, // x 2^7814 ~= 10^2352
0x8a6ff950599f8ae5, 0xd1cbbb7d005a76d3, 0x413407cfeeac9743, 0x93530b43e5e2c129, // x 2^8000 ~= 10^2408
0xd4e6b6e847550caa, 0x56a3106227b87706, 0x7efa7d29c44e11b7, 0x963575ce63b6332d, // x 2^8186 ~= 10^2464
0xd835c90b09842263, 0xb69f01a641da2a42, 0x5a848859645d1c6f, 0x9926556bc8defe43, // x 2^8372 ~= 10^2520
0x9b0ae73c204ecd61, 0x0794fd5e5a51ac2f, 0x51edea897b34601f, 0x9c25f29286e9ddb6, // x 2^8558 ~= 10^2576
0x3130484fb0a61d89, 0x32b7105223a27365, 0xb50008d92529e91f, 0x9f3497244186fca4, // x 2^8744 ~= 10^2632
0x8cd036553f38a1e8, 0x5e997e9f45d7897d, 0xf09e780bcc8238d9, 0xa2528e74eaf101fc, // x 2^8930 ~= 10^2688
0xe1f8b43b08b5d0ef, 0xa0eaf3f62dc1777c, 0x3a5828869701a165, 0xa580255203f84b47, // x 2^9116 ~= 10^2744
0x3c7f62e3154fa708, 0x5786f3927eb15bd5, 0x8b231a70eb5444ce, 0xa8bdaa0a0064fa44, // x 2^9302 ~= 10^2800
0x1ebc24a19cd70a2a, 0x843fddd10c7006b8, 0xfa1bde1f473556a4, 0xac0b6c73d065f8cc, // x 2^9488 ~= 10^2856
0x46b6aae34cfd26fc, 0x00db7d919b136c68, 0x7730e00421da4d55, 0xaf69bdf68fc6a740, // x 2^9674 ~= 10^2912
0x1c4edcb83fc4c49d, 0x61c0edd56bbcb3e8, 0x7f959cb702329d14, 0xb2d8f1915ba88ca5, // x 2^9860 ~= 10^2968
0x428c840d247382fe, 0x9cc3b1569b1325a4, 0x40c3a071220f5567, 0xb6595be34f821493, // x 2^10046 ~= 10^3024
0xbeb82e734787ec63, 0xbeff12280d5a1676, 0x11c48d02b8326bd3, 0xb9eb5333aa272e9b, // x 2^10232 ~= 10^3080
0x302349e12f45c73f, 0xb494bcc96d53e49c, 0x566765461bd2f61b, 0xbd8f2f7a1ba47d6d, // x 2^10418 ~= 10^3136
0x5704ebf5f16946ce, 0x431388ec68ac7a26, 0xb889018e4f6e9a52, 0xc1454a673cb9b1ce, // x 2^10604 ~= 10^3192
0x5a30431166af9b23, 0x132d031fc1d1fec0, 0xf85333a94848659f, 0xc50dff6d30c3aefc, // x 2^10790 ~= 10^3248
0x7573d4b3ffe4ba3b, 0xf888498a40220657, 0x1a1aeae7cf8a9d3d, 0xc8e9abc872eb2bc1, // x 2^10976 ~= 10^3304
0xb5eaef7441511eb9, 0xc9cf998035a91664, 0x12e29f09d9061609, 0xccd8ae88cf70ad84, // x 2^11162 ~= 10^3360
0x73aed4f1908f4d01, 0x8c53e7beeca4578f, 0xdf7601457ca20b35, 0xd0db689a89f2f9b1, // x 2^11348 ~= 10^3416
0x5adbd55696e1cdd9, 0x4949d09424b87626, 0xcbdcd02f23cc7690, 0xd4f23ccfb1916df5, // x 2^11534 ~= 10^3472
0x3f500ccf4ea03593, 0x9b80aac81b50762a, 0x44289dd21b589d7a, 0xd91d8fe9a3d019cc, // x 2^11720 ~= 10^3528
0x134ca67a679b84ae, 0x8909e424a112a3cd, 0x95aa118ec1d08317, 0xdd5dc8a2bf27f3f7, // x 2^11906 ~= 10^3584
0xe89e3cf733d9ff40, 0x014344660a175c36, 0x72c4d2cad73b0a7b, 0xe1b34fb846321d04, // x 2^12092 ~= 10^3640
0x68c0a2c6c02dae9a, 0x0b11160a6edb5f57, 0xe20a88f1134f906d, 0xe61e8ff47461cda9, // x 2^12278 ~= 10^3696
0x47fa54906741561a, 0xaa13acba1e5511f5, 0xc7c91d5c341ed39d, 0xea9ff638c54554e1, // x 2^12464 ~= 10^3752
0x365460ed91271c24, 0xabe33496aff629b4, 0xf659ede2159a45ec, 0xef37f1886f4b6690, // x 2^12650 ~= 10^3808
0xe4cbf4acc7fba37f, 0x350e915f7055b1b8, 0x78d946bab954b82f, 0xf3e6f313130ef0ef, // x 2^12836 ~= 10^3864
0xe692accdfa5bd859, 0xf4d4d3202379829e, 0xc9b1474d8f89c269, 0xf8ad6e3fa030bd15, // x 2^13022 ~= 10^3920
0xeca0018ea3b8d1b4, 0xe878edb67072c26d, 0x6b1d2745340e7b14, 0xfd8bd8b770cb469e, // x 2^13208 ~= 10^3976
0xce5fec949ab87cf7, 0x0151dcd7a53488c3, 0xf22e502fcdd4bca2, 0x81415538ce493bd5, // x 2^13395 ~= 10^4032
0x5e1731fbff8c032e, 0xe752f53c2f8fa6c1, 0x7c1735fc3b813c8c, 0x83c92edf425b292d, // x 2^13581 ~= 10^4088
0xb552102ea83f47e6, 0xdf0fd2002ff6b3a3, 0x0367500a8e9a178f, 0x865db7a9ccd2839e, // x 2^13767 ~= 10^4144
0x76507bafe00ec873, 0x71b256ecd954434c, 0xc9ac50475e25293a, 0x88ff2f2bade74531, // x 2^13953 ~= 10^4200
0x5e2075ba289a360b, 0xac376f28b45e5acc, 0x0879b2e5f6ee8b1c, 0x8badd636cc48b341, // x 2^14139 ~= 10^4256
0xab87d85e6311e801, 0xb7f786d14d58173d, 0x2f33c652bd12fab7, 0x8e69eee1f23f2be5, // x 2^14325 ~= 10^4312
0x7fed9b68d77255be, 0x35dc241819de7182, 0xad6a6308a8e8b557, 0x9133bc8f2a130fe5, // x 2^14511 ~= 10^4368
0x728ae72899d4bd12, 0xe5413d9414142a55, 0x9dbaa465efe141a0, 0x940b83f23a55842a, // x 2^14697 ~= 10^4424
0x0f7740145246fb8f, 0x186ef2c39acb4103, 0x888c9ab2fc5b3437, 0x96f18b1742aad751, // x 2^14883 ~= 10^4480
0xd8bb0fba2183c6ef, 0xbf66d66cc34f0197, 0xba00864671d1053f, 0x99e6196979b978f1, // x 2^15069 ~= 10^4536
0x9b71ed2ceb790e49, 0x6faac32d59cc1f5d, 0x61d59d402aae4fea, 0x9ce977ba0ce3a0bd, // x 2^15255 ~= 10^4592
0xa0aa6d5e63991cfb, 0x19482fa0ac45669c, 0x803c1cd864033781, 0x9ffbf04722750449, // x 2^15441 ~= 10^4648
0x95a9949e04b8bff3, 0x900aa3c2f02ac9d4, 0xa28a151725a55e10, 0xa31dcec2fef14b30, // x 2^15627 ~= 10^4704
0x3acf9496dade0ce9, 0xbd8ecf923d23bec0, 0x5b8452af2302fe13, 0xa64f605b4e3352cd, // x 2^15813 ~= 10^4760
0x6204425d2b58e822, 0xdee162a8a1248550, 0x82b84cabc828bf93, 0xa990f3c09110c544, // x 2^15999 ~= 10^4816
0x091a2658e0639f32, 0x66fa2184cee0b861, 0x8d29dd5122e4278d, 0xace2d92db0390b59, // x 2^16185 ~= 10^4872
0x80acda113324758a, 0xded179c26d9ab828, 0x58f8fde02c03a6c6, 0xb045626fb50a35e7, // x 2^16371 ~= 10^4928
0x7128a8aad239ce8f, 0x8737bd250290cd5b, 0xd950102978dbd0ff, 0xb3b8e2eda91a232d, // x 2^16557 ~= 10^4984
]
@available(SwiftStdlib 6.2, *)
fileprivate func _intervalContainingPowerOf10_Binary128(
p: Int,
lower: inout _UInt256,
upper: inout _UInt256
) -> Int {
if p >= 0 && p <= 55 {
let exactLow = powersOf10_Exact128[p * 2]
let exactHigh = powersOf10_Exact128[p * 2 + 1]
lower = _UInt256(high: exactHigh, exactLow, 0, low: 0)
upper = lower
return binaryExponentFor10ToThe(p)
}
let index = p + 4984
let offset = (index / 56) * 4
lower = _UInt256(
high: powersOf10_Binary128[offset + 3],
powersOf10_Binary128[offset + 2],
powersOf10_Binary128[offset + 1],
low: powersOf10_Binary128[offset + 0])
let extraPower = index % 56
var e = binaryExponentFor10ToThe(p - extraPower)
if extraPower > 0 {
let extra = UInt128(
_low: powersOf10_Exact128[extraPower * 2],
_high: powersOf10_Exact128[extraPower * 2 + 1])
lower.multiplyRoundingDown(by: extra)
e += binaryExponentFor10ToThe(extraPower)
}
upper = lower
upper.low += 2
return e
}