Home | History | Annotate | Download | only in big
      1 // Copyright 2015 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 // This file implements Float-to-string conversion functions.
      6 // It is closely following the corresponding implementation
      7 // in strconv/ftoa.go, but modified and simplified for Float.
      8 
      9 package big
     10 
     11 import (
     12 	"fmt"
     13 	"strconv"
     14 	"strings"
     15 )
     16 
     17 // Text converts the floating-point number x to a string according
     18 // to the given format and precision prec. The format is one of:
     19 //
     20 //	'e'	-d.ddddedd, decimal exponent, at least two (possibly 0) exponent digits
     21 //	'E'	-d.ddddEdd, decimal exponent, at least two (possibly 0) exponent digits
     22 //	'f'	-ddddd.dddd, no exponent
     23 //	'g'	like 'e' for large exponents, like 'f' otherwise
     24 //	'G'	like 'E' for large exponents, like 'f' otherwise
     25 //	'b'	-ddddddpdd, binary exponent
     26 //	'p'	-0x.dddpdd, binary exponent, hexadecimal mantissa
     27 //
     28 // For the binary exponent formats, the mantissa is printed in normalized form:
     29 //
     30 //	'b'	decimal integer mantissa using x.Prec() bits, or -0
     31 //	'p'	hexadecimal fraction with 0.5 <= 0.mantissa < 1.0, or -0
     32 //
     33 // If format is a different character, Text returns a "%" followed by the
     34 // unrecognized format character.
     35 //
     36 // The precision prec controls the number of digits (excluding the exponent)
     37 // printed by the 'e', 'E', 'f', 'g', and 'G' formats. For 'e', 'E', and 'f'
     38 // it is the number of digits after the decimal point. For 'g' and 'G' it is
     39 // the total number of digits. A negative precision selects the smallest
     40 // number of digits necessary to identify the value x uniquely.
     41 // The prec value is ignored for the 'b' or 'p' format.
     42 //
     43 // BUG(gri) Float.Text does not accept negative precisions (issue #10991).
     44 func (x *Float) Text(format byte, prec int) string {
     45 	const extra = 10 // TODO(gri) determine a good/better value here
     46 	return string(x.Append(make([]byte, 0, prec+extra), format, prec))
     47 }
     48 
     49 // String formats x like x.Text('g', 10).
     50 func (x *Float) String() string {
     51 	return x.Text('g', 10)
     52 }
     53 
     54 // Append appends to buf the string form of the floating-point number x,
     55 // as generated by x.Text, and returns the extended buffer.
     56 func (x *Float) Append(buf []byte, fmt byte, prec int) []byte {
     57 	// sign
     58 	if x.neg {
     59 		buf = append(buf, '-')
     60 	}
     61 
     62 	// Inf
     63 	if x.form == inf {
     64 		if !x.neg {
     65 			buf = append(buf, '+')
     66 		}
     67 		return append(buf, "Inf"...)
     68 	}
     69 
     70 	// pick off easy formats
     71 	switch fmt {
     72 	case 'b':
     73 		return x.fmtB(buf)
     74 	case 'p':
     75 		return x.fmtP(buf)
     76 	}
     77 
     78 	// Algorithm:
     79 	//   1) convert Float to multiprecision decimal
     80 	//   2) round to desired precision
     81 	//   3) read digits out and format
     82 
     83 	// 1) convert Float to multiprecision decimal
     84 	var d decimal // == 0.0
     85 	if x.form == finite {
     86 		d.init(x.mant, int(x.exp)-x.mant.bitLen())
     87 	}
     88 
     89 	// 2) round to desired precision
     90 	shortest := false
     91 	if prec < 0 {
     92 		shortest = true
     93 		panic("unimplemented")
     94 		// TODO(gri) complete this
     95 		// roundShortest(&d, f.mant, int(f.exp))
     96 		// Precision for shortest representation mode.
     97 		switch fmt {
     98 		case 'e', 'E':
     99 			prec = len(d.mant) - 1
    100 		case 'f':
    101 			prec = max(len(d.mant)-d.exp, 0)
    102 		case 'g', 'G':
    103 			prec = len(d.mant)
    104 		}
    105 	} else {
    106 		// round appropriately
    107 		switch fmt {
    108 		case 'e', 'E':
    109 			// one digit before and number of digits after decimal point
    110 			d.round(1 + prec)
    111 		case 'f':
    112 			// number of digits before and after decimal point
    113 			d.round(d.exp + prec)
    114 		case 'g', 'G':
    115 			if prec == 0 {
    116 				prec = 1
    117 			}
    118 			d.round(prec)
    119 		}
    120 	}
    121 
    122 	// 3) read digits out and format
    123 	switch fmt {
    124 	case 'e', 'E':
    125 		return fmtE(buf, fmt, prec, d)
    126 	case 'f':
    127 		return fmtF(buf, prec, d)
    128 	case 'g', 'G':
    129 		// trim trailing fractional zeros in %e format
    130 		eprec := prec
    131 		if eprec > len(d.mant) && len(d.mant) >= d.exp {
    132 			eprec = len(d.mant)
    133 		}
    134 		// %e is used if the exponent from the conversion
    135 		// is less than -4 or greater than or equal to the precision.
    136 		// If precision was the shortest possible, use eprec = 6 for
    137 		// this decision.
    138 		if shortest {
    139 			eprec = 6
    140 		}
    141 		exp := d.exp - 1
    142 		if exp < -4 || exp >= eprec {
    143 			if prec > len(d.mant) {
    144 				prec = len(d.mant)
    145 			}
    146 			return fmtE(buf, fmt+'e'-'g', prec-1, d)
    147 		}
    148 		if prec > d.exp {
    149 			prec = len(d.mant)
    150 		}
    151 		return fmtF(buf, max(prec-d.exp, 0), d)
    152 	}
    153 
    154 	// unknown format
    155 	if x.neg {
    156 		buf = buf[:len(buf)-1] // sign was added prematurely - remove it again
    157 	}
    158 	return append(buf, '%', fmt)
    159 }
    160 
    161 // %e: d.dddddedd
    162 func fmtE(buf []byte, fmt byte, prec int, d decimal) []byte {
    163 	// first digit
    164 	ch := byte('0')
    165 	if len(d.mant) > 0 {
    166 		ch = d.mant[0]
    167 	}
    168 	buf = append(buf, ch)
    169 
    170 	// .moredigits
    171 	if prec > 0 {
    172 		buf = append(buf, '.')
    173 		i := 1
    174 		m := min(len(d.mant), prec+1)
    175 		if i < m {
    176 			buf = append(buf, d.mant[i:m]...)
    177 			i = m
    178 		}
    179 		for ; i <= prec; i++ {
    180 			buf = append(buf, '0')
    181 		}
    182 	}
    183 
    184 	// e
    185 	buf = append(buf, fmt)
    186 	var exp int64
    187 	if len(d.mant) > 0 {
    188 		exp = int64(d.exp) - 1 // -1 because first digit was printed before '.'
    189 	}
    190 	if exp < 0 {
    191 		ch = '-'
    192 		exp = -exp
    193 	} else {
    194 		ch = '+'
    195 	}
    196 	buf = append(buf, ch)
    197 
    198 	// dd...d
    199 	if exp < 10 {
    200 		buf = append(buf, '0') // at least 2 exponent digits
    201 	}
    202 	return strconv.AppendInt(buf, exp, 10)
    203 }
    204 
    205 // %f: ddddddd.ddddd
    206 func fmtF(buf []byte, prec int, d decimal) []byte {
    207 	// integer, padded with zeros as needed
    208 	if d.exp > 0 {
    209 		m := min(len(d.mant), d.exp)
    210 		buf = append(buf, d.mant[:m]...)
    211 		for ; m < d.exp; m++ {
    212 			buf = append(buf, '0')
    213 		}
    214 	} else {
    215 		buf = append(buf, '0')
    216 	}
    217 
    218 	// fraction
    219 	if prec > 0 {
    220 		buf = append(buf, '.')
    221 		for i := 0; i < prec; i++ {
    222 			ch := byte('0')
    223 			if j := d.exp + i; 0 <= j && j < len(d.mant) {
    224 				ch = d.mant[j]
    225 			}
    226 			buf = append(buf, ch)
    227 		}
    228 	}
    229 
    230 	return buf
    231 }
    232 
    233 // fmtB appends the string of x in the format mantissa "p" exponent
    234 // with a decimal mantissa and a binary exponent, or 0" if x is zero,
    235 // and returns the extended buffer.
    236 // The mantissa is normalized such that is uses x.Prec() bits in binary
    237 // representation.
    238 // The sign of x is ignored, and x must not be an Inf.
    239 func (x *Float) fmtB(buf []byte) []byte {
    240 	if x.form == zero {
    241 		return append(buf, '0')
    242 	}
    243 
    244 	if debugFloat && x.form != finite {
    245 		panic("non-finite float")
    246 	}
    247 	// x != 0
    248 
    249 	// adjust mantissa to use exactly x.prec bits
    250 	m := x.mant
    251 	switch w := uint32(len(x.mant)) * _W; {
    252 	case w < x.prec:
    253 		m = nat(nil).shl(m, uint(x.prec-w))
    254 	case w > x.prec:
    255 		m = nat(nil).shr(m, uint(w-x.prec))
    256 	}
    257 
    258 	buf = append(buf, m.decimalString()...)
    259 	buf = append(buf, 'p')
    260 	e := int64(x.exp) - int64(x.prec)
    261 	if e >= 0 {
    262 		buf = append(buf, '+')
    263 	}
    264 	return strconv.AppendInt(buf, e, 10)
    265 }
    266 
    267 // fmtP appends the string of x in the format 0x." mantissa "p" exponent
    268 // with a hexadecimal mantissa and a binary exponent, or 0" if x is zero,
    269 // ad returns the extended buffer.
    270 // The mantissa is normalized such that 0.5 <= 0.mantissa < 1.0.
    271 // The sign of x is ignored, and x must not be an Inf.
    272 func (x *Float) fmtP(buf []byte) []byte {
    273 	if x.form == zero {
    274 		return append(buf, '0')
    275 	}
    276 
    277 	if debugFloat && x.form != finite {
    278 		panic("non-finite float")
    279 	}
    280 	// x != 0
    281 
    282 	// remove trailing 0 words early
    283 	// (no need to convert to hex 0's and trim later)
    284 	m := x.mant
    285 	i := 0
    286 	for i < len(m) && m[i] == 0 {
    287 		i++
    288 	}
    289 	m = m[i:]
    290 
    291 	buf = append(buf, "0x."...)
    292 	buf = append(buf, strings.TrimRight(m.hexString(), "0")...)
    293 	buf = append(buf, 'p')
    294 	if x.exp >= 0 {
    295 		buf = append(buf, '+')
    296 	}
    297 	return strconv.AppendInt(buf, int64(x.exp), 10)
    298 }
    299 
    300 func min(x, y int) int {
    301 	if x < y {
    302 		return x
    303 	}
    304 	return y
    305 }
    306 
    307 // Format implements fmt.Formatter. It accepts all the regular
    308 // formats for floating-point numbers ('e', 'E', 'f', 'F', 'g',
    309 // 'G') as well as 'b', 'p', and 'v'. See (*Float).Text for the
    310 // interpretation of 'b' and 'p'. The 'v' format is handled like
    311 // 'g'.
    312 // Format also supports specification of the minimum precision
    313 // in digits, the output field width, as well as the format verbs
    314 // '+' and ' ' for sign control, '0' for space or zero padding,
    315 // and '-' for left or right justification. See the fmt package
    316 // for details.
    317 //
    318 // BUG(gri) A missing precision for the 'g' format, or a negative
    319 //          (via '*') precision is not yet supported. Instead the
    320 //          default precision (6) is used in that case (issue #10991).
    321 func (x *Float) Format(s fmt.State, format rune) {
    322 	prec, hasPrec := s.Precision()
    323 	if !hasPrec {
    324 		prec = 6 // default precision for 'e', 'f'
    325 	}
    326 
    327 	switch format {
    328 	case 'e', 'E', 'f', 'b', 'p':
    329 		// nothing to do
    330 	case 'F':
    331 		// (*Float).Text doesn't support 'F'; handle like 'f'
    332 		format = 'f'
    333 	case 'v':
    334 		// handle like 'g'
    335 		format = 'g'
    336 		fallthrough
    337 	case 'g', 'G':
    338 		if !hasPrec {
    339 			// TODO(gri) uncomment once (*Float).Text handles prec < 0
    340 			// prec = -1 // default precision for 'g', 'G'
    341 		}
    342 	default:
    343 		fmt.Fprintf(s, "%%!%c(*big.Float=%s)", format, x.String())
    344 		return
    345 	}
    346 	var buf []byte
    347 	buf = x.Append(buf, byte(format), prec)
    348 	if len(buf) == 0 {
    349 		buf = []byte("?") // should never happen, but don't crash
    350 	}
    351 	// len(buf) > 0
    352 
    353 	var sign string
    354 	switch {
    355 	case buf[0] == '-':
    356 		sign = "-"
    357 		buf = buf[1:]
    358 	case buf[0] == '+':
    359 		// +Inf
    360 		sign = "+"
    361 		if s.Flag(' ') {
    362 			sign = " "
    363 		}
    364 		buf = buf[1:]
    365 	case s.Flag('+'):
    366 		sign = "+"
    367 	case s.Flag(' '):
    368 		sign = " "
    369 	}
    370 
    371 	var padding int
    372 	if width, hasWidth := s.Width(); hasWidth && width > len(sign)+len(buf) {
    373 		padding = width - len(sign) - len(buf)
    374 	}
    375 
    376 	switch {
    377 	case s.Flag('0') && !x.IsInf():
    378 		// 0-padding on left
    379 		writeMultiple(s, sign, 1)
    380 		writeMultiple(s, "0", padding)
    381 		s.Write(buf)
    382 	case s.Flag('-'):
    383 		// padding on right
    384 		writeMultiple(s, sign, 1)
    385 		s.Write(buf)
    386 		writeMultiple(s, " ", padding)
    387 	default:
    388 		// padding on left
    389 		writeMultiple(s, " ", padding)
    390 		writeMultiple(s, sign, 1)
    391 		s.Write(buf)
    392 	}
    393 }
    394