//===--- FloatingPointToString.swift -------------------------*- Swift -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2018-2026 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, logging, debugging, and any general printing where you don't
// have specific UI requirements.
//
// Because it doesn't have to support an arbitrary number of output
// digits, this format can be generated _much_ faster than the
// `printf` `%e`/`%f`/`%g` forms, which makes it even more ideal for
// JSON, logging, and general output.
//
//===---------------------------------------------------------------------===//
///
/// This code has used a variety of different algorithms over the years, all
/// generating exactly identical output.  The current implementation starts
/// with a floating-point value `s * 2^e` where s and e are both integers.  We
/// compute
/// ```
///   p = ceil(e * log10(2))
/// ```
/// and then do a fixed-precision multiply
/// ```
///   (s + 1/2) * 2^e * 10^-p
/// ```
/// with different rounding depending on whether the original
/// `s` is even or odd.
///
/// * About 40% of the time, the integer portion of this last value is
///   precisely the integer significand of the optimal base-10
///   representation we want.  We only need to convert this integer to
///   digits with a fast integer conversion algorithm, such as the
///   one below based on work by Paul Khuong.
///
/// * The remaining 60% of the time, we need to generate a single
///   additional digit (with correct rounding).
///
/// The description above omits many key details, of course.  A more complete
/// description appears in the extensive comments for the Float32 implementation
/// below.  (The other implementations below use essentially the same approach,
/// only with fewer comments.)
///
/// The implementation draws inspiration from the following papers:
///
/// ------------
/// Raffaello Guilietti; "The Schubfach way to render doubles", 2021.
/// Published online.
///
/// Credit: Guilietti computes the power-of-10 scaling factor based on
/// the width of the rounding interval in order to obtain a
/// nearly-final result from the initial scaling, avoiding the need
/// for per-digit iteration.
/// ------------
/// Florian Loitsch; "Printing Floating-Point Numbers Quickly and
/// Accurately with Integers", 2010.
/// https://doi.org/10.1145/1806596.1806623
///
/// Credit: Our test for whether the initial digits give us a value in
/// the rounding interval is from Loitsch' Grisu algorithm.  The logic
/// to generate an additional digit when needed is also based on Grisu.
/// ------------
/// Marc Andrysco, Ranjit Jhala, Sorin Lerner; "Printing Floating-Point
/// Numbers: A Faster, Always Correct Method", 2016.
/// https://doi.org/10.1145/2837614.2837654
///
/// Credit: Andrysco, Jhala, and Lerner explored the impact of
/// higher precision arithmetic on Grisu-style algorithms and
/// provided a robust way to test implementations of such algorithms
/// for correctness.
/// ------------
/// TODO: Cite vitaut's exploration of Schubfach variants?
///
/// In short, this implementation is:
///
/// * Fast.  It uses only fixed-width integer arithmetic and has
///   constant memory requirements.  On an Apple M4 processor,
///   _Float64ToASCII can convert a Float64 into text in about 10ns.
///
/// * Always Round-trip 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, Eisel-Lemire 2021, or that
///   used in FloatingPointFromString.swift.)
///
/// * 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 to achieve other goals.
/// Any attempt to compare the performance of this implementation
/// to others should consider the following:
/// * The output ergonomics described above do take some time.
///   It would be faster to always emit an integer significand
///   followed by an exponent (e.g., "123456e-78").
/// * 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 `_powerOf10_*()` functions)
/// * Compare code size as well as performance.  Our Float64
///   implementation here is a little over 3kiB compiled, _including_
///   the supporting data tables.  (Most published implementations
///   require twice that just for their supporting tables.)
///
// ----------------------------------------------------------------------------

// ================================================================
//
// Float16
//
// ================================================================

#if (os(macOS) || targetEnvironment(macCatalyst)) && arch(x86_64)

// Float16 is not currently supported on Intel x86_64 macOS,
// (including macCatalyst on x86_64) but this symbol somehow got
// exported there.
// This preserves that export.

// Note: Other platforms that don't support Float16 should
// NOT export this.
@available(SwiftStdlib 5.3, *)
@_silgen_name("swift_float16ToString")
public func _float16ToStringImpl(
  _ textBuffer: UnsafeMutablePointer<UTF8.CodeUnit>,
  _ bufferLength: UInt,
  _ value: Float,
  _ debug: Bool
) -> UInt64 {
  fatalError()
}

#else

// Support Legacy ABI on top of new implementation
@available(SwiftStdlib 5.3, *)
@_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))
  for i in 0..<Int(bufferLength) {
    unsafe buffer[unchecked: i] = 0x30
  }
  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.
// 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 20 bytes long and must be pre-filled
// with "0" characters, e.g., via
// `InlineArray<32,UTF8.CodeUnit>(repeating:0x30)`

