blob: 0a9edfd7b22330586dc29290f9609ca1a040c36d [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 the 'e', 'f', 'g' floating-point formats.
// It is closely following the corresponding implementation in
// strconv/ftoa.go, but modified and simplified for big.Float.
// Algorithm:
// 1) convert Float to multiprecision decimal
// 2) round to desired precision
// 3) read digits out and format
package big
import "strconv"
// TODO(gri) Consider moving sign into decimal - could make the signatures below cleaner.
// bigFtoa formats a float for the %e, %E, %f, %g, and %G formats.
func (f *Float) bigFtoa(buf []byte, fmt byte, prec int) []byte {
if debugFloat && f.IsInf() {
panic("non-finite float")
}
// 1) convert Float to multiprecision decimal
var mant nat
if f.form == finite {
mant = f.mant
}
var d decimal
d.init(mant, int(f.exp)-f.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, f.neg, d)
case 'f':
return fmtF(buf, prec, f.neg, 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, f.neg, d)
}
if prec > d.exp {
prec = len(d.mant)
}
return fmtF(buf, max(prec-d.exp, 0), f.neg, d)
}
// unknown format
return append(buf, '%', fmt)
}
// %e: -d.ddddde±dd
func fmtE(buf []byte, fmt byte, prec int, neg bool, d decimal) []byte {
// sign
if neg {
buf = append(buf, '-')
}
// 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, neg bool, d decimal) []byte {
// sign
if neg {
buf = append(buf, '-')
}
// 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
}
func min(x, y int) int {
if x < y {
return x
}
return y
}