rsc.io/go@v0.0.0-20150416155037-e040fd465409/src/math/big/ftoa.go (about)

     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 the 'e', 'f', 'g' floating-point formats.
     6  // It is closely following the corresponding implementation in
     7  // strconv/ftoa.go, but modified and simplified for big.Float.
     8  
     9  // Algorithm:
    10  //   1) convert Float to multiprecision decimal
    11  //   2) round to desired precision
    12  //   3) read digits out and format
    13  
    14  package big
    15  
    16  import "strconv"
    17  
    18  // TODO(gri) Consider moving sign into decimal - could make the signatures below cleaner.
    19  
    20  // bigFtoa formats a float for the %e, %E, %f, %g, and %G formats.
    21  func (f *Float) bigFtoa(buf []byte, fmt byte, prec int) []byte {
    22  	if debugFloat && f.IsInf() {
    23  		panic("non-finite float")
    24  	}
    25  
    26  	// 1) convert Float to multiprecision decimal
    27  	var mant nat
    28  	if f.form == finite {
    29  		mant = f.mant
    30  	}
    31  	var d decimal
    32  	d.init(mant, int(f.exp)-f.mant.bitLen())
    33  
    34  	// 2) round to desired precision
    35  	shortest := false
    36  	if prec < 0 {
    37  		shortest = true
    38  		panic("unimplemented")
    39  		// TODO(gri) complete this
    40  		// roundShortest(&d, f.mant, int(f.exp))
    41  		// Precision for shortest representation mode.
    42  		switch fmt {
    43  		case 'e', 'E':
    44  			prec = len(d.mant) - 1
    45  		case 'f':
    46  			prec = max(len(d.mant)-d.exp, 0)
    47  		case 'g', 'G':
    48  			prec = len(d.mant)
    49  		}
    50  	} else {
    51  		// round appropriately
    52  		switch fmt {
    53  		case 'e', 'E':
    54  			// one digit before and number of digits after decimal point
    55  			d.round(1 + prec)
    56  		case 'f':
    57  			// number of digits before and after decimal point
    58  			d.round(d.exp + prec)
    59  		case 'g', 'G':
    60  			if prec == 0 {
    61  				prec = 1
    62  			}
    63  			d.round(prec)
    64  		}
    65  	}
    66  
    67  	// 3) read digits out and format
    68  	switch fmt {
    69  	case 'e', 'E':
    70  		return fmtE(buf, fmt, prec, f.neg, d)
    71  	case 'f':
    72  		return fmtF(buf, prec, f.neg, d)
    73  	case 'g', 'G':
    74  		// trim trailing fractional zeros in %e format
    75  		eprec := prec
    76  		if eprec > len(d.mant) && len(d.mant) >= d.exp {
    77  			eprec = len(d.mant)
    78  		}
    79  		// %e is used if the exponent from the conversion
    80  		// is less than -4 or greater than or equal to the precision.
    81  		// If precision was the shortest possible, use eprec = 6 for
    82  		// this decision.
    83  		if shortest {
    84  			eprec = 6
    85  		}
    86  		exp := d.exp - 1
    87  		if exp < -4 || exp >= eprec {
    88  			if prec > len(d.mant) {
    89  				prec = len(d.mant)
    90  			}
    91  			return fmtE(buf, fmt+'e'-'g', prec-1, f.neg, d)
    92  		}
    93  		if prec > d.exp {
    94  			prec = len(d.mant)
    95  		}
    96  		return fmtF(buf, max(prec-d.exp, 0), f.neg, d)
    97  	}
    98  
    99  	// unknown format
   100  	return append(buf, '%', fmt)
   101  }
   102  
   103  // %e: -d.ddddde±dd
   104  func fmtE(buf []byte, fmt byte, prec int, neg bool, d decimal) []byte {
   105  	// sign
   106  	if neg {
   107  		buf = append(buf, '-')
   108  	}
   109  
   110  	// first digit
   111  	ch := byte('0')
   112  	if len(d.mant) > 0 {
   113  		ch = d.mant[0]
   114  	}
   115  	buf = append(buf, ch)
   116  
   117  	// .moredigits
   118  	if prec > 0 {
   119  		buf = append(buf, '.')
   120  		i := 1
   121  		m := min(len(d.mant), prec+1)
   122  		if i < m {
   123  			buf = append(buf, d.mant[i:m]...)
   124  			i = m
   125  		}
   126  		for ; i <= prec; i++ {
   127  			buf = append(buf, '0')
   128  		}
   129  	}
   130  
   131  	// e±
   132  	buf = append(buf, fmt)
   133  	var exp int64
   134  	if len(d.mant) > 0 {
   135  		exp = int64(d.exp) - 1 // -1 because first digit was printed before '.'
   136  	}
   137  	if exp < 0 {
   138  		ch = '-'
   139  		exp = -exp
   140  	} else {
   141  		ch = '+'
   142  	}
   143  	buf = append(buf, ch)
   144  
   145  	// dd...d
   146  	if exp < 10 {
   147  		buf = append(buf, '0') // at least 2 exponent digits
   148  	}
   149  	return strconv.AppendInt(buf, exp, 10)
   150  }
   151  
   152  // %f: -ddddddd.ddddd
   153  func fmtF(buf []byte, prec int, neg bool, d decimal) []byte {
   154  	// sign
   155  	if neg {
   156  		buf = append(buf, '-')
   157  	}
   158  
   159  	// integer, padded with zeros as needed
   160  	if d.exp > 0 {
   161  		m := min(len(d.mant), d.exp)
   162  		buf = append(buf, d.mant[:m]...)
   163  		for ; m < d.exp; m++ {
   164  			buf = append(buf, '0')
   165  		}
   166  	} else {
   167  		buf = append(buf, '0')
   168  	}
   169  
   170  	// fraction
   171  	if prec > 0 {
   172  		buf = append(buf, '.')
   173  		for i := 0; i < prec; i++ {
   174  			ch := byte('0')
   175  			if j := d.exp + i; 0 <= j && j < len(d.mant) {
   176  				ch = d.mant[j]
   177  			}
   178  			buf = append(buf, ch)
   179  		}
   180  	}
   181  
   182  	return buf
   183  }
   184  
   185  func min(x, y int) int {
   186  	if x < y {
   187  		return x
   188  	}
   189  	return y
   190  }