// Notes on the implementation here: This implementation writes out
// all integer values in non-exponential form.  This is technically
// not "optimal".  For example, "65504.0" shows 5 significant digits,
// even though the 3 significant digits of "6.55e+04" is sufficient.
// But that just looks silly.
//
// As a side-effect of the above, we end up handling integer values as
// a special case, so the general path only has to deal with values
// whose binary exponent is negative.  This means they are handled by
// multiplying by a positive power of ten, which can be done exactly
// with integer arithmetic due to the small range of Float16.
@available(SwiftStdlib 5.3, *)
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
  assert(utf8Buffer.count >= 24)
  var buffer = utf8Buffer.mutableBytes

  let significandBitCount = Float16.significandBitCount + 1 // 11
  let exponentBias = Int(bitPattern: Float16._exponentBias) - 1 + significandBitCount // 14 + 11 = 25
  let binaryExponent: Int
  let significand: Float16.RawSignificand

  // Step 1: Handle the special cases, decompose the input

  if f.exponentBitPattern == Float16._infinityExponent {
    if (f.isInfinite) {
      return _infinity(buffer: &buffer, sign: f.sign)
    } else { // f.isNaN
      let quietBit = f.significandBitPattern & Float16._quietNaNMask
      let payload = f.significandBitPattern & (Float16._significandMask &>> 2)
      return nan_details(
        buffer: &buffer,
        sign: f.sign,
        quiet: quietBit != 0,
        payloadHigh: 0,
        payloadLow: UInt64(truncatingIfNeeded:payload))
    }
  } else if f.exponentBitPattern == 0 {
    if (f.isZero) {
      return _zero(buffer: &buffer, sign: f.sign)
    } else { // f.isSubnormal
      significand = f.significandBitPattern
      binaryExponent = 1 &- exponentBias
    }
  } else {
    // Normal
    let b = Int(f.exponentBitPattern) &- exponentBias
    let implicitBit = Float16.RawSignificand(1) << Float16.significandBitCount
    let s = f.significandBitPattern &+ implicitBit
    if b > 0 {
      // Special handling for integer values, as described above.
      let base10Significand = UInt32(s) &<< b
      let digits = _intToEightDecimalDigits(base10Significand)
      var start = 1
      buffer.storeBytes(
        of: digits &+ 0x3030303030303030,
        toByteOffset: start,
        as: UInt64.self)
      var end = start + 8

      // Trim leading zeros and insert `-` if appropriate:
      start += digits.trailingZeroBitCount / 8
      if f.sign == .minus {
        buffer.storeBytes(
          of: 0x2d, // "-"
          toByteOffset: start &- 1,
          as: UInt8.self)
        start &-= 1
      }

      // Add ".0" at end
      buffer.storeBytes(
        of: 0x2e, // "."
        toByteOffset: end,
        as: UInt8.self)
      end += 2
      return start..<end
    } else {
      significand = s
      binaryExponent = b
    }
  }

  // Step 2: Compute the base 10 exponent to match the rounding interval
  // log10(2) * 2^8 ~= 77     log10(3/4) * 2^8 ~= -32
  let maybeLogThreeQuarters = f.significandBitPattern == 0 ? -32 : 0
  let rawBase10Power = binaryExponent &* 77 &+ maybeLogThreeQuarters
  let roundedBase10Power = (rawBase10Power &+ 0xff) >> 8
  var base10Power = Int(truncatingIfNeeded: roundedBase10Power)

  // Step 3: Look up power-of-10 scale factor
  let powerOfTen = UInt64(powersOf10_Float16[0 &- base10Power])

  // Step 4: Scale the interval (with rounding)

  // If significand is odd, we need to narrow the interval
  let narrowInterval = f.significandBitPattern & 1

  // Scale 1 binary ULP
  var scaledBinaryUlp = powerOfTen &<< (32 &+ binaryExponent)
  scaledBinaryUlp &+= 2 &- 4 &* UInt64(narrowInterval)

  // We'll use 32.32 fixed point from here on...
  let fractionBits = 32
  let fractionMask = (UInt64(1) << fractionBits) - 1
  let oneHalf = UInt64(1) << (fractionBits - 1)

  // Scaled upper limit of the rounding interval
  let u0 = powerOfTen &* UInt64(significand)
  let u1 = u0 &<< (32 &+ binaryExponent)
  let upperBound = u1 &+ (scaledBinaryUlp >> 1)
  var base10Significand = UInt32(truncatingIfNeeded: upperBound >> fractionBits)
  let delta = upperBound & fractionMask

  // Scaled distance from the value above to the lower bound
  // of the rounding interval.
  var intervalWidth = scaledBinaryUlp
  if f.significandBitPattern == 0 {
    intervalWidth &-= intervalWidth >> 2
  }

  // Step 6: Compute one more decimal digit if necessary
  if intervalWidth < delta {
    // Adjust intervalWidth and delta to measure from the
    // actual target value to the lower bound of the rounding
    // interval.  (Up to this point, they've measured from the upper
    // bound of the rounding interval.)
    let halfUlp = scaledBinaryUlp &* 5
    var delta = delta &* 10 &- halfUlp
    intervalWidth = intervalWidth &* 10 &- halfUlp

    // Compute the additional digit
    var digit = UInt8(truncatingIfNeeded: delta >> fractionBits)
    delta &= fractionMask

    // Note that `digit` is the largest value below the target value;
    // `digit + 1` is the smallest value above the target value.  We
    // want to select whichever is closer to the target value.
    let greater = unsafe unsafeBitCast(delta > oneHalf, to: UInt8.self)
    // If they're equally close, pick the even one
    let greaterOrEqual = unsafe unsafeBitCast(delta >= oneHalf, to: UInt8.self)
    // If the lower one is not in the interval, choose the higher one
    let outOfInterval = unsafe unsafeBitCast(delta > intervalWidth, to: UInt8.self)
    digit &+= greater | (greaterOrEqual & digit) | outOfInterval

    // Add the new digit into the base10Significand
    base10Significand = base10Significand &* 10 &+ UInt32(digit)
    base10Power &-= 1
  }

  // Step 7: Convert the digits and write them out...
  var digits = _intToEightDecimalDigits(base10Significand)
  let trailingZeros = digits.leadingZeroBitCount / 8
  digits &<<= trailingZeros * 8
  base10Power &+= trailingZeros

  let startOffset = 6
  buffer.storeBytes(
    of: digits &+ 0x3030303030303030,
    toByteOffset: startOffset,
    as: UInt64.self)
  let firstDigit = startOffset &+ digits.trailingZeroBitCount / 8
  let nextDigit = startOffset &+ 8

  // Step 8: Finish formatting
  return _finishFormatting(
    buffer: &buffer,
    sign: f.sign,
    firstDigit: firstDigit,
    nextDigit: nextDigit,
    forceExponential: false,
    base10Power: base10Power)
}

fileprivate let powersOf10_Float16: _InlineArray<_, UInt32> = [
  1, 10, 100, 1000, 10000, 100000, 1000000, 10000000
]

#endif

// ================================================================
//
// Float32
//
// ================================================================

// Support Legacy ABI on top of new implementation
@_silgen_name("swift_float32ToString")
@usableFromInline
internal func _float32ToStringImpl(
  _ textBuffer: UnsafeMutablePointer<UTF8.CodeUnit>,
  _ bufferLength: UInt,
  _ value: Float32,
  _ debug: Bool
) -> UInt64 {
  // Code below works with raw memory.
  var buffer = unsafe MutableSpan<UTF8.CodeUnit>(
    _unchecked: textBuffer,
    count: Int(bufferLength))
  for i in 0..<Int(bufferLength) {
    unsafe buffer[unchecked: i] = 0x30
  }
  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)
}

