// Copyright 2009 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. // Binary to decimal floating point conversion. // Algorithm: // 1) store mantissa in multiprecision decimal // 2) shift decimal by exponent // 3) read digits out & format package strconv import ( "math/bits" "unsafe" ) const ( lowerhex = "0123456789abcdef" upperhex = "0123456789ABCDEF" ) const ( float32MantBits = 23 float32ExpBits = 8 float32Bias = -127 float32MinExp = -189 float64MantBits = 52 float64ExpBits = 11 float64Bias = -1023 float64MinExp = -1085 ) // FormatFloat converts the floating-point number f to a string, // according to the format fmt and precision prec. It rounds the // result assuming that the original was obtained from a floating-point // value of bitSize bits (32 for float32, 64 for float64). // // The format fmt is one of // - 'b' (-ddddp±ddd, a binary exponent), // - 'e' (-d.dddde±dd, a decimal exponent), // - 'E' (-d.ddddE±dd, a decimal exponent), // - 'f' (-ddd.dddd, no exponent), // - 'g' ('e' for large exponents, 'f' otherwise), // - 'G' ('E' for large exponents, 'f' otherwise), // - 'x' (-0xd.ddddp±ddd, a hexadecimal fraction and binary exponent), or // - 'X' (-0Xd.ddddP±ddd, a hexadecimal fraction and binary exponent). // // The precision prec controls the number of digits (excluding the exponent) // printed by the 'e', 'E', 'f', 'g', 'G', 'x', and 'X' formats. // For 'e', 'E', 'f', 'x', and 'X', it is the number of digits after the decimal point. // For 'g' and 'G' it is the maximum number of significant digits (trailing // zeros are removed). // The special precision -1 uses the smallest number of digits // necessary such that ParseFloat will return f exactly. // The exponent is written as a decimal integer; // for all formats other than 'b', it will be at least two digits. func FormatFloat(f float64, fmt byte, prec, bitSize int) string { if bitSize == 32 { return string(ftoa32(make([]byte, 0, max(prec+4, 24)), float32(f), fmt, prec)) } if bitSize == 64 { return string(ftoa64(make([]byte, 0, max(prec+4, 24)), f, fmt, prec)) } panic("strconv: illegal FormatFloat bitSize") } // AppendFloat appends the string form of the floating-point number f, // as generated by [FormatFloat], to dst and returns the extended buffer. func AppendFloat(dst []byte, f float64, fmt byte, prec, bitSize int) []byte { if bitSize == 32 { return ftoa32(dst, float32(f), fmt, prec) } if bitSize == 64 { return ftoa64(dst, f, fmt, prec) } panic("strconv: illegal AppendFloat bitSize") } // TODO(rsc): This should be ftoa[F float32 | float64](dst []byte, val F, ...), // but due to some bad interaction between inlining, escape analysis, and generic functions, // the result appears to escape dst to the heap with -l=4, which breaks // TestAbstractOriginSanity. See go.dev/issue/79547. // For now we make two manual specializations ftoa32 and ftoa64 instead. func ftoa32(dst []byte, val float32, fmt byte, prec int) []byte { type F = float32 var b uint64 var expBits, mantBits, bias int // parameterized constants switch 8 * unsafe.Sizeof(val) { case 32: b = uint64(float32bits(float32(val))) expBits = float32ExpBits mantBits = float32MantBits bias = float32Bias case 64: b = float64bits(float64(val)) expBits = float64ExpBits mantBits = float64MantBits bias = float64Bias } neg := b>>(expBits+mantBits) != 0 exp := int(b>>mantBits) & (1<= 0 { digits = 1 + log10Pow2(1+exp) + prec } else { digits = 1 + prec - log10Pow2(-exp) } case 'e', 'E': digits++ case 'g', 'G': if prec == 0 { prec = 1 } digits = prec default: // Invalid mode. digits = 1 } if digits <= 18 { // digits <= 0 happens for %f on very small numbers // and means that we're guaranteed to print all zeros. var buf [24]byte var dp, nd int if digits > 0 { s := 64 - bits.Len64(mant) m := mant << s e := exp - s d, p := fixedWidthFloat(m, e-mantBits, digits, prec, fmt) if d != 0 { dp, nd = setDigits(buf[:], d, p, numDigits(d)) } } return fmtEFG(dst, neg, buf[:], dp, nd, prec, fmt, false) } } // Slow bignum case. Only for non-shortest results. d := new(decimal) d.Assign(mant) d.Shift(exp - mantBits) switch fmt { case 'e', 'E': d.Round(prec + 1) case 'f': d.Round(d.dp + prec) case 'g', 'G': if prec == 0 { prec = 1 } d.Round(prec) } return fmtEFG(dst, neg, d.d[:], d.dp, d.nd, prec, fmt, false) } func ftoa64(dst []byte, val float64, fmt byte, prec int) []byte { type F = float64 var b uint64 var expBits, mantBits, bias int // parameterized constants switch 8 * unsafe.Sizeof(val) { case 32: b = uint64(float32bits(float32(val))) expBits = float32ExpBits mantBits = float32MantBits bias = float32Bias case 64: b = float64bits(float64(val)) expBits = float64ExpBits mantBits = float64MantBits bias = float64Bias } neg := b>>(expBits+mantBits) != 0 exp := int(b>>mantBits) & (1<= 0 { digits = 1 + log10Pow2(1+exp) + prec } else { digits = 1 + prec - log10Pow2(-exp) } case 'e', 'E': digits++ case 'g', 'G': if prec == 0 { prec = 1 } digits = prec default: // Invalid mode. digits = 1 } if digits <= 18 { // digits <= 0 happens for %f on very small numbers // and means that we're guaranteed to print all zeros. var buf [24]byte var dp, nd int if digits > 0 { s := 64 - bits.Len64(mant) m := mant << s e := exp - s d, p := fixedWidthFloat(m, e-mantBits, digits, prec, fmt) if d != 0 { dp, nd = setDigits(buf[:], d, p, numDigits(d)) } } return fmtEFG(dst, neg, buf[:], dp, nd, prec, fmt, false) } } // Slow bignum case. Only for non-shortest results. d := new(decimal) d.Assign(mant) d.Shift(exp - mantBits) switch fmt { case 'e', 'E': d.Round(prec + 1) case 'f': d.Round(d.dp + prec) case 'g', 'G': if prec == 0 { prec = 1 } d.Round(prec) } return fmtEFG(dst, neg, d.d[:], d.dp, d.nd, prec, fmt, false) } func fmtEFG(dst []byte, neg bool, s []byte, dp, nd, prec int, fmt byte, shortest bool) []byte { if fmt == 'g' || fmt == 'G' { // trailing fractional zeros in 'e' form will be trimmed. eprec := prec if eprec > nd && nd >= dp { eprec = nd } // %e is used if the exponent from the conversion // is less than -4 or greater than or equal to the precision. // if precision was the shortest possible, use precision 6 for this decision. if shortest { eprec = 6 } exp := dp - 1 if exp < -4 || exp >= eprec { if prec > nd { prec = nd } prec-- fmt = fmt + 'e' - 'g' } else { if prec > dp { prec = nd } prec = max(prec-dp, 0) fmt = 'f' } } switch fmt { case 'e', 'E': // %e: -d.ddddde±dd // sign if neg { dst = append(dst, '-') } // first digit ch := byte('0') if nd != 0 { ch = s[0] } dst = append(dst, ch) // .moredigits if prec > 0 { dst = append(dst, '.') i := 1 m := min(nd, prec+1) if i < m { dst = append(dst, s[i:m]...) i = m } for range prec + 1 - i { dst = append(dst, '0') } } // e± dst = append(dst, fmt) exp := dp - 1 if nd == 0 { // special case: 0 has exponent 0 exp = 0 } if exp < 0 { ch = '-' exp = -exp } else { ch = '+' } dst = append(dst, ch) // dd or ddd switch { case exp < 10: dst = append(dst, '0', byte(exp)+'0') case exp < 100: dst = append(dst, byte(exp/10)+'0', byte(exp%10)+'0') default: dst = append(dst, byte(exp/100)+'0', byte(exp/10)%10+'0', byte(exp%10)+'0') } return dst case 'f': // %f: -ddddddd.ddddd // sign if neg { dst = append(dst, '-') } // integer, padded with zeros as needed. if dp > 0 { m := min(nd, dp) for _, c := range s[:m] { dst = append(dst, c) } for range dp - m { dst = append(dst, '0') } } else { dst = append(dst, '0') } // fraction if prec > 0 { dst = append(dst, '.') lz := min(prec, max(0, -dp)) // leading zeros off := dp + lz m := min(prec-lz, max(0, nd-off)) // middle digits tz := max(0, prec-lz-m) // trailing zeros for range lz { dst = append(dst, '0') } for i := range m { dst = append(dst, s[off+i]) } for range tz { dst = append(dst, '0') } } return dst } // unknown format return append(dst, '%', fmt) } // %b: -ddddddddp±ddd func fmtB(dst []byte, neg bool, mant uint64, exp int) []byte { if neg { dst = append(dst, '-') } dst = AppendUint(dst, mant, 10) dst = append(dst, 'p') if exp >= 0 { dst = append(dst, '+') } dst = AppendInt(dst, int64(exp), 10) return dst } // %x: -0x1.yyyyyyyyp±ddd or -0x0p+0. (y is hex digit, d is decimal digit) func fmtX(dst []byte, prec int, fmt byte, neg bool, mant uint64, exp, mantBits int) []byte { if mant == 0 { exp = 0 } // Shift digits so leading 1 (if any) is at bit 1<<60. // TODO: Is this the right way to handle subnormals? mant <<= 60 - mantBits for mant != 0 && mant&(1<<60) == 0 { mant <<= 1 exp-- } // Round if requested. if prec >= 0 && prec < 15 { shift := uint(prec * 4) extra := (mant << shift) & (1<<60 - 1) mant >>= 60 - shift if extra|(mant&1) > 1<<59 { mant++ } mant <<= 60 - shift if mant&(1<<61) != 0 { // Wrapped around. mant >>= 1 exp++ } } hex := lowerhex if fmt == 'X' { hex = upperhex } // sign, 0x, leading digit if neg { dst = append(dst, '-') } dst = append(dst, '0', fmt, '0'+byte((mant>>60)&1)) // .fraction mant <<= 4 // remove leading 0 or 1 if prec < 0 && mant != 0 { dst = append(dst, '.') for mant != 0 { dst = append(dst, hex[(mant>>60)&15]) mant <<= 4 } } else if prec > 0 { dst = append(dst, '.') for i := 0; i < prec; i++ { dst = append(dst, hex[(mant>>60)&15]) mant <<= 4 } } // p± ch := byte('P') if fmt == lower(fmt) { ch = 'p' } dst = append(dst, ch) if exp < 0 { ch = '-' exp = -exp } else { ch = '+' } dst = append(dst, ch) // dd or ddd or dddd switch { case exp < 100: dst = append(dst, byte(exp/10)+'0', byte(exp%10)+'0') case exp < 1000: dst = append(dst, byte(exp/100)+'0', byte((exp/10)%10)+'0', byte(exp%10)+'0') default: dst = append(dst, byte(exp/1000)+'0', byte(exp/100)%10+'0', byte((exp/10)%10)+'0', byte(exp%10)+'0') } return dst }