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 | // Multiprecision decimal numbers. |
| 6 | // For floating-point formatting only; not general purpose. |
| 7 | // Only operations are assign and (binary) left/right shift. |
| 8 | // Can do binary floating point in multiprecision decimal precisely |
| 9 | // because 2 divides 10; cannot do decimal floating point |
| 10 | // in multiprecision binary precisely. |
| 11 | |
| 12 | package strconv |
| 13 | |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 14 | type decimal struct { |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 15 | // TODO(rsc): Can make d[] a bit smaller and add |
| 16 | // truncated bool; |
Robert Griesemer | 314b358 | 2009-11-04 23:20:49 -0800 | [diff] [blame] | 17 | d [2000]byte; // digits |
| 18 | nd int; // number of digits used |
| 19 | dp int; // decimal point |
| 20 | } |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 21 | |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 22 | func (a *decimal) String() string { |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 23 | n := 10 + a.nd; |
| 24 | if a.dp > 0 { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 25 | n += a.dp |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 26 | } |
| 27 | if a.dp < 0 { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 28 | n += -a.dp |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 29 | } |
| 30 | |
Russ Cox | 5564504 | 2009-01-06 15:19:02 -0800 | [diff] [blame] | 31 | buf := make([]byte, n); |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 32 | w := 0; |
| 33 | switch { |
Russ Cox | cf9b7f7 | 2008-11-19 12:50:34 -0800 | [diff] [blame] | 34 | case a.nd == 0: |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 35 | return "0" |
Russ Cox | cf9b7f7 | 2008-11-19 12:50:34 -0800 | [diff] [blame] | 36 | |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 37 | case a.dp <= 0: |
| 38 | // zeros fill space between decimal point and digits |
| 39 | buf[w] = '0'; |
| 40 | w++; |
| 41 | buf[w] = '.'; |
| 42 | w++; |
Robert Griesemer | 3bb0032 | 2009-11-09 21:23:52 -0800 | [diff] [blame] | 43 | w += digitZero(buf[w : w+-a.dp]); |
Rob Pike | e70cedf | 2009-11-18 15:24:24 -0800 | [diff] [blame] | 44 | w += copy(buf[w:w+a.nd], a.d[0:a.nd]); |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 45 | |
| 46 | case a.dp < a.nd: |
| 47 | // decimal point in middle of digits |
Rob Pike | e70cedf | 2009-11-18 15:24:24 -0800 | [diff] [blame] | 48 | w += copy(buf[w:w+a.dp], a.d[0:a.dp]); |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 49 | buf[w] = '.'; |
| 50 | w++; |
Rob Pike | e70cedf | 2009-11-18 15:24:24 -0800 | [diff] [blame] | 51 | w += copy(buf[w:w+a.nd-a.dp], a.d[a.dp:a.nd]); |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 52 | |
| 53 | default: |
| 54 | // zeros fill space between digits and decimal point |
Rob Pike | e70cedf | 2009-11-18 15:24:24 -0800 | [diff] [blame] | 55 | w += copy(buf[w:w+a.nd], a.d[0:a.nd]); |
Robert Griesemer | 3bb0032 | 2009-11-09 21:23:52 -0800 | [diff] [blame] | 56 | w += digitZero(buf[w : w+a.dp-a.nd]); |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 57 | } |
| 58 | return string(buf[0:w]); |
| 59 | } |
| 60 | |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 61 | func copy(dst []byte, src []byte) int { |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 62 | for i := 0; i < len(dst); i++ { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 63 | dst[i] = src[i] |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 64 | } |
| 65 | return len(dst); |
| 66 | } |
| 67 | |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 68 | func digitZero(dst []byte) int { |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 69 | for i := 0; i < len(dst); i++ { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 70 | dst[i] = '0' |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 71 | } |
| 72 | return len(dst); |
| 73 | } |
| 74 | |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 75 | // trim trailing zeros from number. |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 76 | // (They are meaningless; the decimal point is tracked |
| 77 | // independent of the number of digits.) |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 78 | func trim(a *decimal) { |
Robert Griesemer | 3bb0032 | 2009-11-09 21:23:52 -0800 | [diff] [blame] | 79 | for a.nd > 0 && a.d[a.nd-1] == '0' { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 80 | a.nd-- |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 81 | } |
| 82 | if a.nd == 0 { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 83 | a.dp = 0 |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 84 | } |
| 85 | } |
| 86 | |
| 87 | // Assign v to a. |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 88 | func (a *decimal) Assign(v uint64) { |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 89 | var buf [50]byte; |
| 90 | |
| 91 | // Write reversed decimal in buf. |
| 92 | n := 0; |
| 93 | for v > 0 { |
Robert Griesemer | 3bb0032 | 2009-11-09 21:23:52 -0800 | [diff] [blame] | 94 | v1 := v / 10; |
| 95 | v -= 10 * v1; |
| 96 | buf[n] = byte(v + '0'); |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 97 | n++; |
| 98 | v = v1; |
| 99 | } |
| 100 | |
| 101 | // Reverse again to produce forward decimal in a.d. |
| 102 | a.nd = 0; |
Robert Griesemer | 314b358 | 2009-11-04 23:20:49 -0800 | [diff] [blame] | 103 | for n--; n >= 0; n-- { |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 104 | a.d[a.nd] = buf[n]; |
| 105 | a.nd++; |
| 106 | } |
| 107 | a.dp = a.nd; |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 108 | trim(a); |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 109 | } |
| 110 | |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 111 | func newDecimal(i uint64) *decimal { |
| 112 | a := new(decimal); |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 113 | a.Assign(i); |
| 114 | return a; |
| 115 | } |
| 116 | |
| 117 | // Maximum shift that we can do in one pass without overflow. |
| 118 | // Signed int has 31 bits, and we have to be able to accomodate 9<<k. |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 119 | const maxShift = 27 |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 120 | |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 121 | // Binary shift right (* 2) by k bits. k <= maxShift to avoid overflow. |
| 122 | func rightShift(a *decimal, k uint) { |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 123 | r := 0; // read pointer |
| 124 | w := 0; // write pointer |
| 125 | |
| 126 | // Pick up enough leading digits to cover first shift. |
| 127 | n := 0; |
| 128 | for ; n>>k == 0; r++ { |
| 129 | if r >= a.nd { |
| 130 | if n == 0 { |
Russ Cox | cf9b7f7 | 2008-11-19 12:50:34 -0800 | [diff] [blame] | 131 | // a == 0; shouldn't get here, but handle anyway. |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 132 | a.nd = 0; |
| 133 | return; |
| 134 | } |
Russ Cox | cf9b7f7 | 2008-11-19 12:50:34 -0800 | [diff] [blame] | 135 | for n>>k == 0 { |
Robert Griesemer | 3bb0032 | 2009-11-09 21:23:52 -0800 | [diff] [blame] | 136 | n = n * 10; |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 137 | r++; |
| 138 | } |
| 139 | break; |
| 140 | } |
| 141 | c := int(a.d[r]); |
Robert Griesemer | 314b358 | 2009-11-04 23:20:49 -0800 | [diff] [blame] | 142 | n = n*10 + c - '0'; |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 143 | } |
Robert Griesemer | 3bb0032 | 2009-11-09 21:23:52 -0800 | [diff] [blame] | 144 | a.dp -= r - 1; |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 145 | |
| 146 | // Pick up a digit, put down a digit. |
| 147 | for ; r < a.nd; r++ { |
| 148 | c := int(a.d[r]); |
Robert Griesemer | 3bb0032 | 2009-11-09 21:23:52 -0800 | [diff] [blame] | 149 | dig := n >> k; |
| 150 | n -= dig << k; |
| 151 | a.d[w] = byte(dig + '0'); |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 152 | w++; |
Robert Griesemer | 314b358 | 2009-11-04 23:20:49 -0800 | [diff] [blame] | 153 | n = n*10 + c - '0'; |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 154 | } |
| 155 | |
| 156 | // Put down extra digits. |
| 157 | for n > 0 { |
Robert Griesemer | 3bb0032 | 2009-11-09 21:23:52 -0800 | [diff] [blame] | 158 | dig := n >> k; |
| 159 | n -= dig << k; |
| 160 | a.d[w] = byte(dig + '0'); |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 161 | w++; |
Robert Griesemer | 3bb0032 | 2009-11-09 21:23:52 -0800 | [diff] [blame] | 162 | n = n * 10; |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 163 | } |
| 164 | |
| 165 | a.nd = w; |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 166 | trim(a); |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 167 | } |
| 168 | |
| 169 | // Cheat sheet for left shift: table indexed by shift count giving |
| 170 | // number of new digits that will be introduced by that shift. |
| 171 | // |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 172 | // For example, leftcheats[4] = {2, "625"}. That means that |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 173 | // if we are shifting by 4 (multiplying by 16), it will add 2 digits |
| 174 | // when the string prefix is "625" through "999", and one fewer digit |
| 175 | // if the string prefix is "000" through "624". |
| 176 | // |
| 177 | // Credit for this trick goes to Ken. |
| 178 | |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 179 | type leftCheat struct { |
Robert Griesemer | 314b358 | 2009-11-04 23:20:49 -0800 | [diff] [blame] | 180 | delta int; // number of new digits |
| 181 | cutoff string; // minus one digit if original < a. |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 182 | } |
| 183 | |
Robert Griesemer | 314b358 | 2009-11-04 23:20:49 -0800 | [diff] [blame] | 184 | var leftcheats = []leftCheat{ |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 185 | // Leading digits of 1/2^i = 5^i. |
| 186 | // 5^23 is not an exact 64-bit floating point number, |
| 187 | // so have to use bc for the math. |
| 188 | /* |
Robert Griesemer | 314b358 | 2009-11-04 23:20:49 -0800 | [diff] [blame] | 189 | seq 27 | sed 's/^/5^/' | bc | |
| 190 | awk 'BEGIN{ print "\tleftCheat{ 0, \"\" }," } |
| 191 | { |
| 192 | log2 = log(2)/log(10) |
| 193 | printf("\tleftCheat{ %d, \"%s\" },\t// * %d\n", |
| 194 | int(log2*NR+1), $0, 2**NR) |
| 195 | }' |
| 196 | */ |
| 197 | leftCheat{0, ""}, |
| 198 | leftCheat{1, "5"}, // * 2 |
| 199 | leftCheat{1, "25"}, // * 4 |
| 200 | leftCheat{1, "125"}, // * 8 |
| 201 | leftCheat{2, "625"}, // * 16 |
| 202 | leftCheat{2, "3125"}, // * 32 |
| 203 | leftCheat{2, "15625"}, // * 64 |
| 204 | leftCheat{3, "78125"}, // * 128 |
| 205 | leftCheat{3, "390625"}, // * 256 |
| 206 | leftCheat{3, "1953125"}, // * 512 |
| 207 | leftCheat{4, "9765625"}, // * 1024 |
| 208 | leftCheat{4, "48828125"}, // * 2048 |
| 209 | leftCheat{4, "244140625"}, // * 4096 |
| 210 | leftCheat{4, "1220703125"}, // * 8192 |
| 211 | leftCheat{5, "6103515625"}, // * 16384 |
| 212 | leftCheat{5, "30517578125"}, // * 32768 |
| 213 | leftCheat{5, "152587890625"}, // * 65536 |
| 214 | leftCheat{6, "762939453125"}, // * 131072 |
| 215 | leftCheat{6, "3814697265625"}, // * 262144 |
| 216 | leftCheat{6, "19073486328125"}, // * 524288 |
| 217 | leftCheat{7, "95367431640625"}, // * 1048576 |
| 218 | leftCheat{7, "476837158203125"}, // * 2097152 |
| 219 | leftCheat{7, "2384185791015625"}, // * 4194304 |
| 220 | leftCheat{7, "11920928955078125"}, // * 8388608 |
| 221 | leftCheat{8, "59604644775390625"}, // * 16777216 |
| 222 | leftCheat{8, "298023223876953125"}, // * 33554432 |
| 223 | leftCheat{8, "1490116119384765625"}, // * 67108864 |
| 224 | leftCheat{9, "7450580596923828125"}, // * 134217728 |
Russ Cox | be2edb5 | 2009-03-03 08:39:12 -0800 | [diff] [blame] | 225 | } |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 226 | |
| 227 | // Is the leading prefix of b lexicographically less than s? |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 228 | func prefixIsLessThan(b []byte, s string) bool { |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 229 | for i := 0; i < len(s); i++ { |
| 230 | if i >= len(b) { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 231 | return true |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 232 | } |
| 233 | if b[i] != s[i] { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 234 | return b[i] < s[i] |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 235 | } |
| 236 | } |
| 237 | return false; |
| 238 | } |
| 239 | |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 240 | // Binary shift left (/ 2) by k bits. k <= maxShift to avoid overflow. |
| 241 | func leftShift(a *decimal, k uint) { |
| 242 | delta := leftcheats[k].delta; |
Robert Griesemer | 3bb0032 | 2009-11-09 21:23:52 -0800 | [diff] [blame] | 243 | if prefixIsLessThan(a.d[0:a.nd], leftcheats[k].cutoff) { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 244 | delta-- |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 245 | } |
| 246 | |
Robert Griesemer | 314b358 | 2009-11-04 23:20:49 -0800 | [diff] [blame] | 247 | r := a.nd; // read index |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 248 | w := a.nd + delta; // write index |
| 249 | n := 0; |
| 250 | |
| 251 | // Pick up a digit, put down a digit. |
| 252 | for r--; r >= 0; r-- { |
Robert Griesemer | 3bb0032 | 2009-11-09 21:23:52 -0800 | [diff] [blame] | 253 | n += (int(a.d[r]) - '0') << k; |
| 254 | quo := n / 10; |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 255 | rem := n - 10*quo; |
| 256 | w--; |
Robert Griesemer | 3bb0032 | 2009-11-09 21:23:52 -0800 | [diff] [blame] | 257 | a.d[w] = byte(rem + '0'); |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 258 | n = quo; |
| 259 | } |
| 260 | |
| 261 | // Put down extra digits. |
| 262 | for n > 0 { |
Robert Griesemer | 3bb0032 | 2009-11-09 21:23:52 -0800 | [diff] [blame] | 263 | quo := n / 10; |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 264 | rem := n - 10*quo; |
| 265 | w--; |
Robert Griesemer | 3bb0032 | 2009-11-09 21:23:52 -0800 | [diff] [blame] | 266 | a.d[w] = byte(rem + '0'); |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 267 | n = quo; |
| 268 | } |
| 269 | |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 270 | a.nd += delta; |
| 271 | a.dp += delta; |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 272 | trim(a); |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 273 | } |
| 274 | |
| 275 | // Binary shift left (k > 0) or right (k < 0). |
| 276 | // Returns receiver for convenience. |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 277 | func (a *decimal) Shift(k int) *decimal { |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 278 | switch { |
Russ Cox | cf9b7f7 | 2008-11-19 12:50:34 -0800 | [diff] [blame] | 279 | case a.nd == 0: |
| 280 | // nothing to do: a == 0 |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 281 | case k > 0: |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 282 | for k > maxShift { |
| 283 | leftShift(a, maxShift); |
| 284 | k -= maxShift; |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 285 | } |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 286 | leftShift(a, uint(k)); |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 287 | case k < 0: |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 288 | for k < -maxShift { |
| 289 | rightShift(a, maxShift); |
| 290 | k += maxShift; |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 291 | } |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 292 | rightShift(a, uint(-k)); |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 293 | } |
| 294 | return a; |
| 295 | } |
| 296 | |
| 297 | // If we chop a at nd digits, should we round up? |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 298 | func shouldRoundUp(a *decimal, nd int) bool { |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 299 | if nd <= 0 || nd >= a.nd { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 300 | return false |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 301 | } |
| 302 | if a.d[nd] == '5' && nd+1 == a.nd { // exactly halfway - round to even |
Robert Griesemer | 3bb0032 | 2009-11-09 21:23:52 -0800 | [diff] [blame] | 303 | return (a.d[nd-1]-'0')%2 != 0 |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 304 | } |
| 305 | // not halfway - digit tells all |
| 306 | return a.d[nd] >= '5'; |
| 307 | } |
| 308 | |
| 309 | // Round a to nd digits (or fewer). |
| 310 | // Returns receiver for convenience. |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 311 | func (a *decimal) Round(nd int) *decimal { |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 312 | if nd <= 0 || nd >= a.nd { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 313 | return a |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 314 | } |
Russ Cox | a50cbf6 | 2009-06-24 20:12:50 -0700 | [diff] [blame] | 315 | if shouldRoundUp(a, nd) { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 316 | return a.RoundUp(nd) |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 317 | } |
| 318 | return a.RoundDown(nd); |
| 319 | } |
| 320 | |
| 321 | // Round a down to nd digits (or fewer). |
| 322 | // Returns receiver for convenience. |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 323 | func (a *decimal) RoundDown(nd int) *decimal { |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 324 | if nd <= 0 || nd >= a.nd { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 325 | return a |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 326 | } |
| 327 | a.nd = nd; |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 328 | trim(a); |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 329 | return a; |
| 330 | } |
| 331 | |
| 332 | // Round a up to nd digits (or fewer). |
| 333 | // Returns receiver for convenience. |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 334 | func (a *decimal) RoundUp(nd int) *decimal { |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 335 | if nd <= 0 || nd >= a.nd { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 336 | return a |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 337 | } |
| 338 | |
| 339 | // round up |
Robert Griesemer | 3bb0032 | 2009-11-09 21:23:52 -0800 | [diff] [blame] | 340 | for i := nd - 1; i >= 0; i-- { |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 341 | c := a.d[i]; |
Robert Griesemer | 314b358 | 2009-11-04 23:20:49 -0800 | [diff] [blame] | 342 | if c < '9' { // can stop after this digit |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 343 | a.d[i]++; |
Robert Griesemer | 3bb0032 | 2009-11-09 21:23:52 -0800 | [diff] [blame] | 344 | a.nd = i + 1; |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 345 | return a; |
| 346 | } |
| 347 | } |
| 348 | |
| 349 | // Number is all 9s. |
| 350 | // Change to single 1 with adjusted decimal point. |
| 351 | a.d[0] = '1'; |
| 352 | a.nd = 1; |
| 353 | a.dp++; |
| 354 | return a; |
| 355 | } |
| 356 | |
| 357 | // Extract integer part, rounded appropriately. |
| 358 | // No guarantees about overflow. |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 359 | func (a *decimal) RoundedInteger() uint64 { |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 360 | if a.dp > 20 { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 361 | return 0xFFFFFFFFFFFFFFFF |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 362 | } |
| 363 | var i int; |
| 364 | n := uint64(0); |
| 365 | for i = 0; i < a.dp && i < a.nd; i++ { |
Robert Griesemer | 3bb0032 | 2009-11-09 21:23:52 -0800 | [diff] [blame] | 366 | n = n*10 + uint64(a.d[i]-'0') |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 367 | } |
| 368 | for ; i < a.dp; i++ { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 369 | n *= 10 |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 370 | } |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 371 | if shouldRoundUp(a, a.dp) { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 372 | n++ |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 373 | } |
| 374 | return n; |
| 375 | } |