// Convert a Float32 to an optimal ASCII representation.
// 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 20 bytes long and must be pre-filled
// with "0" characters, e.g., via
// `InlineArray<32,UTF8.CodeUnit>(repeating:0x30)`
internal func _Float32ToASCII(
  value f: Float32,
  buffer utf8Buffer: inout MutableSpan<UTF8.CodeUnit>
) -> Range<Int> {
  // We need a MutableRawSpan in order to use wide store/load operations
  assert(utf8Buffer.count >= 24)
  var buffer = utf8Buffer.mutableBytes

  let significandBitCount = Float.significandBitCount + 1 // 24
  let exponentBias = Int(bitPattern: Float32._exponentBias) - 1 + significandBitCount // 126 + 24
  let binaryExponent: Int
  let significand: Float.RawSignificand

  // Step 1: Decompose the input, handle the special cases

  if f.exponentBitPattern == Float32._infinityExponent {
    if (f.isInfinite) {
      return _infinity(buffer: &buffer, sign: f.sign)
    } else { // f.isNaN
      let quietBit = f.significandBitPattern & Float32._quietNaNMask
      let payload = f.significandBitPattern & (Float32._significandMask &>> 2)
      return nan_details(
        buffer: &buffer,
        sign: f.sign,
        quiet: quietBit != 0,
        payloadHigh: 0,
        payloadLow: UInt64(truncatingIfNeeded:payload))
    }
  } else if f.exponentBitPattern == 0 {
    if (f.isZero) {
      return _zero(buffer: &buffer, sign: f.sign)
    } else { // f.isSubnormal
      significand = f.significandBitPattern
      binaryExponent = 1 &- exponentBias
    }
  } else {
    // Normal
    let implicitBit = Float.RawSignificand(1) << Float.significandBitCount
    significand = f.significandBitPattern &+ implicitBit
    binaryExponent = Int(f.exponentBitPattern) &- exponentBias
  }

  // Step 2: Compute the base 10 exponent to match the rounding interval

  // Following Schubfach, let RI = the width of the rounding interval
  // Compute base10Power such that
  //    10^base10Power > RI > 10^(base10Power - 1)
  // This guarantees:
  // * AT MOST one multiple of 10^base10Power in our rounding interval
  //   So if we find one, it's unique and we're done!
  // * AT LEAST one multiple of 10^(base10Power-1) in that interval.
  //   So if we don't find a multiple of 10^base10Power, then
  //   we're certain to find a solution with just one more digit.

  // In the common case where RI = 2^e exactly, this implies that:
  //    base10Power = ceiling(e * log10(2))
  // For an exact power of 2, the rounding interval is 25% narrower, so
  //    base10Power = ceiling(e * log10(2) + log10(0.75))
  let maybeLogThreeQuarters = (f.significandBitPattern == 0
                                 ? Int64(-536607787)
                                 : Int64(0))
  let rawBase10Power = Int64(binaryExponent) &* 1292913986 &+ maybeLogThreeQuarters
  let roundedBase10Power = (rawBase10Power &+ 0xffffffff) &>> 32
  var base10Power = Int(truncatingIfNeeded: roundedBase10Power)

  // Step 3: Look up power-of-10 scale factor

  // To obtain a binary floating-point value for the power of 10, look
  // up the significand in a table and compute the corresponding
  // exponent.  The power of 10 is then:
  //     powerOfTen * 2^powerOfTenExponent
  let powerOfTen = powersOf10_Float32[33 &- base10Power]
  let powerOfTenExponent = binaryExponentFor10ToThe(0 &- base10Power)

  // Step 4: Scale the interval (with rounding)
  // We compute `upperBound` the upper bound of the rounding interval and
  // `intervalWidth` the width, both scaled by 10^(-base10Power)

  // After scaling, we need enough integer bits to represent the
  // scaled significand.  The worst case is the asymmetric case: then
  // 10^p > 3/4 * 2^e, so the maximum possible value of 2^e 10^-p is
  // 4/3, which means we'll need one more bit than the binary
  // significand.
  let integerBits = significandBitCount + 1
  let fractionBits = 64 - integerBits
  let fractionMask = (UInt64(1) << fractionBits) - 1
  let oneHalf = UInt64(1) << (fractionBits - 1)

  // If the significand is even, we want to widen the interval by
  // rounding up the upper midpoint and increasing intervalWidth.
  // Conversely if the significand is odd.  This ensures that the
  // exact endpoints of the rounding interval are handled correctly.
  let narrowInterval = f.significandBitPattern & 1

  // Scale one binary ULP
  let align = integerBits &- (binaryExponent &+ powerOfTenExponent)
  var scaledBinaryUlp = powerOfTen &>> align
  // Widen or narrow the Ulp estimate -- this will flow through
  // to the other places we need even/odd adjustments.
  scaledBinaryUlp &+= 3 &- 6 &* UInt64(narrowInterval)

  // Scaled upper bound of the rounding interval
  let mask32 = UInt64(UInt32.max)
  let low = (powerOfTen & mask32) &* UInt64(significand)
  let high = (powerOfTen &>> 32) &* UInt64(significand)
  // (high << 32) + low is the 96-bit intermediate value. Combine and
  // align to 64 bit fixed-point, and add 1/2 ULP for the upper bound.
  let upperBound = ((low &>> align)
                      &+ (high &<< (32 &- align))
                      &+ (scaledBinaryUlp &>> 1))

  // Width of the scaled rounding interval
  // In the non-power-of-two case, the rounding interval has width 1 ULP.
  var intervalWidth = scaledBinaryUlp
  if f.significandBitPattern == 0 {
    // In the power-of-two case, the interval is 3/4 ULP.
    intervalWidth &-= intervalWidth &>> 2
  }

  // Step 5: Emit most of the decimal digits into the destination buffer

  // firstDigit starts at 6 to give us room to insert an initial minus
  // sign and/or leading zeros.  This is used by the ergonomic formatting
  // logic done in `finishFormatting()`.
  var firstDigit = 6
  var nextDigit = firstDigit

  // Split the scaled upperBound into the integer part
  // (initial base-10 significand) and fraction `delta` (distance
  // between that significand and the upper bound of the
  // interval).
  let base10Significand = UInt32(truncatingIfNeeded: upperBound &>> fractionBits)
  var delta = upperBound & fractionMask

  // Convert the digits we have so far and write them out...  We could
  // delay this and fold an extra digit (if necessary) into the
  // base10Significand.  That would work because a UInt32 can hold any
  // 9-digit integer.  It would allow us to mimic the Float16
  // structure above, where this entire function basically just
  // converts a pair of integers (significand, binaryExponent) into
  // another pair of integers (base10Significand, base10Power) and
  // then at the end converts that pair of integers into a textual
  // representation.

  // But if we do the conversion here, we know we don't have more than
  // 8 digits, which eliminates the need for extra fussing to conditionally
  // support up to 9 digits later on.
  let digits = _intToEightDecimalDigits(base10Significand)
  buffer.storeBytes(
    of: digits + 0x3030303030303030,
    toByteOffset: firstDigit,
    as: UInt64.self)
  nextDigit &+= 8

  // Trim leading zero digits
  // (Little-endian assumption means that the _trailing_
  // zero digits in `digits` are the most-significant ones.)
  firstDigit &+= digits.trailingZeroBitCount / 8

  // Step 6: Compute one more decimal digit if necessary

  // Because of how we computed base10Power above, either the digits
  // so far are in the rounding interval and we're essentially done,
  // or we need exactly one more digit.

  // Intuitively: `intervalWidth` is the width of the rounding interval,
  // `delta` is the distance from the value we've computed so far
  // to our current target value.
  if intervalWidth > delta {
    // Step 6, Case A: We have a value that lies within the rounding
    // interval, and it's a multiple of 10^base10Power.  As described
    // earlier, there can only be one such. This is the fast case.

    // Experimentally, ~39% of all inputs get here.

    // We don't need any more digits, just trim trailing zeros
    let trailingZeros = digits.leadingZeroBitCount / 8
    nextDigit &-= trailingZeros
    base10Power &+= trailingZeros
    // No final-digit adjustment is needed
  } else {
    // Step 6, Case B: We need another digit
    // Experimentally, ~61% of all inputs get here.

    // Adjust `delta` to measure the distance from the value we've computed so
    // far to the actual float value (instead of the upper endpoint).  This
    // requires subtracting 1/2 ULP from both delta and intervalWidth:
    let halfUlp = scaledBinaryUlp &* 5
    // This is `delta -= ulp/2; delta *= 10` reordered to reduce precision loss
    delta = delta &* 10 &- halfUlp
    // Adjust `intervalWidth` to reflect only the lower part of the rounding interval
    intervalWidth = intervalWidth &* 10 &- halfUlp

    // Multiplying by 10 above scaled us from 10^base10Power
    // to 10^(base10Power - 1), which gives us the extra digit we want:
    var digit = UInt8(truncatingIfNeeded: delta &>> fractionBits)
    delta &= fractionMask

    // `delta` is now the scaled distance from our current base-10
    // value to the actual binary target value. `digit` is the closest
    // base-10 digit _below_ our target, `digit + 1` is the closest
    // _above_ the target.  We need to choose the best one.

    // Note: Using arithmetic to combine booleans as below is considerably
    // faster than the obvious if-else chain.  Swift booleans are consistently
    // represented as single-byte 0 or 1 values.
    let epsilon = UInt64(64)
    // If `delta` is definitively > 1/2, then `digit` is further than `digit + 1`
    let greater = unsafe unsafeBitCast(delta > (oneHalf &+ epsilon), to: UInt8.self)
    // They might be equally close (ignoring precision loss in the earlier calculations),
    // in which case we want `digit + 1` if that would make the result even.
    let greaterOrEqual = unsafe unsafeBitCast(delta > (oneHalf &- epsilon), to: UInt8.self)
    // If the lower value is outside of the interval, we definitely want the upper one:
    // (Note: in the regular symmetric interval case, this is redundant with `greater`)
    let outOfInterval = unsafe unsafeBitCast(delta > intervalWidth, to: UInt8.self)
    // Select the upper value if any of these three conditions holds:
    digit &+= greater | (greaterOrEqual & digit) | outOfInterval

    // Write out the extra digit
    buffer.storeBytes(
      of: digit &+ 0x30,
      toByteOffset: nextDigit,
      as: UInt8.self)
    nextDigit &+= 1
    base10Power &-= 1
  }

  // Step 7: Finish formatting
  let isBoundary = (f.significandBitPattern == 0)
  let forceExponential = ((binaryExponent > 1) || (binaryExponent == 1 && !isBoundary))
  return _finishFormatting(
    buffer: &buffer,
    sign: f.sign,
    firstDigit: firstDigit,
    nextDigit: nextDigit,
    forceExponential: forceExponential,
    base10Power: base10Power)
}

// ================================================================
//
// Float64
//
// ================================================================

// Support Legacy ABI on top of new implementation
@_silgen_name("swift_float64ToString")
@usableFromInline
internal func _float64ToStringImpl(
  _ textBuffer: UnsafeMutablePointer<UTF8.CodeUnit>,
  _ bufferLength: UInt,
  _ value: Float64,
  _ debug: Bool
) -> UInt64 {
  // Code below works with raw memory.
  var buffer = unsafe MutableSpan<UTF8.CodeUnit>(
    _unchecked: textBuffer,
    count: Int(bufferLength))
  for i in 0..<Int(bufferLength) {
    unsafe buffer[unchecked: i] = 0x30
  }
  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)
}

