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 { |
Robert Griesemer | 9209d89 | 2015-02-02 16:42:40 -0800 | [diff] [blame] | 15 | d [800]byte // digits, big-endian representation |
Russ Cox | 3ee2085 | 2012-02-07 23:37:15 -0500 | [diff] [blame] | 16 | nd int // number of digits used |
| 17 | dp int // decimal point |
Adam Kisala | 8cb40fa | 2017-07-15 19:27:56 +0100 | [diff] [blame] | 18 | neg bool // negative flag |
| 19 | trunc bool // discarded nonzero digits beyond d[:nd] |
Robert Griesemer | 314b358 | 2009-11-04 23:20:49 -0800 | [diff] [blame] | 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 { |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 23 | n := 10 + a.nd |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 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 | |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 31 | buf := make([]byte, n) |
| 32 | w := 0 |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 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 |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 39 | buf[w] = '0' |
| 40 | w++ |
| 41 | buf[w] = '.' |
| 42 | w++ |
| 43 | w += digitZero(buf[w : w+-a.dp]) |
Russ Cox | 7d7ebd2 | 2010-05-03 17:47:40 -0700 | [diff] [blame] | 44 | w += copy(buf[w:], 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 |
Russ Cox | 7d7ebd2 | 2010-05-03 17:47:40 -0700 | [diff] [blame] | 48 | w += copy(buf[w:], a.d[0:a.dp]) |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 49 | buf[w] = '.' |
| 50 | w++ |
Russ Cox | 7d7ebd2 | 2010-05-03 17:47:40 -0700 | [diff] [blame] | 51 | w += copy(buf[w:], 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 |
Russ Cox | 7d7ebd2 | 2010-05-03 17:47:40 -0700 | [diff] [blame] | 55 | w += copy(buf[w:], a.d[0:a.nd]) |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -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 | } |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 58 | return string(buf[0:w]) |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 59 | } |
| 60 | |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 61 | func digitZero(dst []byte) int { |
Russ Cox | 7d7ebd2 | 2010-05-03 17:47:40 -0700 | [diff] [blame] | 62 | for i := range dst { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 63 | dst[i] = '0' |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 64 | } |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 65 | return len(dst) |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 66 | } |
| 67 | |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 68 | // trim trailing zeros from number. |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 69 | // (They are meaningless; the decimal point is tracked |
| 70 | // independent of the number of digits.) |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 71 | func trim(a *decimal) { |
Robert Griesemer | 3bb0032 | 2009-11-09 21:23:52 -0800 | [diff] [blame] | 72 | for a.nd > 0 && a.d[a.nd-1] == '0' { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 73 | a.nd-- |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 74 | } |
| 75 | if a.nd == 0 { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 76 | a.dp = 0 |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 77 | } |
| 78 | } |
| 79 | |
| 80 | // Assign v to a. |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 81 | func (a *decimal) Assign(v uint64) { |
Rémy Oudompheng | d6147d8 | 2012-07-10 07:44:23 +0200 | [diff] [blame] | 82 | var buf [24]byte |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 83 | |
| 84 | // Write reversed decimal in buf. |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 85 | n := 0 |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 86 | for v > 0 { |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 87 | v1 := v / 10 |
| 88 | v -= 10 * v1 |
| 89 | buf[n] = byte(v + '0') |
| 90 | n++ |
| 91 | v = v1 |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 92 | } |
| 93 | |
| 94 | // Reverse again to produce forward decimal in a.d. |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 95 | a.nd = 0 |
Robert Griesemer | 314b358 | 2009-11-04 23:20:49 -0800 | [diff] [blame] | 96 | for n--; n >= 0; n-- { |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 97 | a.d[a.nd] = buf[n] |
| 98 | a.nd++ |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 99 | } |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 100 | a.dp = a.nd |
| 101 | trim(a) |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 102 | } |
| 103 | |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 104 | // Maximum shift that we can do in one pass without overflow. |
Robert Griesemer | faa9d1e | 2015-02-02 17:22:18 -0800 | [diff] [blame] | 105 | // A uint has 32 or 64 bits, and we have to be able to accommodate 9<<k. |
| 106 | const uintSize = 32 << (^uint(0) >> 63) |
| 107 | const maxShift = uintSize - 4 |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 108 | |
Robert Griesemer | 9209d89 | 2015-02-02 16:42:40 -0800 | [diff] [blame] | 109 | // Binary shift right (/ 2) by k bits. k <= maxShift to avoid overflow. |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 110 | func rightShift(a *decimal, k uint) { |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 111 | r := 0 // read pointer |
| 112 | w := 0 // write pointer |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 113 | |
| 114 | // Pick up enough leading digits to cover first shift. |
Robert Griesemer | faa9d1e | 2015-02-02 17:22:18 -0800 | [diff] [blame] | 115 | var n uint |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 116 | for ; n>>k == 0; r++ { |
| 117 | if r >= a.nd { |
| 118 | if n == 0 { |
Russ Cox | cf9b7f7 | 2008-11-19 12:50:34 -0800 | [diff] [blame] | 119 | // a == 0; shouldn't get here, but handle anyway. |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 120 | a.nd = 0 |
| 121 | return |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 122 | } |
Russ Cox | cf9b7f7 | 2008-11-19 12:50:34 -0800 | [diff] [blame] | 123 | for n>>k == 0 { |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 124 | n = n * 10 |
| 125 | r++ |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 126 | } |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 127 | break |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 128 | } |
Robert Griesemer | faa9d1e | 2015-02-02 17:22:18 -0800 | [diff] [blame] | 129 | c := uint(a.d[r]) |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 130 | n = n*10 + c - '0' |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 131 | } |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 132 | a.dp -= r - 1 |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 133 | |
Alberto Donizetti | cbf28ff | 2016-10-01 14:01:09 +0200 | [diff] [blame] | 134 | var mask uint = (1 << k) - 1 |
| 135 | |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 136 | // Pick up a digit, put down a digit. |
| 137 | for ; r < a.nd; r++ { |
Robert Griesemer | faa9d1e | 2015-02-02 17:22:18 -0800 | [diff] [blame] | 138 | c := uint(a.d[r]) |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 139 | dig := n >> k |
Alberto Donizetti | cbf28ff | 2016-10-01 14:01:09 +0200 | [diff] [blame] | 140 | n &= mask |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 141 | a.d[w] = byte(dig + '0') |
| 142 | w++ |
| 143 | n = n*10 + c - '0' |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 144 | } |
| 145 | |
| 146 | // Put down extra digits. |
| 147 | for n > 0 { |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 148 | dig := n >> k |
Alberto Donizetti | cbf28ff | 2016-10-01 14:01:09 +0200 | [diff] [blame] | 149 | n &= mask |
Russ Cox | 3ee2085 | 2012-02-07 23:37:15 -0500 | [diff] [blame] | 150 | if w < len(a.d) { |
| 151 | a.d[w] = byte(dig + '0') |
| 152 | w++ |
| 153 | } else if dig > 0 { |
| 154 | a.trunc = true |
| 155 | } |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 156 | n = n * 10 |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 157 | } |
| 158 | |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 159 | a.nd = w |
| 160 | trim(a) |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 161 | } |
| 162 | |
| 163 | // Cheat sheet for left shift: table indexed by shift count giving |
| 164 | // number of new digits that will be introduced by that shift. |
| 165 | // |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 166 | // For example, leftcheats[4] = {2, "625"}. That means that |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 167 | // if we are shifting by 4 (multiplying by 16), it will add 2 digits |
| 168 | // when the string prefix is "625" through "999", and one fewer digit |
| 169 | // if the string prefix is "000" through "624". |
| 170 | // |
| 171 | // Credit for this trick goes to Ken. |
| 172 | |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 173 | type leftCheat struct { |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 174 | delta int // number of new digits |
Robert Griesemer | faa9d1e | 2015-02-02 17:22:18 -0800 | [diff] [blame] | 175 | cutoff string // minus one digit if original < a. |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 176 | } |
| 177 | |
Robert Griesemer | 314b358 | 2009-11-04 23:20:49 -0800 | [diff] [blame] | 178 | var leftcheats = []leftCheat{ |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 179 | // Leading digits of 1/2^i = 5^i. |
| 180 | // 5^23 is not an exact 64-bit floating point number, |
| 181 | // so have to use bc for the math. |
Robert Griesemer | faa9d1e | 2015-02-02 17:22:18 -0800 | [diff] [blame] | 182 | // Go up to 60 to be large enough for 32bit and 64bit platforms. |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 183 | /* |
Robert Griesemer | faa9d1e | 2015-02-02 17:22:18 -0800 | [diff] [blame] | 184 | seq 60 | sed 's/^/5^/' | bc | |
| 185 | awk 'BEGIN{ print "\t{ 0, \"\" }," } |
Robert Griesemer | 314b358 | 2009-11-04 23:20:49 -0800 | [diff] [blame] | 186 | { |
| 187 | log2 = log(2)/log(10) |
Robert Griesemer | faa9d1e | 2015-02-02 17:22:18 -0800 | [diff] [blame] | 188 | printf("\t{ %d, \"%s\" },\t// * %d\n", |
Robert Griesemer | 314b358 | 2009-11-04 23:20:49 -0800 | [diff] [blame] | 189 | int(log2*NR+1), $0, 2**NR) |
| 190 | }' |
| 191 | */ |
Robert Griesemer | 3478891 | 2010-10-22 10:06:33 -0700 | [diff] [blame] | 192 | {0, ""}, |
Robert Griesemer | faa9d1e | 2015-02-02 17:22:18 -0800 | [diff] [blame] | 193 | {1, "5"}, // * 2 |
| 194 | {1, "25"}, // * 4 |
| 195 | {1, "125"}, // * 8 |
| 196 | {2, "625"}, // * 16 |
| 197 | {2, "3125"}, // * 32 |
| 198 | {2, "15625"}, // * 64 |
| 199 | {3, "78125"}, // * 128 |
| 200 | {3, "390625"}, // * 256 |
| 201 | {3, "1953125"}, // * 512 |
| 202 | {4, "9765625"}, // * 1024 |
| 203 | {4, "48828125"}, // * 2048 |
| 204 | {4, "244140625"}, // * 4096 |
| 205 | {4, "1220703125"}, // * 8192 |
| 206 | {5, "6103515625"}, // * 16384 |
| 207 | {5, "30517578125"}, // * 32768 |
| 208 | {5, "152587890625"}, // * 65536 |
| 209 | {6, "762939453125"}, // * 131072 |
| 210 | {6, "3814697265625"}, // * 262144 |
| 211 | {6, "19073486328125"}, // * 524288 |
| 212 | {7, "95367431640625"}, // * 1048576 |
| 213 | {7, "476837158203125"}, // * 2097152 |
| 214 | {7, "2384185791015625"}, // * 4194304 |
| 215 | {7, "11920928955078125"}, // * 8388608 |
| 216 | {8, "59604644775390625"}, // * 16777216 |
| 217 | {8, "298023223876953125"}, // * 33554432 |
| 218 | {8, "1490116119384765625"}, // * 67108864 |
| 219 | {9, "7450580596923828125"}, // * 134217728 |
| 220 | {9, "37252902984619140625"}, // * 268435456 |
| 221 | {9, "186264514923095703125"}, // * 536870912 |
| 222 | {10, "931322574615478515625"}, // * 1073741824 |
| 223 | {10, "4656612873077392578125"}, // * 2147483648 |
| 224 | {10, "23283064365386962890625"}, // * 4294967296 |
| 225 | {10, "116415321826934814453125"}, // * 8589934592 |
| 226 | {11, "582076609134674072265625"}, // * 17179869184 |
| 227 | {11, "2910383045673370361328125"}, // * 34359738368 |
| 228 | {11, "14551915228366851806640625"}, // * 68719476736 |
| 229 | {12, "72759576141834259033203125"}, // * 137438953472 |
| 230 | {12, "363797880709171295166015625"}, // * 274877906944 |
| 231 | {12, "1818989403545856475830078125"}, // * 549755813888 |
| 232 | {13, "9094947017729282379150390625"}, // * 1099511627776 |
| 233 | {13, "45474735088646411895751953125"}, // * 2199023255552 |
| 234 | {13, "227373675443232059478759765625"}, // * 4398046511104 |
| 235 | {13, "1136868377216160297393798828125"}, // * 8796093022208 |
| 236 | {14, "5684341886080801486968994140625"}, // * 17592186044416 |
| 237 | {14, "28421709430404007434844970703125"}, // * 35184372088832 |
| 238 | {14, "142108547152020037174224853515625"}, // * 70368744177664 |
| 239 | {15, "710542735760100185871124267578125"}, // * 140737488355328 |
| 240 | {15, "3552713678800500929355621337890625"}, // * 281474976710656 |
| 241 | {15, "17763568394002504646778106689453125"}, // * 562949953421312 |
| 242 | {16, "88817841970012523233890533447265625"}, // * 1125899906842624 |
| 243 | {16, "444089209850062616169452667236328125"}, // * 2251799813685248 |
| 244 | {16, "2220446049250313080847263336181640625"}, // * 4503599627370496 |
| 245 | {16, "11102230246251565404236316680908203125"}, // * 9007199254740992 |
| 246 | {17, "55511151231257827021181583404541015625"}, // * 18014398509481984 |
| 247 | {17, "277555756156289135105907917022705078125"}, // * 36028797018963968 |
| 248 | {17, "1387778780781445675529539585113525390625"}, // * 72057594037927936 |
| 249 | {18, "6938893903907228377647697925567626953125"}, // * 144115188075855872 |
| 250 | {18, "34694469519536141888238489627838134765625"}, // * 288230376151711744 |
| 251 | {18, "173472347597680709441192448139190673828125"}, // * 576460752303423488 |
| 252 | {19, "867361737988403547205962240695953369140625"}, // * 1152921504606846976 |
Russ Cox | be2edb5 | 2009-03-03 08:39:12 -0800 | [diff] [blame] | 253 | } |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 254 | |
| 255 | // Is the leading prefix of b lexicographically less than s? |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 256 | func prefixIsLessThan(b []byte, s string) bool { |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 257 | for i := 0; i < len(s); i++ { |
| 258 | if i >= len(b) { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 259 | return true |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 260 | } |
| 261 | if b[i] != s[i] { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 262 | return b[i] < s[i] |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 263 | } |
| 264 | } |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 265 | return false |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 266 | } |
| 267 | |
Robert Griesemer | 9209d89 | 2015-02-02 16:42:40 -0800 | [diff] [blame] | 268 | // Binary shift left (* 2) by k bits. k <= maxShift to avoid overflow. |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 269 | func leftShift(a *decimal, k uint) { |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 270 | delta := leftcheats[k].delta |
Robert Griesemer | 3bb0032 | 2009-11-09 21:23:52 -0800 | [diff] [blame] | 271 | if prefixIsLessThan(a.d[0:a.nd], leftcheats[k].cutoff) { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 272 | delta-- |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 273 | } |
| 274 | |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 275 | r := a.nd // read index |
| 276 | w := a.nd + delta // write index |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 277 | |
| 278 | // Pick up a digit, put down a digit. |
Robert Griesemer | faa9d1e | 2015-02-02 17:22:18 -0800 | [diff] [blame] | 279 | var n uint |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 280 | for r--; r >= 0; r-- { |
Robert Griesemer | faa9d1e | 2015-02-02 17:22:18 -0800 | [diff] [blame] | 281 | n += (uint(a.d[r]) - '0') << k |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 282 | quo := n / 10 |
| 283 | rem := n - 10*quo |
| 284 | w-- |
Russ Cox | 3ee2085 | 2012-02-07 23:37:15 -0500 | [diff] [blame] | 285 | if w < len(a.d) { |
| 286 | a.d[w] = byte(rem + '0') |
| 287 | } else if rem != 0 { |
| 288 | a.trunc = true |
| 289 | } |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 290 | n = quo |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 291 | } |
| 292 | |
| 293 | // Put down extra digits. |
| 294 | for n > 0 { |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 295 | quo := n / 10 |
| 296 | rem := n - 10*quo |
| 297 | w-- |
Russ Cox | 3ee2085 | 2012-02-07 23:37:15 -0500 | [diff] [blame] | 298 | if w < len(a.d) { |
| 299 | a.d[w] = byte(rem + '0') |
| 300 | } else if rem != 0 { |
| 301 | a.trunc = true |
| 302 | } |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 303 | n = quo |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 304 | } |
| 305 | |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 306 | a.nd += delta |
Russ Cox | 3ee2085 | 2012-02-07 23:37:15 -0500 | [diff] [blame] | 307 | if a.nd >= len(a.d) { |
| 308 | a.nd = len(a.d) |
| 309 | } |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 310 | a.dp += delta |
| 311 | trim(a) |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 312 | } |
| 313 | |
| 314 | // Binary shift left (k > 0) or right (k < 0). |
Russ Cox | cb51fdc | 2011-08-25 17:54:14 -0400 | [diff] [blame] | 315 | func (a *decimal) Shift(k int) { |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 316 | switch { |
Russ Cox | cf9b7f7 | 2008-11-19 12:50:34 -0800 | [diff] [blame] | 317 | case a.nd == 0: |
| 318 | // nothing to do: a == 0 |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 319 | case k > 0: |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 320 | for k > maxShift { |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 321 | leftShift(a, maxShift) |
| 322 | k -= maxShift |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 323 | } |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 324 | leftShift(a, uint(k)) |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 325 | case k < 0: |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 326 | for k < -maxShift { |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 327 | rightShift(a, maxShift) |
| 328 | k += maxShift |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 329 | } |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 330 | rightShift(a, uint(-k)) |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 331 | } |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 332 | } |
| 333 | |
| 334 | // If we chop a at nd digits, should we round up? |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 335 | func shouldRoundUp(a *decimal, nd int) bool { |
Rob Pike | 64b6a78 | 2010-06-29 16:51:56 -0700 | [diff] [blame] | 336 | if nd < 0 || nd >= a.nd { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 337 | return false |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 338 | } |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 339 | if a.d[nd] == '5' && nd+1 == a.nd { // exactly halfway - round to even |
Russ Cox | 3ee2085 | 2012-02-07 23:37:15 -0500 | [diff] [blame] | 340 | // if we truncated, a little higher than what's recorded - always round up |
| 341 | if a.trunc { |
| 342 | return true |
| 343 | } |
Rob Pike | 64b6a78 | 2010-06-29 16:51:56 -0700 | [diff] [blame] | 344 | return nd > 0 && (a.d[nd-1]-'0')%2 != 0 |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 345 | } |
| 346 | // not halfway - digit tells all |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 347 | return a.d[nd] >= '5' |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 348 | } |
| 349 | |
| 350 | // Round a to nd digits (or fewer). |
Rob Pike | 64b6a78 | 2010-06-29 16:51:56 -0700 | [diff] [blame] | 351 | // If nd is zero, it means we're rounding |
| 352 | // just to the left of the digits, as in |
| 353 | // 0.09 -> 0.1. |
Russ Cox | 0ed5e6a | 2011-11-15 12:17:25 -0500 | [diff] [blame] | 354 | func (a *decimal) Round(nd int) { |
Rob Pike | 64b6a78 | 2010-06-29 16:51:56 -0700 | [diff] [blame] | 355 | if nd < 0 || nd >= a.nd { |
Russ Cox | 0ed5e6a | 2011-11-15 12:17:25 -0500 | [diff] [blame] | 356 | return |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 357 | } |
Russ Cox | a50cbf6 | 2009-06-24 20:12:50 -0700 | [diff] [blame] | 358 | if shouldRoundUp(a, nd) { |
Russ Cox | 0ed5e6a | 2011-11-15 12:17:25 -0500 | [diff] [blame] | 359 | a.RoundUp(nd) |
| 360 | } else { |
| 361 | a.RoundDown(nd) |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 362 | } |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 363 | } |
| 364 | |
| 365 | // Round a down to nd digits (or fewer). |
Russ Cox | 0ed5e6a | 2011-11-15 12:17:25 -0500 | [diff] [blame] | 366 | func (a *decimal) RoundDown(nd int) { |
Rob Pike | 64b6a78 | 2010-06-29 16:51:56 -0700 | [diff] [blame] | 367 | if nd < 0 || nd >= a.nd { |
Russ Cox | 0ed5e6a | 2011-11-15 12:17:25 -0500 | [diff] [blame] | 368 | return |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 369 | } |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 370 | a.nd = nd |
| 371 | trim(a) |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 372 | } |
| 373 | |
| 374 | // Round a up to nd digits (or fewer). |
Russ Cox | 0ed5e6a | 2011-11-15 12:17:25 -0500 | [diff] [blame] | 375 | func (a *decimal) RoundUp(nd int) { |
Rob Pike | 64b6a78 | 2010-06-29 16:51:56 -0700 | [diff] [blame] | 376 | if nd < 0 || nd >= a.nd { |
Russ Cox | 0ed5e6a | 2011-11-15 12:17:25 -0500 | [diff] [blame] | 377 | return |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 378 | } |
| 379 | |
| 380 | // round up |
Robert Griesemer | 3bb0032 | 2009-11-09 21:23:52 -0800 | [diff] [blame] | 381 | for i := nd - 1; i >= 0; i-- { |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 382 | c := a.d[i] |
| 383 | if c < '9' { // can stop after this digit |
| 384 | a.d[i]++ |
| 385 | a.nd = i + 1 |
Russ Cox | 0ed5e6a | 2011-11-15 12:17:25 -0500 | [diff] [blame] | 386 | return |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 387 | } |
| 388 | } |
| 389 | |
| 390 | // Number is all 9s. |
| 391 | // Change to single 1 with adjusted decimal point. |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 392 | a.d[0] = '1' |
| 393 | a.nd = 1 |
| 394 | a.dp++ |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 395 | } |
| 396 | |
| 397 | // Extract integer part, rounded appropriately. |
| 398 | // No guarantees about overflow. |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 399 | func (a *decimal) RoundedInteger() uint64 { |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 400 | if a.dp > 20 { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 401 | return 0xFFFFFFFFFFFFFFFF |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 402 | } |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 403 | var i int |
| 404 | n := uint64(0) |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 405 | for i = 0; i < a.dp && i < a.nd; i++ { |
Robert Griesemer | 3bb0032 | 2009-11-09 21:23:52 -0800 | [diff] [blame] | 406 | n = n*10 + uint64(a.d[i]-'0') |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 407 | } |
| 408 | for ; i < a.dp; i++ { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 409 | n *= 10 |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 410 | } |
Russ Cox | 8a7cbad | 2009-01-15 17:22:17 -0800 | [diff] [blame] | 411 | if shouldRoundUp(a, a.dp) { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 412 | n++ |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 413 | } |
Robert Griesemer | d65a5cc | 2009-12-15 15:40:16 -0800 | [diff] [blame] | 414 | return n |
Russ Cox | 079c00a | 2008-11-17 12:34:03 -0800 | [diff] [blame] | 415 | } |