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