// Convert a Float64 to an optimal ASCII representation.
// See the Float32 implementation for extensive comments
// explaining the algorithm.
// 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)`
internal func _Float64ToASCII(
  value f: Float64,
  buffer utf8Buffer: inout MutableSpan<UTF8.CodeUnit>
) -> Range<Int> {
  assert(utf8Buffer.count >= 32)
  var buffer = utf8Buffer.mutableBytes

  let significandBitCount = Double.significandBitCount + 1 // 53
  let exponentBias = Int(bitPattern: Float64._exponentBias) - 1 + significandBitCount
  let binaryExponent: Int
  let significand: Double.RawSignificand

  // Step 1: Handle the special cases, decompose the input

  if  f.exponentBitPattern == Float64._infinityExponent {
    if (f.isInfinite) {
      return _infinity(buffer: &buffer, sign: f.sign)
    } else { // f.isNaN
      let quietBit = f.significandBitPattern & Float64._quietNaNMask
      let payload = f.significandBitPattern & (Float64._significandMask &>> 2)
      return nan_details(
        buffer: &buffer,
        sign: f.sign,
        quiet: quietBit != 0,
        payloadHigh: 0,
        payloadLow: payload
      )
    }
  } else if f.exponentBitPattern == 0 {
    if (f.isZero) {
      return _zero(buffer: &buffer, sign: f.sign)
    } else { // f.isSubnormal
      significand = f.significandBitPattern
      binaryExponent = 1 &- exponentBias
    }
  } else {
    // Normal
    let implicitBit = Double.RawSignificand(1) << Double.significandBitCount
    significand = f.significandBitPattern &+ implicitBit
    binaryExponent = Int(f.exponentBitPattern) &- exponentBias
  }

  // Step 2: Compute the base 10 exponent to match the rounding interval

  let maybeLogThreeQuarters = (f.significandBitPattern == 0
                                 ? Int64(-536607787)
                                 : Int64(0))
  let rawBase10Power = Int64(binaryExponent) &* 1292913986 &+ maybeLogThreeQuarters
  let roundedBase10Power = (rawBase10Power &+ 0xffffffff) &>> 32
  var base10Power = Int(truncatingIfNeeded: roundedBase10Power)

  // Step 3: Look up power-of-10 scale factor

  var powerOfTen: _UInt128 = 0
  let powerOfTenExponent = _powerOf10_Binary64(p: 0 &- base10Power,
                                               significand: &powerOfTen)

  // Step 4: Scale the interval (with rounding)

  // After scaling, we'll use a 54.74 fixed-point format
  let integerBits = significandBitCount + 1
  let fractionBits = 128 - integerBits
  let fractionMask = (_UInt128(1) << fractionBits) - 1
  let oneHalf = _UInt128(1) << (fractionBits - 1)

  let narrowInterval = f.significandBitPattern & 1

  // Scale one binary ULP
  let align = integerBits &- (binaryExponent &+ powerOfTenExponent)
  var scaledBinaryUlp = powerOfTen &>> align
  scaledBinaryUlp &+= _UInt128(bitPattern: _Int128(3 &- 6 &* Int(narrowInterval)))

  // Scale the upper limit
  let low = _UInt128(powerOfTen._low) &* _UInt128(significand)
  let high = _UInt128(powerOfTen._high) &* _UInt128(significand)
  let upperBound = ((low &>> align)
                      &+ (high &<< (64 - align))
                      &+ (scaledBinaryUlp &>> 1))

  // Compute the width of the scaled rounding interval
  var intervalWidth = scaledBinaryUlp
  if f.significandBitPattern == 0 {
    // In the power-of-two case, the interval is 3/4 ULP.
    intervalWidth &-= intervalWidth &>> 2
  }

  // Step 5: Emit most of the decimal digits into the destination buffer

  var firstDigit = 6
  var nextDigit = firstDigit

  let base10Significand = UInt64(truncatingIfNeeded: upperBound >> fractionBits)
  var delta = upperBound & fractionMask

  // Convert the digits we have so far and write them out...
  let digits = _intToSixteenDecimalDigits(base10Significand)
  let zeros = _UInt128(
    _low: 0x3030303030303030,
    _high: 0x3030303030303030)
  buffer.storeBytes(
    of: digits &+ zeros,
    toByteOffset: firstDigit,
    as: _UInt128.self)
  nextDigit &+= 16

  // Trim leading zero digits
  firstDigit &+= digits.trailingZeroBitCount / 8

  // Step 6: Compute one more decimal digit if necessary

  if intervalWidth > delta {
    // Step 6, Case A: We have a value that lies within the interval
    // We don't need any more digits, we only need to trim trailing zeros
    let trailingZeros = digits.leadingZeroBitCount / 8
    nextDigit &-= trailingZeros
    base10Power &+= trailingZeros
    // No final-digit adjustment is needed
  } else {
    // Step 6, Case B: We need another digit
    let halfUlp = scaledBinaryUlp &* 5
    delta = delta &* 10 &- halfUlp
    intervalWidth = intervalWidth &* 10 &- halfUlp

    var digit = UInt8(truncatingIfNeeded: delta >> fractionBits)
    delta &= fractionMask

    let epsilon = _UInt128(64)
    let greater = unsafe unsafeBitCast(delta > (oneHalf &+ epsilon), to: UInt8.self)
    let greaterOrEqual = unsafe unsafeBitCast(delta > (oneHalf &- epsilon), to: UInt8.self)
    let outOfInterval = unsafe unsafeBitCast(delta > intervalWidth, to: UInt8.self)
    digit &+= greater | (greaterOrEqual & digit) | outOfInterval

    buffer.storeBytes(
      of: digit &+ 0x30,
      toByteOffset: nextDigit,
      as: UInt8.self)
    nextDigit &+= 1
    base10Power &-= 1
  }

  // Step 7: Finish formatting
  let isBoundary = (f.significandBitPattern == 0)
  let forceExponential =
    ((binaryExponent > 1)
       || (binaryExponent == 1 && !isBoundary))
  return _finishFormatting(
    buffer: &buffer,
    sign: f.sign,
    firstDigit: firstDigit,
    nextDigit: nextDigit,
    forceExponential: forceExponential,
    base10Power: base10Power)
}


// ================================================================
//
// 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")
@usableFromInline
internal func _float80ToStringImpl(
  _ textBuffer: UnsafeMutablePointer<UTF8.CodeUnit>,
  _ bufferLength: UInt,
  _ value: Float80,
  _ debug: Bool
) -> UInt64 {
  // Code below works with raw memory.
  var buffer = unsafe MutableSpan<UTF8.CodeUnit>(
    _unchecked: textBuffer,
    count: Int(bufferLength))
  for i in 0..<Int(bufferLength) {
    unsafe buffer[unchecked: i] = 0x30
  }
  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)
}

// Convert a Float80 to an optimal ASCII representation.
// See the Float32 implementation for extensive comments
// explaining 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 64 bytes long and must be pre-filled
// with "0" characters, e.g., via
// `InlineArray<64,UTF8.CodeUnit>(repeating:0x30)`
internal func _Float80ToASCII(
  value f: Float80,
  buffer utf8Buffer: inout MutableSpan<UTF8.CodeUnit>
) -> Range<Int> {
  assert(utf8Buffer.count >= 64)
  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.

  // Float80.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 significandBits = Float80.significandBitCount + 1
  let exponentBias = Int(bitPattern: Float80._exponentBias) - 1 + significandBits // 16382 + 64
  let isBoundary = f.significandBitPattern == 0

  if f.exponentBitPattern == Float80._infinityExponent { // 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 & (Float80._quietNaNMask - 1)
    switch selector {
    case 0: // ∞ or sNaN on 287, invalid on 387
      fallthrough
    case 1: // Pseudo-NaN: sNaN on 287, invalid on 387
      // We treat invalid/indefinite patterns as plain qNaN
      fallthrough
    case 3: // sNaN on 287, qNaN on 387
      return nan_details(
        buffer: &buffer,
        sign: f.sign,
        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)
      }
    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 (J-bit set)
    binaryExponent = Int(f.exponentBitPattern) &- exponentBias
    significand = rawSignificand
  } else {
    // Invalid: non-zero exponent with J-bit clear ("unnormal")
    return nan_details(
      buffer: &buffer,
      sign: .plus,
      quiet: true,
      payloadHigh: 0,
      payloadLow: 0)
  }

  return _Float128Backend(
    utf8Buffer: &utf8Buffer,
    isBoundary: isBoundary,
    binaryExponent: binaryExponent,
    significand: _UInt128(significand),
    sign: f.sign)
}
#endif

