Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 1 | // Copyright 2009 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 | // Binary to decimal floating point conversion. |
| 6 | // Algorithm: |
| 7 | // 1) store mantissa in multiprecision decimal |
| 8 | // 2) shift decimal by exponent |
| 9 | // 3) read digits out & format |
| 10 | |
| 11 | package strconv |
| 12 | |
Russ Cox | 3b864e4 | 2009-08-12 13:18:37 -0700 | [diff] [blame] | 13 | import "math" |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 14 | |
| 15 | // TODO: move elsewhere? |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 16 | type floatInfo struct { |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 17 | mantbits uint |
| 18 | expbits uint |
| 19 | bias int |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 20 | } |
Russ Cox | 094f1d5 | 2009-10-08 15:14:54 -0700 | [diff] [blame] | 21 | |
| 22 | var float32info = floatInfo{23, 8, -127} |
| 23 | var float64info = floatInfo{52, 11, -1023} |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 24 | |
Russ Cox | efbeaed | 2011-12-05 15:48:21 -0500 | [diff] [blame] | 25 | // FormatFloat converts the floating-point number f to a string, |
| 26 | // according to the format fmt and precision prec. It rounds the |
| 27 | // result assuming that the original was obtained from a floating-point |
| 28 | // value of bitSize bits (32 for float32, 64 for float64). |
Russ Cox | 5bf0fbe | 2009-03-05 15:29:04 -0800 | [diff] [blame] | 29 | // |
| 30 | // The format fmt is one of |
| 31 | // 'b' (-ddddp±ddd, a binary exponent), |
| 32 | // 'e' (-d.dddde±dd, a decimal exponent), |
Russ Cox | 71793d4 | 2011-01-04 13:13:12 -0500 | [diff] [blame] | 33 | // 'E' (-d.ddddE±dd, a decimal exponent), |
| 34 | // 'f' (-ddd.dddd, no exponent), |
| 35 | // 'g' ('e' for large exponents, 'f' otherwise), or |
| 36 | // 'G' ('E' for large exponents, 'f' otherwise). |
Russ Cox | 5bf0fbe | 2009-03-05 15:29:04 -0800 | [diff] [blame] | 37 | // |
| 38 | // The precision prec controls the number of digits |
Russ Cox | 71793d4 | 2011-01-04 13:13:12 -0500 | [diff] [blame] | 39 | // (excluding the exponent) printed by the 'e', 'E', 'f', 'g', and 'G' formats. |
| 40 | // For 'e', 'E', and 'f' it is the number of digits after the decimal point. |
| 41 | // For 'g' and 'G' it is the total number of digits. |
Russ Cox | 5bf0fbe | 2009-03-05 15:29:04 -0800 | [diff] [blame] | 42 | // The special precision -1 uses the smallest number of digits |
Ian Lance Taylor | 3639781 | 2011-12-19 20:57:32 -0800 | [diff] [blame] | 43 | // necessary such that ParseFloat will return f exactly. |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 44 | func FormatFloat(f float64, fmt byte, prec, bitSize int) string { |
Robert Griesemer | 2e3bd89 | 2011-12-07 14:45:45 -0800 | [diff] [blame] | 45 | return string(genericFtoa(make([]byte, 0, max(prec+4, 24)), f, fmt, prec, bitSize)) |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 46 | } |
| 47 | |
Russ Cox | efbeaed | 2011-12-05 15:48:21 -0500 | [diff] [blame] | 48 | // AppendFloat appends the string form of the floating-point number f, |
| 49 | // as generated by FormatFloat, to dst and returns the extended buffer. |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 50 | func AppendFloat(dst []byte, f float64, fmt byte, prec int, bitSize int) []byte { |
| 51 | return genericFtoa(dst, f, fmt, prec, bitSize) |
Russ Cox | 2d3e47c | 2010-06-18 22:43:37 -0700 | [diff] [blame] | 52 | } |
| 53 | |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 54 | func genericFtoa(dst []byte, val float64, fmt byte, prec, bitSize int) []byte { |
| 55 | var bits uint64 |
| 56 | var flt *floatInfo |
| 57 | switch bitSize { |
| 58 | case 32: |
| 59 | bits = uint64(math.Float32bits(float32(val))) |
| 60 | flt = &float32info |
| 61 | case 64: |
| 62 | bits = math.Float64bits(val) |
| 63 | flt = &float64info |
| 64 | default: |
| 65 | panic("strconv: illegal AppendFloat/FormatFloat bitSize") |
| 66 | } |
| 67 | |
Rob Pike | a67292f | 2011-02-11 16:06:04 -0800 | [diff] [blame] | 68 | neg := bits>>(flt.expbits+flt.mantbits) != 0 |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 69 | exp := int(bits>>flt.mantbits) & (1<<flt.expbits - 1) |
| 70 | mant := bits & (uint64(1)<<flt.mantbits - 1) |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 71 | |
| 72 | switch exp { |
Robert Griesemer | 3bb0032 | 2009-11-09 21:23:52 -0800 | [diff] [blame] | 73 | case 1<<flt.expbits - 1: |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 74 | // Inf, NaN |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 75 | var s string |
| 76 | switch { |
| 77 | case mant != 0: |
| 78 | s = "NaN" |
| 79 | case neg: |
| 80 | s = "-Inf" |
| 81 | default: |
| 82 | s = "+Inf" |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 83 | } |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 84 | return append(dst, s...) |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 85 | |
| 86 | case 0: |
| 87 | // denormalized |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 88 | exp++ |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 89 | |
| 90 | default: |
| 91 | // add implicit top bit |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 92 | mant |= uint64(1) << flt.mantbits |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 93 | } |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 94 | exp += flt.bias |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 95 | |
| 96 | // Pick off easy binary format. |
| 97 | if fmt == 'b' { |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 98 | return fmtB(dst, neg, mant, exp, flt) |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 99 | } |
| 100 | |
Rémy Oudompheng | c1c0279 | 2012-09-01 16:31:46 +0200 | [diff] [blame] | 101 | if !optimize { |
| 102 | return bigFtoa(dst, prec, fmt, neg, mant, exp, flt) |
| 103 | } |
Rémy Oudompheng | 0575cd9 | 2012-01-13 23:24:33 +0100 | [diff] [blame] | 104 | |
Rémy Oudompheng | ff03482 | 2012-08-05 20:30:13 +0200 | [diff] [blame] | 105 | var digs decimalSlice |
Rémy Oudompheng | c1c0279 | 2012-09-01 16:31:46 +0200 | [diff] [blame] | 106 | ok := false |
| 107 | // Negative precision means "only as much as needed to be exact." |
| 108 | shortest := prec < 0 |
Rémy Oudompheng | 0575cd9 | 2012-01-13 23:24:33 +0100 | [diff] [blame] | 109 | if shortest { |
Rémy Oudompheng | c1c0279 | 2012-09-01 16:31:46 +0200 | [diff] [blame] | 110 | // Try Grisu3 algorithm. |
| 111 | f := new(extFloat) |
| 112 | lower, upper := f.AssignComputeBounds(mant, exp, neg, flt) |
| 113 | var buf [32]byte |
| 114 | digs.d = buf[:] |
| 115 | ok = f.ShortestDecimal(&digs, &lower, &upper) |
Rémy Oudompheng | 0575cd9 | 2012-01-13 23:24:33 +0100 | [diff] [blame] | 116 | if !ok { |
Rémy Oudompheng | c1c0279 | 2012-09-01 16:31:46 +0200 | [diff] [blame] | 117 | return bigFtoa(dst, prec, fmt, neg, mant, exp, flt) |
Rémy Oudompheng | 0575cd9 | 2012-01-13 23:24:33 +0100 | [diff] [blame] | 118 | } |
| 119 | // Precision for shortest representation mode. |
Rémy Oudompheng | c1c0279 | 2012-09-01 16:31:46 +0200 | [diff] [blame] | 120 | switch fmt { |
| 121 | case 'e', 'E': |
| 122 | prec = digs.nd - 1 |
| 123 | case 'f': |
| 124 | prec = max(digs.nd-digs.dp, 0) |
| 125 | case 'g', 'G': |
| 126 | prec = digs.nd |
| 127 | } |
| 128 | } else if fmt != 'f' { |
| 129 | // Fixed number of digits. |
| 130 | digits := prec |
| 131 | switch fmt { |
| 132 | case 'e', 'E': |
| 133 | digits++ |
| 134 | case 'g', 'G': |
| 135 | if prec == 0 { |
| 136 | prec = 1 |
Rémy Oudompheng | 0575cd9 | 2012-01-13 23:24:33 +0100 | [diff] [blame] | 137 | } |
Rémy Oudompheng | c1c0279 | 2012-09-01 16:31:46 +0200 | [diff] [blame] | 138 | digits = prec |
| 139 | } |
| 140 | if digits <= 15 { |
| 141 | // try fast algorithm when the number of digits is reasonable. |
| 142 | var buf [24]byte |
| 143 | digs.d = buf[:] |
| 144 | f := extFloat{mant, exp - int(flt.mantbits), neg} |
| 145 | ok = f.FixedDecimal(&digs, digits) |
| 146 | } |
| 147 | } |
| 148 | if !ok { |
| 149 | return bigFtoa(dst, prec, fmt, neg, mant, exp, flt) |
| 150 | } |
| 151 | return formatDigits(dst, shortest, neg, digs, prec, fmt) |
| 152 | } |
| 153 | |
| 154 | // bigFtoa uses multiprecision computations to format a float. |
| 155 | func bigFtoa(dst []byte, prec int, fmt byte, neg bool, mant uint64, exp int, flt *floatInfo) []byte { |
| 156 | d := new(decimal) |
| 157 | d.Assign(mant) |
| 158 | d.Shift(exp - int(flt.mantbits)) |
| 159 | var digs decimalSlice |
| 160 | shortest := prec < 0 |
| 161 | if shortest { |
| 162 | roundShortest(d, mant, exp, flt) |
| 163 | digs = decimalSlice{d: d.d[:], nd: d.nd, dp: d.dp} |
| 164 | // Precision for shortest representation mode. |
| 165 | switch fmt { |
| 166 | case 'e', 'E': |
| 167 | prec = digs.nd - 1 |
| 168 | case 'f': |
| 169 | prec = max(digs.nd-digs.dp, 0) |
| 170 | case 'g', 'G': |
| 171 | prec = digs.nd |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 172 | } |
| 173 | } else { |
Rémy Oudompheng | 0575cd9 | 2012-01-13 23:24:33 +0100 | [diff] [blame] | 174 | // Round appropriately. |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 175 | switch fmt { |
Russ Cox | a843b45 | 2009-08-31 16:38:30 -0700 | [diff] [blame] | 176 | case 'e', 'E': |
Robert Griesemer | 3bb0032 | 2009-11-09 21:23:52 -0800 | [diff] [blame] | 177 | d.Round(prec + 1) |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 178 | case 'f': |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 179 | d.Round(d.dp + prec) |
Russ Cox | a843b45 | 2009-08-31 16:38:30 -0700 | [diff] [blame] | 180 | case 'g', 'G': |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 181 | if prec == 0 { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 182 | prec = 1 |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 183 | } |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 184 | d.Round(prec) |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 185 | } |
Rémy Oudompheng | ff03482 | 2012-08-05 20:30:13 +0200 | [diff] [blame] | 186 | digs = decimalSlice{d: d.d[:], nd: d.nd, dp: d.dp} |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 187 | } |
Rémy Oudompheng | c1c0279 | 2012-09-01 16:31:46 +0200 | [diff] [blame] | 188 | return formatDigits(dst, shortest, neg, digs, prec, fmt) |
| 189 | } |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 190 | |
Rémy Oudompheng | c1c0279 | 2012-09-01 16:31:46 +0200 | [diff] [blame] | 191 | func formatDigits(dst []byte, shortest bool, neg bool, digs decimalSlice, prec int, fmt byte) []byte { |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 192 | switch fmt { |
Russ Cox | a843b45 | 2009-08-31 16:38:30 -0700 | [diff] [blame] | 193 | case 'e', 'E': |
Rémy Oudompheng | ff03482 | 2012-08-05 20:30:13 +0200 | [diff] [blame] | 194 | return fmtE(dst, neg, digs, prec, fmt) |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 195 | case 'f': |
Rémy Oudompheng | ff03482 | 2012-08-05 20:30:13 +0200 | [diff] [blame] | 196 | return fmtF(dst, neg, digs, prec) |
Russ Cox | a843b45 | 2009-08-31 16:38:30 -0700 | [diff] [blame] | 197 | case 'g', 'G': |
Rob Pike | 21f8ae8 | 2010-06-29 16:39:17 -0700 | [diff] [blame] | 198 | // trailing fractional zeros in 'e' form will be trimmed. |
| 199 | eprec := prec |
Rémy Oudompheng | ff03482 | 2012-08-05 20:30:13 +0200 | [diff] [blame] | 200 | if eprec > digs.nd && digs.nd >= digs.dp { |
| 201 | eprec = digs.nd |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 202 | } |
| 203 | // %e is used if the exponent from the conversion |
| 204 | // is less than -4 or greater than or equal to the precision. |
Russ Cox | 0e198da | 2008-11-23 17:27:44 -0800 | [diff] [blame] | 205 | // if precision was the shortest possible, use precision 6 for this decision. |
Russ Cox | 0e198da | 2008-11-23 17:27:44 -0800 | [diff] [blame] | 206 | if shortest { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 207 | eprec = 6 |
Russ Cox | 0e198da | 2008-11-23 17:27:44 -0800 | [diff] [blame] | 208 | } |
Rémy Oudompheng | ff03482 | 2012-08-05 20:30:13 +0200 | [diff] [blame] | 209 | exp := digs.dp - 1 |
Russ Cox | 0e198da | 2008-11-23 17:27:44 -0800 | [diff] [blame] | 210 | if exp < -4 || exp >= eprec { |
Rémy Oudompheng | ff03482 | 2012-08-05 20:30:13 +0200 | [diff] [blame] | 211 | if prec > digs.nd { |
| 212 | prec = digs.nd |
Rob Pike | 21f8ae8 | 2010-06-29 16:39:17 -0700 | [diff] [blame] | 213 | } |
Rémy Oudompheng | ff03482 | 2012-08-05 20:30:13 +0200 | [diff] [blame] | 214 | return fmtE(dst, neg, digs, prec-1, fmt+'e'-'g') |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 215 | } |
Rémy Oudompheng | ff03482 | 2012-08-05 20:30:13 +0200 | [diff] [blame] | 216 | if prec > digs.dp { |
| 217 | prec = digs.nd |
Rob Pike | 21f8ae8 | 2010-06-29 16:39:17 -0700 | [diff] [blame] | 218 | } |
Rémy Oudompheng | ff03482 | 2012-08-05 20:30:13 +0200 | [diff] [blame] | 219 | return fmtF(dst, neg, digs, max(prec-digs.dp, 0)) |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 220 | } |
| 221 | |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 222 | // unknown format |
| 223 | return append(dst, '%', fmt) |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 224 | } |
| 225 | |
| 226 | // Round d (= mant * 2^exp) to the shortest number of digits |
| 227 | // that will let the original floating point value be precisely |
| 228 | // reconstructed. Size is original floating point size (64 or 32). |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 229 | func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) { |
Russ Cox | cf9b7f7 | 2008-11-19 12:50:34 -0800 | [diff] [blame] | 230 | // If mantissa is zero, the number is zero; stop now. |
| 231 | if mant == 0 { |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 232 | d.nd = 0 |
| 233 | return |
Russ Cox | cf9b7f7 | 2008-11-19 12:50:34 -0800 | [diff] [blame] | 234 | } |
| 235 | |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 236 | // Compute upper and lower such that any decimal number |
| 237 | // between upper and lower (possibly inclusive) |
| 238 | // will round to the original floating point number. |
| 239 | |
Rémy Oudompheng | 37cd165 | 2012-01-12 11:34:06 -0800 | [diff] [blame] | 240 | // We may see at once that the number is already shortest. |
| 241 | // |
| 242 | // Suppose d is not denormal, so that 2^exp <= d < 10^dp. |
| 243 | // The closest shorter number is at least 10^(dp-nd) away. |
| 244 | // The lower/upper bounds computed below are at distance |
| 245 | // at most 2^(exp-mantbits). |
| 246 | // |
| 247 | // So the number is already shortest if 10^(dp-nd) > 2^(exp-mantbits), |
| 248 | // or equivalently log2(10)*(dp-nd) > exp-mantbits. |
| 249 | // It is true if 332/100*(dp-nd) >= exp-mantbits (log2(10) > 3.32). |
| 250 | minexp := flt.bias + 1 // minimum possible exponent |
| 251 | if exp > minexp && 332*(d.dp-d.nd) >= 100*(exp-int(flt.mantbits)) { |
| 252 | // The number is already shortest. |
| 253 | return |
| 254 | } |
| 255 | |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 256 | // d = mant << (exp - mantbits) |
| 257 | // Next highest floating point number is mant+1 << exp-mantbits. |
Shenghou Ma | d1ef9b5 | 2012-12-19 03:04:09 +0800 | [diff] [blame^] | 258 | // Our upper bound is halfway between, mant*2+1 << exp-mantbits-1. |
Russ Cox | 0ed5e6a | 2011-11-15 12:17:25 -0500 | [diff] [blame] | 259 | upper := new(decimal) |
| 260 | upper.Assign(mant*2 + 1) |
Russ Cox | cb51fdc | 2011-08-25 17:54:14 -0400 | [diff] [blame] | 261 | upper.Shift(exp - int(flt.mantbits) - 1) |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 262 | |
| 263 | // d = mant << (exp - mantbits) |
| 264 | // Next lowest floating point number is mant-1 << exp-mantbits, |
| 265 | // unless mant-1 drops the significant bit and exp is not the minimum exp, |
| 266 | // in which case the next lowest is mant*2-1 << exp-mantbits-1. |
| 267 | // Either way, call it mantlo << explo-mantbits. |
Shenghou Ma | d1ef9b5 | 2012-12-19 03:04:09 +0800 | [diff] [blame^] | 268 | // Our lower bound is halfway between, mantlo*2+1 << explo-mantbits-1. |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 269 | var mantlo uint64 |
| 270 | var explo int |
Robert Griesemer | 3bb0032 | 2009-11-09 21:23:52 -0800 | [diff] [blame] | 271 | if mant > 1<<flt.mantbits || exp == minexp { |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 272 | mantlo = mant - 1 |
| 273 | explo = exp |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 274 | } else { |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 275 | mantlo = mant*2 - 1 |
| 276 | explo = exp - 1 |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 277 | } |
Russ Cox | 0ed5e6a | 2011-11-15 12:17:25 -0500 | [diff] [blame] | 278 | lower := new(decimal) |
| 279 | lower.Assign(mantlo*2 + 1) |
Russ Cox | cb51fdc | 2011-08-25 17:54:14 -0400 | [diff] [blame] | 280 | lower.Shift(explo - int(flt.mantbits) - 1) |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 281 | |
| 282 | // The upper and lower bounds are possible outputs only if |
| 283 | // the original mantissa is even, so that IEEE round-to-even |
| 284 | // would round to the original mantissa and not the neighbors. |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 285 | inclusive := mant%2 == 0 |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 286 | |
| 287 | // Now we can figure out the minimum number of digits required. |
| 288 | // Walk along until d has distinguished itself from upper and lower. |
| 289 | for i := 0; i < d.nd; i++ { |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 290 | var l, m, u byte // lower, middle, upper digits |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 291 | if i < lower.nd { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 292 | l = lower.d[i] |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 293 | } else { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 294 | l = '0' |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 295 | } |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 296 | m = d.d[i] |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 297 | if i < upper.nd { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 298 | u = upper.d[i] |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 299 | } else { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 300 | u = '0' |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 301 | } |
| 302 | |
| 303 | // Okay to round down (truncate) if lower has a different digit |
| 304 | // or if lower is inclusive and is exactly the result of rounding down. |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 305 | okdown := l != m || (inclusive && l == m && i+1 == lower.nd) |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 306 | |
| 307 | // Okay to round up if upper has a different digit and |
| 308 | // either upper is inclusive or upper is bigger than the result of rounding up. |
Russ Cox | 6f77cd2 | 2012-01-12 11:32:28 -0800 | [diff] [blame] | 309 | okup := m != u && (inclusive || m+1 < u || i+1 < upper.nd) |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 310 | |
| 311 | // If it's okay to do either, then round to the nearest one. |
| 312 | // If it's okay to do only one, do it. |
| 313 | switch { |
| 314 | case okdown && okup: |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 315 | d.Round(i + 1) |
| 316 | return |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 317 | case okdown: |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 318 | d.RoundDown(i + 1) |
| 319 | return |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 320 | case okup: |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 321 | d.RoundUp(i + 1) |
| 322 | return |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 323 | } |
| 324 | } |
| 325 | } |
| 326 | |
Rémy Oudompheng | ff03482 | 2012-08-05 20:30:13 +0200 | [diff] [blame] | 327 | type decimalSlice struct { |
| 328 | d []byte |
| 329 | nd, dp int |
| 330 | neg bool |
| 331 | } |
| 332 | |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 333 | // %e: -d.ddddde±dd |
Rémy Oudompheng | ff03482 | 2012-08-05 20:30:13 +0200 | [diff] [blame] | 334 | func fmtE(dst []byte, neg bool, d decimalSlice, prec int, fmt byte) []byte { |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 335 | // sign |
| 336 | if neg { |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 337 | dst = append(dst, '-') |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 338 | } |
| 339 | |
| 340 | // first digit |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 341 | ch := byte('0') |
| 342 | if d.nd != 0 { |
| 343 | ch = d.d[0] |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 344 | } |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 345 | dst = append(dst, ch) |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 346 | |
| 347 | // .moredigits |
| 348 | if prec > 0 { |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 349 | dst = append(dst, '.') |
Rémy Oudompheng | c1c0279 | 2012-09-01 16:31:46 +0200 | [diff] [blame] | 350 | i := 1 |
| 351 | m := d.nd + prec + 1 - max(d.nd, prec+1) |
| 352 | for i < m { |
| 353 | dst = append(dst, d.d[i]) |
| 354 | i++ |
| 355 | } |
| 356 | for i <= prec { |
| 357 | dst = append(dst, '0') |
| 358 | i++ |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 359 | } |
| 360 | } |
| 361 | |
| 362 | // e± |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 363 | dst = append(dst, fmt) |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 364 | exp := d.dp - 1 |
| 365 | if d.nd == 0 { // special case: 0 has exponent 0 |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 366 | exp = 0 |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 367 | } |
| 368 | if exp < 0 { |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 369 | ch = '-' |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 370 | exp = -exp |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 371 | } else { |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 372 | ch = '+' |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 373 | } |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 374 | dst = append(dst, ch) |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 375 | |
| 376 | // dddd |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 377 | var buf [3]byte |
| 378 | i := len(buf) |
| 379 | for exp >= 10 { |
| 380 | i-- |
| 381 | buf[i] = byte(exp%10 + '0') |
| 382 | exp /= 10 |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 383 | } |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 384 | // exp < 10 |
| 385 | i-- |
| 386 | buf[i] = byte(exp + '0') |
| 387 | |
Rémy Oudompheng | c1c0279 | 2012-09-01 16:31:46 +0200 | [diff] [blame] | 388 | switch i { |
| 389 | case 0: |
| 390 | dst = append(dst, buf[0], buf[1], buf[2]) |
| 391 | case 1: |
| 392 | dst = append(dst, buf[1], buf[2]) |
| 393 | case 2: |
| 394 | // leading zeroes |
| 395 | dst = append(dst, '0', buf[2]) |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 396 | } |
Rémy Oudompheng | c1c0279 | 2012-09-01 16:31:46 +0200 | [diff] [blame] | 397 | return dst |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 398 | } |
| 399 | |
| 400 | // %f: -ddddddd.ddddd |
Rémy Oudompheng | ff03482 | 2012-08-05 20:30:13 +0200 | [diff] [blame] | 401 | func fmtF(dst []byte, neg bool, d decimalSlice, prec int) []byte { |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 402 | // sign |
| 403 | if neg { |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 404 | dst = append(dst, '-') |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 405 | } |
| 406 | |
| 407 | // integer, padded with zeros as needed. |
| 408 | if d.dp > 0 { |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 409 | var i int |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 410 | for i = 0; i < d.dp && i < d.nd; i++ { |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 411 | dst = append(dst, d.d[i]) |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 412 | } |
| 413 | for ; i < d.dp; i++ { |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 414 | dst = append(dst, '0') |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 415 | } |
| 416 | } else { |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 417 | dst = append(dst, '0') |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 418 | } |
| 419 | |
| 420 | // fraction |
| 421 | if prec > 0 { |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 422 | dst = append(dst, '.') |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 423 | for i := 0; i < prec; i++ { |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 424 | ch := byte('0') |
| 425 | if j := d.dp + i; 0 <= j && j < d.nd { |
| 426 | ch = d.d[j] |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 427 | } |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 428 | dst = append(dst, ch) |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 429 | } |
| 430 | } |
| 431 | |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 432 | return dst |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 433 | } |
| 434 | |
| 435 | // %b: -ddddddddp+ddd |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 436 | func fmtB(dst []byte, neg bool, mant uint64, exp int, flt *floatInfo) []byte { |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 437 | var buf [50]byte |
| 438 | w := len(buf) |
| 439 | exp -= int(flt.mantbits) |
| 440 | esign := byte('+') |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 441 | if exp < 0 { |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 442 | esign = '-' |
| 443 | exp = -exp |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 444 | } |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 445 | n := 0 |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 446 | for exp > 0 || n < 1 { |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 447 | n++ |
| 448 | w-- |
| 449 | buf[w] = byte(exp%10 + '0') |
| 450 | exp /= 10 |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 451 | } |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 452 | w-- |
| 453 | buf[w] = esign |
| 454 | w-- |
| 455 | buf[w] = 'p' |
| 456 | n = 0 |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 457 | for mant > 0 || n < 1 { |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 458 | n++ |
| 459 | w-- |
| 460 | buf[w] = byte(mant%10 + '0') |
| 461 | mant /= 10 |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 462 | } |
| 463 | if neg { |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 464 | w-- |
| 465 | buf[w] = '-' |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 466 | } |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 467 | return append(dst, buf[w:]...) |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 468 | } |
| 469 | |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 470 | func max(a, b int) int { |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 471 | if a > b { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 472 | return a |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 473 | } |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 474 | return b |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 475 | } |