Source File
ftoafixed.go
Belonging Package
internal/strconv
// Copyright 2025 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.package strconvimportvar uint64pow10 = [...]uint64{1, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,}// fixedFtoa formats a number of decimal digits of mant*(2^exp) into d,// where mant > 0 and 1 ≤ digits ≤ 18.// If fmt == 'f', digits is a conservative overestimate, and the final// number of digits is prec past the decimal point.func fixedFtoa( *decimalSlice, uint64, , , int, byte) {// The strategy here is to multiply (mant * 2^exp) by a power of 10// to make the resulting integer be the number of digits we want.//// Adams proved in the Ryu paper that 128-bit precision in the// power-of-10 constant is sufficient to produce correctly// rounded output for all float64s, up to 18 digits.// https://dl.acm.org/doi/10.1145/3192366.3192369//// TODO(rsc): The paper is not focused on, nor terribly clear about,// this fact in this context, and the proof seems too complicated.// Post a shorter, more direct proof and link to it here.if > 18 {panic("fixedFtoa called with digits > 18")}// Shift mantissa to have 64 bits,// so that the 192-bit product below will// have at least 63 bits in its top word.:= 64 - bits.Len64()<<=-=// We have f = mant * 2^exp ≥ 2^(63+exp)// and we want to multiply it by some 10^p// to make it have the number of digits plus one rounding bit://// 2 * 10^(digits-1) ≤ f * 10^p < ~2 * 10^digits//// The lower bound is required, but the upper bound is approximate:// we must not have too few digits, but we can round away extra ones.//// f * 10^p ≥ 2 * 10^(digits-1)// 10^p ≥ 2 * 10^(digits-1) / f [dividing by f]// p ≥ (log₁₀ 2) + (digits-1) - log₁₀ f [taking log₁₀]// p ≥ (log₁₀ 2) + (digits-1) - log₁₀ (mant * 2^exp) [expanding f]// p ≥ (log₁₀ 2) + (digits-1) - (log₁₀ 2) * (64 + exp) [mant < 2⁶⁴]// p ≥ (digits - 1) - (log₁₀ 2) * (63 + exp) [refactoring]//// Once we have p, we can compute the scaled value://// dm * 2^de = mant * 2^exp * 10^p// = mant * 2^exp * pow/2^128 * 2^exp2.// = (mant * pow/2^128) * 2^(exp+exp2).:= ( - 1) - mulLog10_2(63+), , := pow10()if ! {// This never happens due to the range of float32/float64 exponentpanic("fixedFtoa: pow10 out of range")}if -22 <= && < 0 {// Special case: Let q=-p. q is in [1,22]. We are dividing by 10^q// and the mantissa may be a multiple of 5^q (5^22 < 2^53),// in which case the division must be computed exactly and// recorded as exact for correct rounding. Our normal computation is://// dm = floor(mant * floor(10^p * 2^s))//// for some scaling shift s. To make this an exact division,// it suffices to change the inner floor to a ceil://// dm = floor(mant * ceil(10^p * 2^s))//// In the range of values we are using, the floor and ceil// cancel each other out and the high 64 bits of the product// come out exactly right.// (This is the same trick compilers use for division by constants.// See Hacker's Delight, 2nd ed., Chapter 10.).Lo++}, , := umul192(, ):= +// Check whether any bits have been truncated from dm.// If so, set dt != 0. If not, leave dt == 0 (meaning dm is exact).var uintswitch {default:// Most powers of 10 use a truncated constant,// meaning the result is also truncated.= 1case 0 <= && <= 55:// Small positive powers of 10 (up to 10⁵⁵) can be represented// precisely in a 128-bit mantissa (5⁵⁵ ≤ 2¹²⁸), so the only truncation// comes from discarding the low bits of the 192-bit product.//// TODO(rsc): The new proof mentioned above should also// prove that we can't have lo1 == 0 and lo0 != 0.// After proving that, drop computation and use of lo0 here.= bool2uint(| != 0)case -22 <= && < 0 && divisiblePow5(, -):// If the original mantissa was a multiple of 5^p,// the result is exact. (See comment above for pow.Lo++.)= 0}// The value we want to format is dm * 2^de, where de < 0.// Multply by 2^de by shifting, but leave one extra bit for rounding.// After the shift, the "integer part" of dm is dm>>1,// the "rounding bit" (the first fractional bit) is dm&1,// and the "truncated bit" (have any bits been discarded?) is dt.:= - - 1|= bool2uint(&(1<<-1) != 0)>>=// Set decimal point in eventual formatted digits,// so we can update it as we adjust the digits..dp = -// Trim excess digit if any, updating truncation and decimal point.// The << 1 is leaving room for the rounding bit.:= uint64pow10[] << 1if >= {var uint, = /10, uint(%10)|= bool2uint( != 0).dp++}// If this is %.*f we may have overestimated the digits needed.// Now that we know where the decimal point is,// trim to the actual number of digits, which is d.dp+prec.if == 'f' && != .dp+ {for > .dp+ {var uint, = /10, uint(%10)|= bool2uint( != 0)--}// Dropping those digits can create a new leftmost// non-zero digit, like if we are formatting %.1f and// convert 0.09 -> 0.1. Detect and adjust for that.if <= 0 {= 1.dp++}= uint64pow10[] << 1}// Round and shift away rounding bit.// We want to round up when// (a) the fractional part is > 0.5 (dm&1 != 0 and dt == 1)// (b) or the fractional part is ≥ 0.5 and the integer part is odd// (dm&1 != 0 and dm&2 != 0).// The bitwise expression encodes that logic.+= uint64(uint() & ( | uint()>>1) & 1)>>= 1if == >>1 {// 999... rolled over to 1000...= uint64pow10[-1].dp++}// Format digits into d.if != 0 {if formatBase10(.d[:], ) != 0 {panic("formatBase10")}.nd =for .d[.nd-1] == '0' {.nd--}}}
![]() |
The pages are generated with Golds v0.8.3-preview. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds. |