// ================================================================
//
// Float128
//
// ================================================================

// This requires only the most basic `Float128` type.  In particular,
// it requires absolutely no arithmetic on that type.

/*
 
@available(macOS 15.0, watchOS 11.0, *)
internal func _Float128ToASCII(
  value f: Float128,
  buffer utf8Buffer: inout MutableSpan<UTF8.CodeUnit>
) -> Range<Int> {
  assert(utf8Buffer.count >= 64)
  var buffer = utf8Buffer.mutableBytes

  let significandBitCount = Float128.significandBitCount + 1 // 113
  let exponentBias = Int(bitPattern: Float128._exponentBias) - 1 + significandBitCount // 16382 + 113
  let binaryExponent: Int
  let significand: UInt128

  // Step 1: Handle the special cases, decompose the input
  if  f.exponentBitPattern == Float128._infinityExponent {
    if (f.isInfinite) {
      return _infinity(buffer: &buffer, sign: f.sign)
    } else { // f.isNaN
      let quietBit = f.significandBitPattern & Float128._quietNaNMask
      let payload = f.significandBitPattern & (Float128._significandMask &>> 2)
      return nan_details(
        buffer: &buffer,
        sign: f.sign,
        quiet: quietBit != 0,
        payloadHigh: payload._high,
        payloadLow: payload._low
      )
    }
  } else if f.exponentBitPattern == 0 {
    if (f.isZero) {
      return _zero(buffer: &buffer, sign: f.sign)
    } else { // f.isSubnormal
      significand = f.significandBitPattern
      binaryExponent = 1 &- exponentBias
    }
  } else {
    // Normal
    let implicitBit = UInt128(1) << Float128.significandBitCount
    significand = f.significandBitPattern &+ implicitBit
    binaryExponent = Int(f.exponentBitPattern) &- exponentBias
  }

  return _Float128Backend(
    utf8Buffer: &utf8Buffer,
    isBoundary: f.significandBitPattern == 0,
    binaryExponent: binaryExponent,
    significand: _UInt128(significand),
    sign: f.sign)
}

*/

// ================================================================
//
// General conversion supporting any format up to Float128
//
// ================================================================

// Core conversion logic for any format with <= 113 bit significand
// and <= 15 bit exponent.  This can be used for Float128, Float80, as
// well as any smaller format.  (Of course, the tuned implementations
// for Float64, Float32, and Float16 above are much faster.)
fileprivate func _Float128Backend(
  utf8Buffer: inout MutableSpan<UTF8.CodeUnit>,
  isBoundary: Bool,
  binaryExponent: Int,
  significand: _UInt128,
  sign: FloatingPointSign
) -> Range<Int> {
  assert(utf8Buffer.count >= 56)
  var buffer = utf8Buffer.mutableBytes

  // Step 2: Compute the base 10 exponent to match the rounding interval

  let maybeLogThreeQuarters = (isBoundary
                                 ? Int64(-536607787)
                                 : Int64(0))
  let rawBase10Power = Int64(binaryExponent) &* 1292913986 &+ maybeLogThreeQuarters
  let roundedBase10Power = (rawBase10Power &+ 0xffffffff) &>> 32
  var base10Power = Int(truncatingIfNeeded: roundedBase10Power)

  // Step 3: Look up power-of-10 scale factor

  var powerOfTen = _UInt256()
  let powerOfTenExponent = _powerOf10_Binary128(
    p: 0 &- base10Power,
    significand: &powerOfTen)

  // Step 4: Scale the interval (with rounding)

  // Fixed-point format: 114 integer bits, 142 fraction bits = 256 total
  let significandBits = 113 // Sufficient for Float128
  let integerBits = significandBits + 1
  let fractionMask = _UInt256(
    high: 0,
    UInt64.max >> (integerBits - 64),
    UInt64.max,
    low: UInt64.max)
  let oneHalf = _UInt256(
    high: 0,
    UInt64(1) << (128 - 1 - integerBits),
    0,
    low: 0)

  let narrowInterval = significand & 1

  // Scale one binary ULP: scaledBinaryUlp = powerOfTen >> align
  let align = integerBits &- (binaryExponent &+ powerOfTenExponent)
  var scaledBinaryUlp = powerOfTen &>> align
  // Widen or narrow the ULP for even/odd significand
  let adjustment = 4 &- 8 &* Int(narrowInterval)
  scaledBinaryUlp &+= adjustment

  // Scale the upper limit
  var rawProduct = powerOfTen
  rawProduct.multiply(by: significand, lsr: align)
  let upperBound = rawProduct &+ (scaledBinaryUlp &>> 1)

  // Compute the width of the scaled rounding interval
  var intervalWidth = scaledBinaryUlp
  if isBoundary {
    // Power-of-two: interval is 3/4 ULP
    intervalWidth = intervalWidth &- (intervalWidth &>> 2)
  }

  // Step 5: Emit most of the decimal digits into the destination buffer

  // Float80 requires 1-21 digits for round-trip correctness; initial
  // scaling will produce a value of up to 4/3 * 2^64 ≈ 2.45e19 (0-20
  // digits, regarding 65 bits) at this point.
  // Float128 requires 1-36 digits; initial scaling can produce a value
  // up to 4/3 * 2^113 ≈ 1.38e34 (0-35 digits, requiring 114 bits).
  // Larger than Float128 would likely require more than 256 bit arithmetic.

  var firstDigit = 8
  var nextDigit = firstDigit

  // Extract the integer part
  let base10Significand = upperBound.high >> (128 - integerBits)
  var delta = upperBound & fractionMask

  let zeros8 = UInt64(0x3030303030303030)
  let zeros16 = _UInt128(
    _low: 0x3030303030303030,
    _high: 0x3030303030303030)
  let TenE16 = UInt64(10000000000000000)

  // As noted above, Float128 has at most 35 digits at this point.
  // Top part is at most 1.38e18, which fits in 64 bits
  let reciprocal = _UInt128(UInt64(0xe69594bec44de15b)) // ≈ 2^117 / 10^16
  let hi19low = (_UInt128(base10Significand._low) * reciprocal) >> 64
  let hi19extended = (_UInt128(base10Significand._high) * reciprocal + hi19low) >> 53
  var hi19 = UInt64(truncatingIfNeeded: hi19extended)
  var lo16 = UInt64(truncatingIfNeeded: base10Significand - _UInt128(hi19) * _UInt128(TenE16))
  // Correct the two values above...
  if lo16 >= TenE16 {
    lo16 -= TenE16
    hi19 += 1
  }

  // The above is a faster version of this div/rem pair, which otherwise
  // accounts for >80% of the total runtime of this function:
  // let hi19 = UInt64(truncatingIfNeeded: base10Significand / _UInt128(TenE16))
  // let lo16 = UInt64(truncatingIfNeeded: base10Significand % _UInt128(TenE16))

  let hi3 = UInt32(truncatingIfNeeded: hi19 / TenE16)
  let digitsHi = _intToEightDecimalDigits(hi3)
  buffer.storeBytes(
    of: digitsHi &+ zeros8,
    toByteOffset: firstDigit,
    as: UInt64.self)
  nextDigit &+= 8

  let mid16 = UInt64(truncatingIfNeeded: hi19 % TenE16)
  let digitsMid = _intToSixteenDecimalDigits(mid16)
  buffer.storeBytes(
    of: digitsMid &+ zeros16,
    toByteOffset: nextDigit,
    as: _UInt128.self)
  nextDigit &+= 16

  let digitsLo = _intToSixteenDecimalDigits(lo16)
  buffer.storeBytes(
    of: digitsLo &+ zeros16,
    toByteOffset: nextDigit,
    as: _UInt128.self)
  nextDigit &+= 16

  // Trim leading zero digits
  if digitsHi != 0 {
    firstDigit &+= digitsHi.trailingZeroBitCount / 8
  } else if digitsMid != 0 {
    firstDigit &+= 8 + digitsMid.trailingZeroBitCount / 8
  } else {
    firstDigit &+= 24 &+ digitsLo.trailingZeroBitCount / 8
  }

  // Step 6: Compute one more decimal digit if necessary

  if intervalWidth > delta {
    // Step 6, Case A: Value lies within the interval.
    // Trim trailing zeros.
    let trailingZeros: Int
    if digitsLo != 0 {
      trailingZeros = digitsLo.leadingZeroBitCount / 8
    } else if digitsMid != 0 {
      trailingZeros = 16 &+ digitsMid.leadingZeroBitCount / 8
    } else {
      trailingZeros = 32 &+ digitsHi.leadingZeroBitCount / 8
    }
    nextDigit &-= trailingZeros
    base10Power &+= trailingZeros
  } else {
    // Step 6, Case B: We need another digit
    var halfUlp = scaledBinaryUlp
    halfUlp.multiply(by: UInt32(5))
    // delta = delta * 10 - halfUlp
    delta.multiply(by: UInt32(10))
    delta = delta &- halfUlp
    // intervalWidth = intervalWidth * 10 - halfUlp
    intervalWidth.multiply(by: UInt32(10))
    intervalWidth = intervalWidth &- halfUlp

    // Extract the next digit
    var digit = UInt8(truncatingIfNeeded: delta.high >> (128 - integerBits))
    delta = delta & fractionMask

    let epsilon = _UInt256(UInt64(256))
    let greater = unsafe unsafeBitCast(
      delta > (oneHalf &+ epsilon), to: UInt8.self)
    let greaterOrEqual = unsafe unsafeBitCast(
      delta > (oneHalf &- epsilon), to: UInt8.self)
    let outOfInterval = unsafe unsafeBitCast(
      delta > intervalWidth, to: UInt8.self)
    digit &+= greater | (greaterOrEqual & digit) | outOfInterval

    buffer.storeBytes(
      of: digit &+ 0x30,
      toByteOffset: nextDigit,
      as: UInt8.self)
    nextDigit &+= 1
    base10Power &-= 1
  }

  // Step 7: Finish formatting
  let forceExponential =
    ((binaryExponent > 1)
       || (binaryExponent == 1 && !isBoundary))
  return _finishFormatting(
    buffer: &buffer,
    sign: sign,
    firstDigit: firstDigit,
    nextDigit: nextDigit,
    forceExponential: forceExponential,
    base10Power: base10Power)
}

