github.com/rsc/go@v0.0.0-20150416155037-e040fd465409/src/math/big/decimal.go (about) 1 // Copyright 2015 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // This file implements multi-precision decimal numbers. 6 // The implementation is for float to decimal conversion only; 7 // not general purpose use. 8 // The only operations are precise conversion from binary to 9 // decimal and rounding. 10 // 11 // The key observation and some code (shr) is borrowed from 12 // strconv/decimal.go: conversion of binary fractional values can be done 13 // precisely in multi-precision decimal because 2 divides 10 (required for 14 // >> of mantissa); but conversion of decimal floating-point values cannot 15 // be done precisely in binary representation. 16 // 17 // In contrast to strconv/decimal.go, only right shift is implemented in 18 // decimal format - left shift can be done precisely in binary format. 19 20 package big 21 22 // A decimal represents a floating-point number in decimal representation. 23 // The value of a decimal x is x.mant * 10 ** x.exp with 0.5 <= x.mant < 1, 24 // with the most-significant mantissa digit at index 0. 25 type decimal struct { 26 mant []byte // mantissa ASCII digits, big-endian 27 exp int // exponent, valid if len(mant) > 0 28 } 29 30 // Maximum shift amount that can be done in one pass without overflow. 31 // A Word has _W bits and (1<<maxShift - 1)*10 + 9 must fit into Word. 32 const maxShift = _W - 4 33 34 // TODO(gri) Since we know the desired decimal precision when converting 35 // a floating-point number, we may be able to limit the number of decimal 36 // digits that need to be computed by init by providing an additional 37 // precision argument and keeping track of when a number was truncated early 38 // (equivalent of "sticky bit" in binary rounding). 39 40 // TODO(gri) Along the same lines, enforce some limit to shift magnitudes 41 // to avoid "infinitely" long running conversions (until we run out of space). 42 43 // Init initializes x to the decimal representation of m << shift (for 44 // shift >= 0), or m >> -shift (for shift < 0). 45 func (x *decimal) init(m nat, shift int) { 46 // special case 0 47 if len(m) == 0 { 48 x.mant = x.mant[:0] 49 return 50 } 51 52 // Optimization: If we need to shift right, first remove any trailing 53 // zero bits from m to reduce shift amount that needs to be done in 54 // decimal format (since that is likely slower). 55 if shift < 0 { 56 ntz := m.trailingZeroBits() 57 s := uint(-shift) 58 if s >= ntz { 59 s = ntz // shift at most ntz bits 60 } 61 m = nat(nil).shr(m, s) 62 shift += int(s) 63 } 64 65 // Do any shift left in binary representation. 66 if shift > 0 { 67 m = nat(nil).shl(m, uint(shift)) 68 shift = 0 69 } 70 71 // Convert mantissa into decimal representation. 72 s := m.decimalString() // TODO(gri) avoid string conversion here 73 n := len(s) 74 x.exp = n 75 // Trim trailing zeros; instead the exponent is tracking 76 // the decimal point independent of the number of digits. 77 for n > 0 && s[n-1] == '0' { 78 n-- 79 } 80 x.mant = append(x.mant[:0], s[:n]...) 81 82 // Do any (remaining) shift right in decimal representation. 83 if shift < 0 { 84 for shift < -maxShift { 85 shr(x, maxShift) 86 shift += maxShift 87 } 88 shr(x, uint(-shift)) 89 } 90 } 91 92 // Possibly optimization: The current implementation of nat.string takes 93 // a charset argument. When a right shift is needed, we could provide 94 // "\x00\x01...\x09" instead of "012..9" (as in nat.decimalString) and 95 // avoid the repeated +'0' and -'0' operations in decimal.shr (and do a 96 // single +'0' pass at the end). 97 98 // shr implements x >> s, for s <= maxShift. 99 func shr(x *decimal, s uint) { 100 // Division by 1<<s using shift-and-subtract algorithm. 101 102 // pick up enough leading digits to cover first shift 103 r := 0 // read index 104 var n Word 105 for n>>s == 0 && r < len(x.mant) { 106 ch := Word(x.mant[r]) 107 r++ 108 n = n*10 + ch - '0' 109 } 110 if n == 0 { 111 // x == 0; shouldn't get here, but handle anyway 112 x.mant = x.mant[:0] 113 return 114 } 115 for n>>s == 0 { 116 r++ 117 n *= 10 118 } 119 x.exp += 1 - r 120 121 // read a digit, write a digit 122 w := 0 // write index 123 for r < len(x.mant) { 124 ch := Word(x.mant[r]) 125 r++ 126 d := n >> s 127 n -= d << s 128 x.mant[w] = byte(d + '0') 129 w++ 130 n = n*10 + ch - '0' 131 } 132 133 // write extra digits that still fit 134 for n > 0 && w < len(x.mant) { 135 d := n >> s 136 n -= d << s 137 x.mant[w] = byte(d + '0') 138 w++ 139 n = n * 10 140 } 141 x.mant = x.mant[:w] // the number may be shorter (e.g. 1024 >> 10) 142 143 // append additional digits that didn't fit 144 for n > 0 { 145 d := n >> s 146 n -= d << s 147 x.mant = append(x.mant, byte(d+'0')) 148 n = n * 10 149 } 150 151 trim(x) 152 } 153 154 func (x *decimal) String() string { 155 if len(x.mant) == 0 { 156 return "0" 157 } 158 159 var buf []byte 160 switch { 161 case x.exp <= 0: 162 // 0.00ddd 163 buf = append(buf, "0."...) 164 buf = appendZeros(buf, -x.exp) 165 buf = append(buf, x.mant...) 166 167 case /* 0 < */ x.exp < len(x.mant): 168 // dd.ddd 169 buf = append(buf, x.mant[:x.exp]...) 170 buf = append(buf, '.') 171 buf = append(buf, x.mant[x.exp:]...) 172 173 default: // len(x.mant) <= x.exp 174 // ddd00 175 buf = append(buf, x.mant...) 176 buf = appendZeros(buf, x.exp-len(x.mant)) 177 } 178 179 return string(buf) 180 } 181 182 // appendZeros appends n 0 digits to buf and returns buf. 183 func appendZeros(buf []byte, n int) []byte { 184 for ; n > 0; n-- { 185 buf = append(buf, '0') 186 } 187 return buf 188 } 189 190 // shouldRoundUp reports if x should be rounded up 191 // if shortened to n digits. n must be a valid index 192 // for x.mant. 193 func shouldRoundUp(x *decimal, n int) bool { 194 if x.mant[n] == '5' && n+1 == len(x.mant) { 195 // exactly halfway - round to even 196 return n > 0 && (x.mant[n-1]-'0')&1 != 0 197 } 198 // not halfway - digit tells all (x.mant has no trailing zeros) 199 return x.mant[n] >= '5' 200 } 201 202 // round sets x to (at most) n mantissa digits by rounding it 203 // to the nearest even value with n (or fever) mantissa digits. 204 // If n < 0, x remains unchanged. 205 func (x *decimal) round(n int) { 206 if n < 0 || n >= len(x.mant) { 207 return // nothing to do 208 } 209 210 if shouldRoundUp(x, n) { 211 x.roundUp(n) 212 } else { 213 x.roundDown(n) 214 } 215 } 216 217 func (x *decimal) roundUp(n int) { 218 if n < 0 || n >= len(x.mant) { 219 return // nothing to do 220 } 221 // 0 <= n < len(x.mant) 222 223 // find first digit < '9' 224 for n > 0 && x.mant[n-1] >= '9' { 225 n-- 226 } 227 228 if n == 0 { 229 // all digits are '9's => round up to '1' and update exponent 230 x.mant[0] = '1' // ok since len(x.mant) > n 231 x.mant = x.mant[:1] 232 x.exp++ 233 return 234 } 235 236 // n > 0 && x.mant[n-1] < '9' 237 x.mant[n-1]++ 238 x.mant = x.mant[:n] 239 // x already trimmed 240 } 241 242 func (x *decimal) roundDown(n int) { 243 if n < 0 || n >= len(x.mant) { 244 return // nothing to do 245 } 246 x.mant = x.mant[:n] 247 trim(x) 248 } 249 250 // trim cuts off any trailing zeros from x's mantissa; 251 // they are meaningless for the value of x. 252 func trim(x *decimal) { 253 i := len(x.mant) 254 for i > 0 && x.mant[i-1] == '0' { 255 i-- 256 } 257 x.mant = x.mant[:i] 258 }