blob: 5c5f2cea460cc87a211e527d24f218f820c12a11 [file] [log] [blame]
// Copyright 2015 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.
// This file implements Float-to-string conversion functions.
// It is closely following the corresponding implementation
// in strconv/ftoa.go, but modified and simplified for Float.
package big
import (
"fmt"
"strconv"
"strings"
)
// Text converts the floating-point number x to a string according
// to the given format and precision prec. The format is one of:
//
// 'e' -d.dddde±dd, decimal exponent, at least two (possibly 0) exponent digits
// 'E' -d.ddddE±dd, decimal exponent, at least two (possibly 0) exponent digits
// 'f' -ddddd.dddd, no exponent
// 'g' like 'e' for large exponents, like 'f' otherwise
// 'G' like 'E' for large exponents, like 'f' otherwise
// 'b' -ddddddp±dd, binary exponent
// 'p' -0x.dddp±dd, binary exponent, hexadecimal mantissa
//
// For the binary exponent formats, the mantissa is printed in normalized form:
//
// 'b' decimal integer mantissa using x.Prec() bits, or -0
// 'p' hexadecimal fraction with 0.5 <= 0.mantissa < 1.0, or -0
//
// If format is a different character, Text returns a "%" followed by the
// unrecognized format character.
//
// The precision prec controls the number of digits (excluding the exponent)
// printed by the 'e', 'E', 'f', 'g', and 'G' formats. For 'e', 'E', and 'f'
// it is the number of digits after the decimal point. For 'g' and 'G' it is
// the total number of digits. A negative precision selects the smallest
// number of digits necessary to identify the value x uniquely.
// The prec value is ignored for the 'b' or 'p' format.
//
// BUG(gri) Float.Text does not accept negative precisions (issue #10991).
func (x *Float) Text(format byte, prec int) string {
const extra = 10 // TODO(gri) determine a good/better value here
return string(x.Append(make([]byte, 0, prec+extra), format, prec))
}
// String formats x like x.Text('g', 10).
func (x *Float) String() string {
return x.Text('g', 10)
}
// Append appends to buf the string form of the floating-point number x,
// as generated by x.Text, and returns the extended buffer.
func (x *Float) Append(buf []byte, fmt byte, prec int) []byte {
// sign
if x.neg {
buf = append(buf, '-')
}
// Inf
if x.form == inf {
if !x.neg {
buf = append(buf, '+')
}
return append(buf, "Inf"...)
}
// pick off easy formats
switch fmt {
case 'b':
return x.fmtB(buf)
case 'p':
return x.fmtP(buf)
}
// Algorithm:
// 1) convert Float to multiprecision decimal
// 2) round to desired precision
// 3) read digits out and format
// 1) convert Float to multiprecision decimal
var d decimal // == 0.0
if x.form == finite {
d.init(x.mant, int(x.exp)-x.mant.bitLen())
}
// 2) round to desired precision
shortest := false
if prec < 0 {
shortest = true
panic("unimplemented")
// TODO(gri) complete this
// roundShortest(&d, f.mant, int(f.exp))
// Precision for shortest representation mode.
switch fmt {
case 'e', 'E':
prec = len(d.mant) - 1
case 'f':
prec = max(len(d.mant)-d.exp, 0)
case 'g', 'G':
prec = len(d.mant)
}
} else {
// round appropriately
switch fmt {
case 'e', 'E':
// one digit before and number of digits after decimal point
d.round(1 + prec)
case 'f':
// number of digits before and after decimal point
d.round(d.exp + prec)
case 'g', 'G':
if prec == 0 {
prec = 1
}
d.round(prec)
}
}
// 3) read digits out and format
switch fmt {
case 'e', 'E':
return fmtE(buf, fmt, prec, d)
case 'f':
return fmtF(buf, prec, d)
case 'g', 'G':
// trim trailing fractional zeros in %e format
eprec := prec
if eprec > len(d.mant) && len(d.mant) >= d.exp {
eprec = len(d.mant)
}
// %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 eprec = 6 for
// this decision.
if shortest {
eprec = 6
}
exp := d.exp - 1
if exp < -4 || exp >= eprec {
if prec > len(d.mant) {
prec = len(d.mant)
}
return fmtE(buf, fmt+'e'-'g', prec-1, d)
}
if prec > d.exp {
prec = len(d.mant)
}
return fmtF(buf, max(prec-d.exp, 0), d)
}
// unknown format
if x.neg {
buf = buf[:len(buf)-1] // sign was added prematurely - remove it again
}
return append(buf, '%', fmt)
}
// %e: d.ddddde±dd
func fmtE(buf []byte, fmt byte, prec int, d decimal) []byte {
// first digit
ch := byte('0')
if len(d.mant) > 0 {
ch = d.mant[0]
}
buf = append(buf, ch)
// .moredigits
if prec > 0 {
buf = append(buf, '.')
i := 1
m := min(len(d.mant), prec+1)
if i < m {
buf = append(buf, d.mant[i:m]...)
i = m
}
for ; i <= prec; i++ {
buf = append(buf, '0')
}
}
// e±
buf = append(buf, fmt)
var exp int64
if len(d.mant) > 0 {
exp = int64(d.exp) - 1 // -1 because first digit was printed before '.'
}
if exp < 0 {
ch = '-'
exp = -exp
} else {
ch = '+'
}
buf = append(buf, ch)
// dd...d
if exp < 10 {
buf = append(buf, '0') // at least 2 exponent digits
}
return strconv.AppendInt(buf, exp, 10)
}
// %f: ddddddd.ddddd
func fmtF(buf []byte, prec int, d decimal) []byte {
// integer, padded with zeros as needed
if d.exp > 0 {
m := min(len(d.mant), d.exp)
buf = append(buf, d.mant[:m]...)
for ; m < d.exp; m++ {
buf = append(buf, '0')
}
} else {
buf = append(buf, '0')
}
// fraction
if prec > 0 {
buf = append(buf, '.')
for i := 0; i < prec; i++ {
ch := byte('0')
if j := d.exp + i; 0 <= j && j < len(d.mant) {
ch = d.mant[j]
}
buf = append(buf, ch)
}
}
return buf
}
// fmtB appends the string of x in the format mantissa "p" exponent
// with a decimal mantissa and a binary exponent, or 0" if x is zero,
// and returns the extended buffer.
// The mantissa is normalized such that is uses x.Prec() bits in binary
// representation.
// The sign of x is ignored, and x must not be an Inf.
func (x *Float) fmtB(buf []byte) []byte {
if x.form == zero {
return append(buf, '0')
}
if debugFloat && x.form != finite {
panic("non-finite float")
}
// x != 0
// adjust mantissa to use exactly x.prec bits
m := x.mant
switch w := uint32(len(x.mant)) * _W; {
case w < x.prec:
m = nat(nil).shl(m, uint(x.prec-w))
case w > x.prec:
m = nat(nil).shr(m, uint(w-x.prec))
}
buf = append(buf, m.decimalString()...)
buf = append(buf, 'p')
e := int64(x.exp) - int64(x.prec)
if e >= 0 {
buf = append(buf, '+')
}
return strconv.AppendInt(buf, e, 10)
}
// fmtP appends the string of x in the format 0x." mantissa "p" exponent
// with a hexadecimal mantissa and a binary exponent, or 0" if x is zero,
// ad returns the extended buffer.
// The mantissa is normalized such that 0.5 <= 0.mantissa < 1.0.
// The sign of x is ignored, and x must not be an Inf.
func (x *Float) fmtP(buf []byte) []byte {
if x.form == zero {
return append(buf, '0')
}
if debugFloat && x.form != finite {
panic("non-finite float")
}
// x != 0
// remove trailing 0 words early
// (no need to convert to hex 0's and trim later)
m := x.mant
i := 0
for i < len(m) && m[i] == 0 {
i++
}
m = m[i:]
buf = append(buf, "0x."...)
buf = append(buf, strings.TrimRight(m.hexString(), "0")...)
buf = append(buf, 'p')
if x.exp >= 0 {
buf = append(buf, '+')
}
return strconv.AppendInt(buf, int64(x.exp), 10)
}
func min(x, y int) int {
if x < y {
return x
}
return y
}
// Format implements fmt.Formatter. It accepts all the regular
// formats for floating-point numbers ('e', 'E', 'f', 'F', 'g',
// 'G') as well as 'b', 'p', and 'v'. See (*Float).Text for the
// interpretation of 'b' and 'p'. The 'v' format is handled like
// 'g'.
// Format also supports specification of the minimum precision
// in digits, the output field width, as well as the format verbs
// '+' and ' ' for sign control, '0' for space or zero padding,
// and '-' for left or right justification. See the fmt package
// for details.
//
// BUG(gri) A missing precision for the 'g' format, or a negative
// (via '*') precision is not yet supported. Instead the
// default precision (6) is used in that case (issue #10991).
func (x *Float) Format(s fmt.State, format rune) {
prec, hasPrec := s.Precision()
if !hasPrec {
prec = 6 // default precision for 'e', 'f'
}
switch format {
case 'e', 'E', 'f', 'b', 'p':
// nothing to do
case 'F':
// (*Float).Text doesn't support 'F'; handle like 'f'
format = 'f'
case 'v':
// handle like 'g'
format = 'g'
fallthrough
case 'g', 'G':
if !hasPrec {
// TODO(gri) uncomment once (*Float).Text handles prec < 0
// prec = -1 // default precision for 'g', 'G'
}
default:
fmt.Fprintf(s, "%%!%c(*big.Float=%s)", format, x.String())
return
}
var buf []byte
buf = x.Append(buf, byte(format), prec)
if len(buf) == 0 {
buf = []byte("?") // should never happen, but don't crash
}
// len(buf) > 0
var sign string
switch {
case buf[0] == '-':
sign = "-"
buf = buf[1:]
case buf[0] == '+':
// +Inf
sign = "+"
if s.Flag(' ') {
sign = " "
}
buf = buf[1:]
case s.Flag('+'):
sign = "+"
case s.Flag(' '):
sign = " "
}
var padding int
if width, hasWidth := s.Width(); hasWidth && width > len(sign)+len(buf) {
padding = width - len(sign) - len(buf)
}
switch {
case s.Flag('0') && !x.IsInf():
// 0-padding on left
writeMultiple(s, sign, 1)
writeMultiple(s, "0", padding)
s.Write(buf)
case s.Flag('-'):
// padding on right
writeMultiple(s, sign, 1)
s.Write(buf)
writeMultiple(s, " ", padding)
default:
// padding on left
writeMultiple(s, " ", padding)
writeMultiple(s, sign, 1)
s.Write(buf)
}
}