// ================================================================
//
// 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 6 unused bytes at the beginning of `buffer` before
// `firstDigit` and that all unused bytes are filled with `"0"` (0x30)
// characters.

fileprivate func _finishFormatting(
  buffer: inout MutableRawSpan,
  sign: FloatingPointSign,
  firstDigit: Int,
  nextDigit: Int,
  forceExponential: Bool,
  base10Power: 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
  var p = base10Power &+ digitCount &- 1
  if p < -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
    let powerSign: UInt8
    if p < 0 {
      powerSign = 0x2d // "-"
      p = 0 &- p
    } else {
      powerSign = 0x2b // "+"
    }
    unsafe buffer.storeBytes(
      of: powerSign,
      toUncheckedByteOffset: nextDigit,
      as: UInt8.self)
    nextDigit &+= 1
    if p > 99 {
      if p > 999 {
        let d = asciiDigitTable[p / 100]
        unsafe buffer.storeBytes(
          of: d,
          toUncheckedByteOffset: nextDigit,
          as: UInt16.self)
        nextDigit &+= 2
      } else {
        let d = 0x30 &+ UInt8(truncatingIfNeeded: (p / 100))
        unsafe buffer.storeBytes(
          of: d,
          toUncheckedByteOffset: nextDigit,
          as: UInt8.self)
        nextDigit &+= 1
      }
      p = p % 100
    }
    // For historical reasons, exponents are always at least 2 digits
    let d = unsafe asciiDigitTable[unchecked: p]
    buffer.storeBytes(
      of: d,
      toByteOffset: nextDigit,
      as: UInt16.self)
    nextDigit &+= 2
  } else if p < 0 {
    // "-0.000123456789"
    // We need up to 6 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 &+= p &- 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 p &+ 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...(p &+ 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 &+ p &+ 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 &+ p &+ 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
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
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
  }
}

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
  }
}

fileprivate let hexdigits: _InlineArray<16, UInt8> = [
  0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
  0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66
]

// Emit up to 16 hex digits, pruning leading zeros
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
  }
}

// Emit exactly 16 hex digits
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
  }
}

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 decimal digits in a
// UInt64.  Assuming little-endian, you can add 0x3030303030303030 and
// store the result directly to memory to get an ASCII decimal.
//
// 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/
@inline(__always)
fileprivate func _intToEightDecimalDigits(_ 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)

  return singles
}

@inline(__always)
fileprivate func _intToSixteenDecimalDigits(_ n: UInt64) -> _UInt128 {
  let hi8 = n / 100000000
  let lo8 = n &- hi8 * 100000000

  return _UInt128(
    _low: _intToEightDecimalDigits(UInt32(truncatingIfNeeded: hi8)),
    _high: _intToEightDecimalDigits(UInt32(truncatingIfNeeded: lo8))
  )
}

// ================================================================
//
// Arithmetic Helpers
//
// ================================================================

