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