| // 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<<expBits - 1) |
| mant := b & (1<<mantBits - 1) |
| if exp == 1<<expBits-1 { |
| if mant != 0 { |
| return append(dst, "NaN"...) |
| } |
| if neg { |
| return append(dst, "-Inf"...) |
| } |
| return append(dst, "+Inf"...) |
| } |
| if exp == 0 { |
| exp++ |
| } else { |
| mant |= 1 << mantBits |
| } |
| exp += bias |
| |
| // Pick off easy binary, hex formats. |
| if fmt == 'b' { |
| return fmtB(dst, neg, mant, exp-mantBits) |
| } |
| if fmt == 'x' || fmt == 'X' { |
| return fmtX(dst, prec, fmt, neg, mant, exp, mantBits) |
| } |
| |
| // Pick off zero. |
| if mant == 0 { |
| return fmtEFG(dst, neg, nil, 0, 0, prec, fmt, prec < 0) |
| } |
| |
| // Negative precision means "only as much as needed to be exact." |
| if prec < 0 { |
| // Use fast unrounded scaling. |
| var buf [32]byte |
| s := 64 - bits.Len64(mant) |
| m := mant << s |
| e := exp - s |
| d, p := shortFloat[F](m, e-mantBits) |
| dp, nd := setDigits(buf[:], d, p, numDigits(d)) |
| // Precision for shortest representation mode. |
| switch fmt { |
| case 'e', 'E': |
| prec = max(nd-1, 0) |
| case 'f': |
| prec = max(nd-dp, 0) |
| case 'g', 'G': |
| prec = nd |
| } |
| return fmtEFG(dst, neg, buf[:], dp, nd, prec, fmt, true) |
| } |
| |
| if optimize { |
| // Fixed number of digits. |
| digits := prec |
| switch fmt { |
| case 'f': |
| // %f precision specifies digits after the decimal point. |
| // Estimate an upper bound on the total number of digits needed. |
| // ftoaFixed will shorten as needed according to prec. |
| if exp >= 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<<expBits - 1) |
| mant := b & (1<<mantBits - 1) |
| if exp == 1<<expBits-1 { |
| if mant != 0 { |
| return append(dst, "NaN"...) |
| } |
| if neg { |
| return append(dst, "-Inf"...) |
| } |
| return append(dst, "+Inf"...) |
| } |
| if exp == 0 { |
| exp++ |
| } else { |
| mant |= 1 << mantBits |
| } |
| exp += bias |
| |
| // Pick off easy binary, hex formats. |
| if fmt == 'b' { |
| return fmtB(dst, neg, mant, exp-mantBits) |
| } |
| if fmt == 'x' || fmt == 'X' { |
| return fmtX(dst, prec, fmt, neg, mant, exp, mantBits) |
| } |
| |
| // Pick off zero. |
| if mant == 0 { |
| return fmtEFG(dst, neg, nil, 0, 0, prec, fmt, prec < 0) |
| } |
| |
| // Negative precision means "only as much as needed to be exact." |
| if prec < 0 { |
| // Use fast unrounded scaling. |
| var buf [32]byte |
| s := 64 - bits.Len64(mant) |
| m := mant << s |
| e := exp - s |
| d, p := shortFloat[F](m, e-mantBits) |
| dp, nd := setDigits(buf[:], d, p, numDigits(d)) |
| // Precision for shortest representation mode. |
| switch fmt { |
| case 'e', 'E': |
| prec = max(nd-1, 0) |
| case 'f': |
| prec = max(nd-dp, 0) |
| case 'g', 'G': |
| prec = nd |
| } |
| return fmtEFG(dst, neg, buf[:], dp, nd, prec, fmt, true) |
| } |
| |
| if optimize { |
| // Fixed number of digits. |
| digits := prec |
| switch fmt { |
| case 'f': |
| // %f precision specifies digits after the decimal point. |
| // Estimate an upper bound on the total number of digits needed. |
| // ftoaFixed will shorten as needed according to prec. |
| if exp >= 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 |
| } |