// Custom 256-bit unsigned integer type, with various arithmetic
// helpers as methods.
// Used by 80- and 128-bit floating point formatting logic above...
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
  }

  init(_ low: _UInt128) {
      self.high = 0
      self.low = low
  }

  init(_ low: UInt64) {
      self.high = 0
      self.low = _UInt128(low)
  }

  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)
  }

  // Multiply by a UInt128,
  // shift 384-bit intermediate value right by the specified amount, and
  // return the low-order 256 bits
  mutating func multiply(by rhs: _UInt128, lsr: Int) {
    // Break into 64-bit components
    let rhs1 = _UInt128(rhs._high)
    let rhs0 = _UInt128(rhs._low)
    let lhs3 = _UInt128(high._high)
    let lhs2 = _UInt128(high._low)
    let lhs1 = _UInt128(low._high)
    let lhs0 = _UInt128(low._low)

    // Compute cross-products, propagate carries
    let w0 = rhs0 * lhs0
    let w1a = rhs0 * lhs1
    let w1b = rhs1 * lhs0
    let w1 = (_UInt128(w0._high)
                + _UInt128(w1a._low) + _UInt128(w1b._low))
    let w2a = rhs0 * lhs2
    let w2b = rhs1 * lhs1
    let w2 = (_UInt128(w1a._high) + _UInt128(w1b._high) + _UInt128(w1._high)
                + _UInt128(w2a._low) + _UInt128(w2b._low))
    let w3a = rhs0 * lhs3
    let w3b = rhs1 * lhs2
    let w3 = (_UInt128(w2a._high) + _UInt128(w2b._high) + _UInt128(w2._high)
                + _UInt128(w3a._low) + _UInt128(w3b._low))
    let w4 = (_UInt128(w3a._high) + _UInt128(w3b._high) + _UInt128(w3._high)
                + rhs1 * lhs3)

    // Discard overhead bits
    var x0 = w0._low
    var x1 = w1._low
    var x2 = w2._low
    var x3 = w3._low
    var x4 = w4._low
    var x5 = w4._high

    // Shift right by a multiple of 64 bits
    var shift = lsr
    while shift >= 64 {
      x0 = x1
      x1 = x2
      x2 = x3
      x3 = x4
      x4 = x5
      x5 = 0
      shift -= 64
    }

    // Shift the remainder
    if shift > 0 {
      x0 = (x0 >> shift) + (x1 << (64 - shift))
      x1 = (x1 >> shift) + (x2 << (64 - shift))
      x2 = (x2 >> shift) + (x3 << (64 - shift))
      x3 = (x3 >> shift) + (x4 << (64 - shift))
//    x4 = (x4 >> shift) + (x5 << (64 - shift))
//    x5 = (x5 >> shift)
    }

    // Keep the low-order 256 bits
    low = _UInt128(_low: x0, _high: x1)
    high = _UInt128(_low: x2, _high: x3)
  }

  mutating func multiplyHigh(by rhs: _UInt128) {
    multiply(by: rhs, lsr: 128)
  }

  // TODO: Reduce code size here either by rephrasing it
  // as a nested for loop with additional bookkeeping, or
  // by recursively using the 256-bit x 128-bit multiplies above.
  mutating func multiplyHigh(by rhs: _UInt256) {
    var current = _UInt128(low._low) * _UInt128(rhs.low._low)

    current = _UInt128(current._high)
    var t = _UInt128(low._low) &* _UInt128(rhs.low._high)
    current += _UInt128(t._low)
    var next = _UInt128(t._high)
    t = _UInt128(low._high) &* _UInt128(rhs.low._low)
    current += _UInt128(t._low)
    next += _UInt128(t._high)

    current = next + _UInt128(current._high)
    t = _UInt128(low._low) &* _UInt128(rhs.high._low)
    current += _UInt128(t._low)
    next = _UInt128(t._high)
    t = _UInt128(low._high) &* _UInt128(rhs.low._high)
    current += _UInt128(t._low)
    next += _UInt128(t._high)
    t = _UInt128(high._low) &* _UInt128(rhs.low._low)
    current += _UInt128(t._low)
    next += _UInt128(t._high)

    current = next + _UInt128(current._high)
    t = _UInt128(low._low) &* _UInt128(rhs.high._high)
    current += _UInt128(t._low)
    next = _UInt128(t._high)
    t = _UInt128(low._high) &* _UInt128(rhs.high._low)
    current += _UInt128(t._low)
    next += _UInt128(t._high)
    t = _UInt128(high._low) &* _UInt128(rhs.low._high)
    current += _UInt128(t._low)
    next += _UInt128(t._high)
    t = _UInt128(high._high) &* _UInt128(rhs.low._low)
    current += _UInt128(t._low)
    next += _UInt128(t._high)

    current = next + _UInt128(current._high)
    t = _UInt128(low._high) &* _UInt128(rhs.high._high)
    current += _UInt128(t._low)
    next = _UInt128(t._high)
    t = _UInt128(high._low) &* _UInt128(rhs.high._low)
    current += _UInt128(t._low)
    next += _UInt128(t._high)
    t = _UInt128(high._high) &* _UInt128(rhs.low._high)
    current += _UInt128(t._low)
    next += _UInt128(t._high)
    let newlow = current._low

    current = next + _UInt128(current._high)
    t = _UInt128(high._low) &* _UInt128(rhs.high._high)
    current += _UInt128(t._low)
    next = _UInt128(t._high)
    t = _UInt128(high._high) &* _UInt128(rhs.high._low)
    current += _UInt128(t._low)
    next += _UInt128(t._high)
    low = _UInt128(_low: newlow, _high: current._low)

    current = next + _UInt128(current._high)
    high = current + _UInt128(high._high) &* _UInt128(rhs.high._high)
  }

  static func &- (lhs: _UInt256, rhs: _UInt256) -> _UInt256 {
    var t = _UInt128(lhs.low._low) &+ _UInt128(~rhs.low._low) &+ 1
    let w0 = t._low
    t = _UInt128(t._high) &+ _UInt128(lhs.low._high) &+ _UInt128(~rhs.low._high)
    let w1 = t._low
    t = _UInt128(t._high) &+ _UInt128(lhs.high._low) &+ _UInt128(~rhs.high._low)
    let w2 = t._low
    let w3 = t._high &+ lhs.high._high &+ ~rhs.high._high
    return _UInt256(high: w3, w2, w1, low: w0)
  }

  static func < (lhs: _UInt256, rhs: _UInt256) -> Bool {
    return (lhs.high < rhs.high)
      || (lhs.high == rhs.high
            && lhs.low < rhs.low)
  }

  static func > (lhs: _UInt256, rhs: _UInt256) -> Bool {
    return rhs < lhs
  }

  static func &+ (lhs: _UInt256, rhs: _UInt256) -> _UInt256 {
    let low = lhs.low &+ rhs.low
    let carry: _UInt128 = low < lhs.low ? 1 : 0
    let high = lhs.high &+ rhs.high &+ carry
    return _UInt256(high: high, low: low)
  }

  // Add a signed int...
  static func &+= (lhs: inout _UInt256, rhs: Int) {
    if rhs >= 0 {
      let r = _UInt128(UInt(rhs))
      let newlow = lhs.low &+ r
      let carry: _UInt128 = newlow < lhs.low ? 1 : 0
      lhs.high &+= carry
      lhs.low = newlow
    } else {
      let magnitude = _UInt128(UInt(bitPattern: 0 &- rhs))
      let borrow: _UInt128 = lhs.low < magnitude ? 1 : 0
      lhs.low &-= magnitude
      lhs.high &-= borrow
    }
  }

  static func & (lhs: _UInt256, rhs: _UInt256) -> _UInt256 {
    return _UInt256(high: lhs.high & rhs.high, low: lhs.low & rhs.low)
  }

  // Right-shift by 0..<128 bits
  static func &>> (lhs: _UInt256, rhs: Int) -> _UInt256 {
    var hi = lhs.high._high
    var midhi = lhs.high._low
    var midlo = lhs.low._high
    var lo = lhs.low._low
    // Shift by whole 64-bit words first
    if rhs >= 64 {
      lo = midlo
      midlo = midhi
      midhi = hi
      hi = 0
    }
    // Shift by remaining bits
    let r = rhs & 63
    if r > 0 {
      let anti = 64 &- r
      lo = (lo >> r) | (midlo << anti)
      midlo = (midlo >> r) | (midhi << anti)
      midhi = (midhi >> r) | (hi << anti)
      hi = hi >> r
    }
    return _UInt256(high: hi, midhi, midlo, low: lo)
  }
}

// ================================================================
//
// Powers of 10
//
// ================================================================

