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. |
Martin Möhrmann | d5b5d67 | 2015-02-13 15:59:54 +0100 | [diff] [blame] | 50 | func AppendFloat(dst []byte, f float64, fmt byte, prec, bitSize int) []byte { |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 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': |
Robert Griesemer | 81a3f29 | 2015-02-04 15:07:04 -0800 | [diff] [blame] | 122 | prec = max(digs.nd-1, 0) |
Rémy Oudompheng | c1c0279 | 2012-09-01 16:31:46 +0200 | [diff] [blame] | 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 | |
Robert Griesemer | acd82d5 | 2015-05-27 12:08:38 -0700 | [diff] [blame] | 226 | // roundShortest rounds d (= mant * 2^exp) to the shortest number of digits |
| 227 | // that will let the original floating point value be precisely reconstructed. |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 228 | func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) { |
Russ Cox | cf9b7f7 | 2008-11-19 12:50:34 -0800 | [diff] [blame] | 229 | // If mantissa is zero, the number is zero; stop now. |
| 230 | if mant == 0 { |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 231 | d.nd = 0 |
| 232 | return |
Russ Cox | cf9b7f7 | 2008-11-19 12:50:34 -0800 | [diff] [blame] | 233 | } |
| 234 | |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 235 | // Compute upper and lower such that any decimal number |
| 236 | // between upper and lower (possibly inclusive) |
| 237 | // will round to the original floating point number. |
| 238 | |
Rémy Oudompheng | 37cd165 | 2012-01-12 11:34:06 -0800 | [diff] [blame] | 239 | // We may see at once that the number is already shortest. |
| 240 | // |
| 241 | // Suppose d is not denormal, so that 2^exp <= d < 10^dp. |
| 242 | // The closest shorter number is at least 10^(dp-nd) away. |
| 243 | // The lower/upper bounds computed below are at distance |
| 244 | // at most 2^(exp-mantbits). |
| 245 | // |
| 246 | // So the number is already shortest if 10^(dp-nd) > 2^(exp-mantbits), |
| 247 | // or equivalently log2(10)*(dp-nd) > exp-mantbits. |
| 248 | // It is true if 332/100*(dp-nd) >= exp-mantbits (log2(10) > 3.32). |
| 249 | minexp := flt.bias + 1 // minimum possible exponent |
| 250 | if exp > minexp && 332*(d.dp-d.nd) >= 100*(exp-int(flt.mantbits)) { |
| 251 | // The number is already shortest. |
| 252 | return |
| 253 | } |
| 254 | |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 255 | // d = mant << (exp - mantbits) |
| 256 | // Next highest floating point number is mant+1 << exp-mantbits. |
Shenghou Ma | d1ef9b5 | 2012-12-19 03:04:09 +0800 | [diff] [blame] | 257 | // Our upper bound is halfway between, mant*2+1 << exp-mantbits-1. |
Russ Cox | 0ed5e6a | 2011-11-15 12:17:25 -0500 | [diff] [blame] | 258 | upper := new(decimal) |
| 259 | upper.Assign(mant*2 + 1) |
Russ Cox | cb51fdc | 2011-08-25 17:54:14 -0400 | [diff] [blame] | 260 | upper.Shift(exp - int(flt.mantbits) - 1) |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 261 | |
| 262 | // d = mant << (exp - mantbits) |
| 263 | // Next lowest floating point number is mant-1 << exp-mantbits, |
| 264 | // unless mant-1 drops the significant bit and exp is not the minimum exp, |
| 265 | // in which case the next lowest is mant*2-1 << exp-mantbits-1. |
| 266 | // Either way, call it mantlo << explo-mantbits. |
Shenghou Ma | d1ef9b5 | 2012-12-19 03:04:09 +0800 | [diff] [blame] | 267 | // Our lower bound is halfway between, mantlo*2+1 << explo-mantbits-1. |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 268 | var mantlo uint64 |
| 269 | var explo int |
Robert Griesemer | 3bb0032 | 2009-11-09 21:23:52 -0800 | [diff] [blame] | 270 | if mant > 1<<flt.mantbits || exp == minexp { |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 271 | mantlo = mant - 1 |
| 272 | explo = exp |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 273 | } else { |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 274 | mantlo = mant*2 - 1 |
| 275 | explo = exp - 1 |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 276 | } |
Russ Cox | 0ed5e6a | 2011-11-15 12:17:25 -0500 | [diff] [blame] | 277 | lower := new(decimal) |
| 278 | lower.Assign(mantlo*2 + 1) |
Russ Cox | cb51fdc | 2011-08-25 17:54:14 -0400 | [diff] [blame] | 279 | lower.Shift(explo - int(flt.mantbits) - 1) |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 280 | |
| 281 | // The upper and lower bounds are possible outputs only if |
| 282 | // the original mantissa is even, so that IEEE round-to-even |
| 283 | // would round to the original mantissa and not the neighbors. |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 284 | inclusive := mant%2 == 0 |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 285 | |
| 286 | // Now we can figure out the minimum number of digits required. |
| 287 | // Walk along until d has distinguished itself from upper and lower. |
| 288 | for i := 0; i < d.nd; i++ { |
Robert Griesemer | 0befa47 | 2015-09-23 09:52:21 -0700 | [diff] [blame^] | 289 | l := byte('0') // lower digit |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 290 | if i < lower.nd { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 291 | l = lower.d[i] |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 292 | } |
Robert Griesemer | 0befa47 | 2015-09-23 09:52:21 -0700 | [diff] [blame^] | 293 | m := d.d[i] // middle digit |
| 294 | u := byte('0') // upper digit |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 295 | if i < upper.nd { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 296 | u = upper.d[i] |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 297 | } |
| 298 | |
| 299 | // Okay to round down (truncate) if lower has a different digit |
Robert Griesemer | 0befa47 | 2015-09-23 09:52:21 -0700 | [diff] [blame^] | 300 | // or if lower is inclusive and is exactly the result of rounding |
| 301 | // down (i.e., and we have reached the final digit of lower). |
| 302 | okdown := l != m || inclusive && i+1 == lower.nd |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 303 | |
Robert Griesemer | 0befa47 | 2015-09-23 09:52:21 -0700 | [diff] [blame^] | 304 | // Okay to round up if upper has a different digit and either upper |
| 305 | // is inclusive or upper is bigger than the result of rounding up. |
Russ Cox | 6f77cd2 | 2012-01-12 11:32:28 -0800 | [diff] [blame] | 306 | okup := m != u && (inclusive || m+1 < u || i+1 < upper.nd) |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 307 | |
| 308 | // If it's okay to do either, then round to the nearest one. |
| 309 | // If it's okay to do only one, do it. |
| 310 | switch { |
| 311 | case okdown && okup: |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 312 | d.Round(i + 1) |
| 313 | return |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 314 | case okdown: |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 315 | d.RoundDown(i + 1) |
| 316 | return |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 317 | case okup: |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 318 | d.RoundUp(i + 1) |
| 319 | return |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 320 | } |
| 321 | } |
| 322 | } |
| 323 | |
Rémy Oudompheng | ff03482 | 2012-08-05 20:30:13 +0200 | [diff] [blame] | 324 | type decimalSlice struct { |
| 325 | d []byte |
| 326 | nd, dp int |
| 327 | neg bool |
| 328 | } |
| 329 | |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 330 | // %e: -d.ddddde±dd |
Rémy Oudompheng | ff03482 | 2012-08-05 20:30:13 +0200 | [diff] [blame] | 331 | 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] | 332 | // sign |
| 333 | if neg { |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 334 | dst = append(dst, '-') |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 335 | } |
| 336 | |
| 337 | // first digit |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 338 | ch := byte('0') |
| 339 | if d.nd != 0 { |
| 340 | ch = d.d[0] |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 341 | } |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 342 | dst = append(dst, ch) |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 343 | |
| 344 | // .moredigits |
| 345 | if prec > 0 { |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 346 | dst = append(dst, '.') |
Rémy Oudompheng | c1c0279 | 2012-09-01 16:31:46 +0200 | [diff] [blame] | 347 | i := 1 |
Robert Griesemer | 81a3f29 | 2015-02-04 15:07:04 -0800 | [diff] [blame] | 348 | m := min(d.nd, prec+1) |
| 349 | if i < m { |
| 350 | dst = append(dst, d.d[i:m]...) |
| 351 | i = m |
Rémy Oudompheng | c1c0279 | 2012-09-01 16:31:46 +0200 | [diff] [blame] | 352 | } |
Robert Griesemer | 81a3f29 | 2015-02-04 15:07:04 -0800 | [diff] [blame] | 353 | for ; i <= prec; i++ { |
Rémy Oudompheng | c1c0279 | 2012-09-01 16:31:46 +0200 | [diff] [blame] | 354 | dst = append(dst, '0') |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 355 | } |
| 356 | } |
| 357 | |
| 358 | // e± |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 359 | dst = append(dst, fmt) |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 360 | exp := d.dp - 1 |
| 361 | if d.nd == 0 { // special case: 0 has exponent 0 |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 362 | exp = 0 |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 363 | } |
| 364 | if exp < 0 { |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 365 | ch = '-' |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 366 | exp = -exp |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 367 | } else { |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 368 | ch = '+' |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 369 | } |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 370 | dst = append(dst, ch) |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 371 | |
Robert Griesemer | 81a3f29 | 2015-02-04 15:07:04 -0800 | [diff] [blame] | 372 | // dd or ddd |
| 373 | switch { |
| 374 | case exp < 10: |
| 375 | dst = append(dst, '0', byte(exp)+'0') |
| 376 | case exp < 100: |
| 377 | dst = append(dst, byte(exp/10)+'0', byte(exp%10)+'0') |
| 378 | default: |
| 379 | dst = append(dst, byte(exp/100)+'0', byte(exp/10)%10+'0', byte(exp%10)+'0') |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 380 | } |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 381 | |
Rémy Oudompheng | c1c0279 | 2012-09-01 16:31:46 +0200 | [diff] [blame] | 382 | return dst |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 383 | } |
| 384 | |
| 385 | // %f: -ddddddd.ddddd |
Rémy Oudompheng | ff03482 | 2012-08-05 20:30:13 +0200 | [diff] [blame] | 386 | func fmtF(dst []byte, neg bool, d decimalSlice, prec int) []byte { |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 387 | // sign |
| 388 | if neg { |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 389 | dst = append(dst, '-') |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 390 | } |
| 391 | |
| 392 | // integer, padded with zeros as needed. |
| 393 | if d.dp > 0 { |
Robert Griesemer | 81a3f29 | 2015-02-04 15:07:04 -0800 | [diff] [blame] | 394 | m := min(d.nd, d.dp) |
| 395 | dst = append(dst, d.d[:m]...) |
| 396 | for ; m < d.dp; m++ { |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 397 | dst = append(dst, '0') |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 398 | } |
| 399 | } else { |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 400 | dst = append(dst, '0') |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 401 | } |
| 402 | |
| 403 | // fraction |
| 404 | if prec > 0 { |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 405 | dst = append(dst, '.') |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 406 | for i := 0; i < prec; i++ { |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 407 | ch := byte('0') |
| 408 | if j := d.dp + i; 0 <= j && j < d.nd { |
| 409 | ch = d.d[j] |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 410 | } |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 411 | dst = append(dst, ch) |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 412 | } |
| 413 | } |
| 414 | |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 415 | return dst |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 416 | } |
| 417 | |
Martin Möhrmann | d5b5d67 | 2015-02-13 15:59:54 +0100 | [diff] [blame] | 418 | // %b: -ddddddddp±ddd |
Robert Griesemer | 127b5a6 | 2011-12-07 10:30:27 -0800 | [diff] [blame] | 419 | func fmtB(dst []byte, neg bool, mant uint64, exp int, flt *floatInfo) []byte { |
Martin Möhrmann | d5b5d67 | 2015-02-13 15:59:54 +0100 | [diff] [blame] | 420 | // sign |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 421 | if neg { |
Martin Möhrmann | d5b5d67 | 2015-02-13 15:59:54 +0100 | [diff] [blame] | 422 | dst = append(dst, '-') |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 423 | } |
Martin Möhrmann | d5b5d67 | 2015-02-13 15:59:54 +0100 | [diff] [blame] | 424 | |
| 425 | // mantissa |
| 426 | dst, _ = formatBits(dst, mant, 10, false, true) |
| 427 | |
| 428 | // p |
| 429 | dst = append(dst, 'p') |
| 430 | |
| 431 | // ±exponent |
| 432 | exp -= int(flt.mantbits) |
| 433 | if exp >= 0 { |
| 434 | dst = append(dst, '+') |
| 435 | } |
| 436 | dst, _ = formatBits(dst, uint64(exp), 10, exp < 0, true) |
| 437 | |
| 438 | return dst |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 439 | } |
| 440 | |
Robert Griesemer | 81a3f29 | 2015-02-04 15:07:04 -0800 | [diff] [blame] | 441 | func min(a, b int) int { |
| 442 | if a < b { |
| 443 | return a |
| 444 | } |
| 445 | return b |
| 446 | } |
| 447 | |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 448 | func max(a, b int) int { |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 449 | if a > b { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 450 | return a |
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 | return b |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 453 | } |