@inline(__always)
fileprivate func _powerOf10_Binary64(
  p: Int,
  significand: inout _UInt128
) -> Int {
  if p >= 0 && p <= 55 {
    let upper64 = powersOf10_Exact128[p &* 2 &+ 1]
    let lower64 = powersOf10_Exact128[p &* 2]
    significand = _UInt128(_low: lower64, _high: upper64)
    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 {
    significand = _UInt128(_low: baseLow, _high: baseHigh)
    return baseExponent
  } else {
    let extra = powersOf10_Exact128[extraPower &* 2 &+ 1]
    let high = (_UInt128(truncatingIfNeeded:baseHigh)
                &* _UInt128(truncatingIfNeeded:extra))
    let low = (_UInt128(truncatingIfNeeded:baseLow)
               &* _UInt128(truncatingIfNeeded:extra)
               &+ _UInt128(UInt64.max >> 1))
    significand = high &+ (low &>> 64)
    return baseExponent &+ binaryExponentFor10ToThe(extraPower)
  }
}

// To keep the supporting table size to a minimum, this
// picks a value from each of three tables and multiplies
// them together to get a sufficiently-accurate final result.
fileprivate func _powerOf10_Binary128(
  p: Int,
  significand: inout _UInt256
) -> Int {
  if p >= 0 && p <= 55 {
    let exactLow = powersOf10_Exact128[p * 2]
    let exactHigh = powersOf10_Exact128[p * 2 + 1]
    significand = _UInt256(high: exactHigh, exactLow, 0, low: 0)
    return binaryExponentFor10ToThe(p)
  }

  let index = p + 5376
  let finePower = index % 56
  let mediumPower = index % 896 - finePower
  let coarsePower = p - mediumPower - finePower

  let coarseIndex = (index / 896) * 4
  let mediumIndex = (mediumPower / 56) * 4
  let fineIndex = finePower * 2

  significand = _UInt256(
    high: powersOf10_Binary128_Coarse[coarseIndex + 3],
    powersOf10_Binary128_Coarse[coarseIndex + 2],
    powersOf10_Binary128_Coarse[coarseIndex + 1],
    low: powersOf10_Binary128_Coarse[coarseIndex + 0])

  let medium = _UInt256(
    high: powersOf10_Binary128_Medium[mediumIndex + 3],
    powersOf10_Binary128_Medium[mediumIndex + 2],
    powersOf10_Binary128_Medium[mediumIndex + 1],
    low: powersOf10_Binary128_Medium[mediumIndex + 0])
  significand.multiplyHigh(by: medium)

  let fine = _UInt128(
    _low: powersOf10_Exact128[fineIndex],
    _high: powersOf10_Exact128[fineIndex + 1])
  significand.multiplyHigh(by: fine)

  let coarseExponent = binaryExponentFor10ToThe(coarsePower)
  let mediumExponent = binaryExponentFor10ToThe(mediumPower)
  let fineExponent = binaryExponentFor10ToThe(finePower)

  return coarseExponent + mediumExponent + fineExponent
}

@inline(__always)
fileprivate func binaryExponentFor10ToThe(_ p: Int) -> Int {
  return Int(((Int64(p) &* 55732705) >> 24) &+ 1)
}

// 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 powersOf10_Binary64 table below says that:
//
//   0x0.95fe7e07c91efafa3931b850df08e738 x 2^-1328
//
// is the result of rounding down the exact binary value of 10^-400 to
// 128 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.

// ## powersOf10_Float32

// Complete table suitable for Float32 conversion.

fileprivate let powersOf10_Float32: _InlineArray<_, UInt64> = [
  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
  0x8000000000000000, // x 2^1 == 10^0 exactly
  0xa000000000000000, // x 2^4 == 10^1 exactly
  0xc800000000000000, // x 2^7 == 10^2 exactly
  0xfa00000000000000, // x 2^10 == 10^3 exactly
  0x9c40000000000000, // x 2^14 == 10^4 exactly
  0xc350000000000000, // x 2^17 == 10^5 exactly
  0xf424000000000000, // x 2^20 == 10^6 exactly
  0x9896800000000000, // x 2^24 == 10^7 exactly
  0xbebc200000000000, // x 2^27 == 10^8 exactly
  0xee6b280000000000, // x 2^30 == 10^9 exactly
  0x9502f90000000000, // x 2^34 == 10^10 exactly
  0xba43b74000000000, // x 2^37 == 10^11 exactly
  0xe8d4a51000000000, // x 2^40 == 10^12 exactly
  0x9184e72a00000000, // x 2^44 == 10^13 exactly
  0xb5e620f480000000, // x 2^47 == 10^14 exactly
  0xe35fa931a0000000, // x 2^50 == 10^15 exactly
  0x8e1bc9bf04000000, // x 2^54 == 10^16 exactly
  0xb1a2bc2ec5000000, // x 2^57 == 10^17 exactly
  0xde0b6b3a76400000, // x 2^60 == 10^18 exactly
  0x8ac7230489e80000, // x 2^64 == 10^19 exactly
  0xad78ebc5ac620000, // x 2^67 == 10^20 exactly
  0xd8d726b7177a8000, // x 2^70 == 10^21 exactly
  0x878678326eac9000, // x 2^74 == 10^22 exactly
  0xa968163f0a57b400, // x 2^77 == 10^23 exactly
  0xd3c21bcecceda100, // x 2^80 == 10^24 exactly
  0x84595161401484a0, // x 2^84 == 10^25 exactly
  0xa56fa5b99019a5c8, // x 2^87 == 10^26 exactly
  0xcecb8f27f4200f3a, // x 2^90 == 10^27 exactly
  0x813f3978f8940984, // x 2^94 ~= 10^28
  0xa18f07d736b90be5, // x 2^97 ~= 10^29
  0xc9f2c9cd04674ede, // x 2^100 ~= 10^30
  0xfc6f7c4045812296, // x 2^103 ~= 10^31
  0x9dc5ada82b70b59d, // x 2^107 ~= 10^32
  0xc5371912364ce305, // x 2^110 ~= 10^33
  0xf684df56c3e01bc6, // x 2^113 ~= 10^34
  0x9a130b963a6c115c, // x 2^117 ~= 10^35
  0xc097ce7bc90715b3, // x 2^120 ~= 10^36
  0xf0bdc21abb48db20, // x 2^123 ~= 10^37
  0x96769950b50d88f4, // x 2^127 ~= 10^38
  0xbc143fa4e250eb31, // x 2^130 ~= 10^39
  0xeb194f8e1ae525fd, // x 2^133 ~= 10^40
  0x92efd1b8d0cf37be, // x 2^137 ~= 10^41
  0xb7abc627050305ad, // x 2^140 ~= 10^42
  0xe596b7b0c643c719, // x 2^143 ~= 10^43
  0x8f7e32ce7bea5c6f, // x 2^147 ~= 10^44
  0xb35dbf821ae4f38b, // x 2^150 ~= 10^45
  0xe0352f62a19e306e, // x 2^153 ~= 10^46
  0x8c213d9da502de45, // x 2^157 ~= 10^47
  0xaf298d050e4395d6, // x 2^160 ~= 10^48
  0xdaf3f04651d47b4c, // x 2^163 ~= 10^49
  0x88d8762bf324cd0f, // x 2^167 ~= 10^50
]

// ## powersOf10_Exact128

// 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 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 support.

// Table size: 896 bytes
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
]

// ## powersOf10_Binary64

// 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
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
]

// ## powersOf10_Binary128

// Needed by 80-bit formatter.  This is sufficient precision
// to support a future Float128 implementation if anyone's interested.

// Total table size: 512 + 384 = 896 bytes 

// Table size: 512 bytes
fileprivate let powersOf10_Binary128_Medium: _InlineArray<_, UInt64> = [
  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
]

// Table size: 12 * 32 = 384 bytes
fileprivate let powersOf10_Binary128_Coarse: _InlineArray<_, UInt64> = [
  0x9f855c9864e639d5, 0x257261546827afab, 0xeddb8bd5b1dd7ed8, 0x9f2f658cea3b24bc, // x 2^-17858 ~= 10^-5376
  0xdadd9645f360cb51, 0xf290163350ecb3eb, 0xa8edffdccfe4db4b, 0xd9167ab0c1965798, // x 2^-14882 ~= 10^-4480
  0x982b64e953ac4e27, 0x45efb05f20cf48b3, 0x4b4de34e0ebc3e06, 0x9406af8f83fd6265, // x 2^-11905 ~= 10^-3584
  0x42032f9f971bfc07, 0x9fb576046ab35018, 0x474b3cb1fe1d6a7f, 0xc9dea80d6283a34c, // x 2^-8929 ~= 10^-2688
  0xb62593291c768919, 0xc098e6ed0bfbd6f6, 0x6c83ad1260ff20f4, 0x89a63ba4c497b50e, // x 2^-5952 ~= 10^-1792
  0x276e3f0f67f0553b, 0x00de73d9d5be6974, 0x6d4aa5b50bb5dc0d, 0xbbb7ef38bb827f2d, // x 2^-2976 ~= 10^-896
  0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x8000000000000000, // x 2^1 == 10^0 exactly
  0xf48b51375df06e86, 0x412fe9e72afd355e, 0x870a8d87239d8f35, 0xae8f2b2ce3d5dbe9, // x 2^2977 ~= 10^896
  0xbf34ff7963028cd9, 0xc20578fa3851488b, 0x2d4070f33b21ab7b, 0xee0ddd84924ab88c, // x 2^5953 ~= 10^1792
  0x8cd036553f38a1e8, 0x5e997e9f45d7897d, 0xf09e780bcc8238d9, 0xa2528e74eaf101fc, // x 2^8930 ~= 10^2688
  0x134ca67a679b84ae, 0x8909e424a112a3cd, 0x95aa118ec1d08317, 0xdd5dc8a2bf27f3f7, // x 2^11906 ~= 10^3584
  0x0f7740145246fb8f, 0x186ef2c39acb4103, 0x888c9ab2fc5b3437, 0x96f18b1742aad751, // x 2^14883 ~= 10^4480
]
