github.com/pingcap/tidb/parser@v0.0.0-20231013125129-93a834a6bf8d/test_driver/test_driver_mydecimal.go (about) 1 // Copyright 2019 PingCAP, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 //go:build !codes 15 // +build !codes 16 17 package test_driver 18 19 const panicInfo = "This branch is not implemented. " + 20 "This is because you are trying to test something specific to TiDB's MyDecimal implementation. " + 21 "It is recommended to do this in TiDB repository." 22 23 // constant values. 24 const ( 25 maxWordBufLen = 9 // A MyDecimal holds 9 words. 26 digitsPerWord = 9 // A word holds 9 digits. 27 digMask = 100000000 28 ) 29 30 var ( 31 wordBufLen = 9 32 ) 33 34 // fixWordCntError limits word count in wordBufLen, and returns overflow or truncate error. 35 func fixWordCntError(wordsInt, wordsFrac int) (newWordsInt int, newWordsFrac int, err error) { 36 if wordsInt+wordsFrac > wordBufLen { 37 panic(panicInfo) 38 } 39 return wordsInt, wordsFrac, nil 40 } 41 42 /* 43 countLeadingZeroes returns the number of leading zeroes that can be removed from fraction. 44 45 @param i start index 46 @param word value to compare against list of powers of 10 47 */ 48 func countLeadingZeroes(i int, word int32) int { 49 leading := 0 50 for word < pow10(i) { 51 i-- 52 leading++ 53 } 54 return leading 55 } 56 57 func digitsToWords(digits int) int { 58 return (digits + digitsPerWord - 1) / digitsPerWord 59 } 60 61 // MyDecimal represents a decimal value. 62 type MyDecimal struct { 63 digitsInt int8 // the number of *decimal* digits before the point. 64 65 digitsFrac int8 // the number of decimal digits after the point. 66 67 resultFrac int8 // result fraction digits. 68 69 negative bool 70 71 // wordBuf is an array of int32 words. 72 // A word is an int32 value can hold 9 digits.(0 <= word < wordBase) 73 wordBuf [maxWordBufLen]int32 74 } 75 76 // String returns the decimal string representation rounded to resultFrac. 77 func (d *MyDecimal) String() string { 78 tmp := *d 79 return string(tmp.ToString()) 80 } 81 82 func (d *MyDecimal) stringSize() int { 83 // sign, zero integer and dot. 84 return int(d.digitsInt + d.digitsFrac + 3) 85 } 86 87 func (d *MyDecimal) removeLeadingZeros() (wordIdx int, digitsInt int) { 88 digitsInt = int(d.digitsInt) 89 i := ((digitsInt - 1) % digitsPerWord) + 1 90 for digitsInt > 0 && d.wordBuf[wordIdx] == 0 { 91 digitsInt -= i 92 i = digitsPerWord 93 wordIdx++ 94 } 95 if digitsInt > 0 { 96 digitsInt -= countLeadingZeroes((digitsInt-1)%digitsPerWord, d.wordBuf[wordIdx]) 97 } else { 98 digitsInt = 0 99 } 100 return 101 } 102 103 // ToString converts decimal to its printable string representation without rounding. 104 // 105 // RETURN VALUE 106 // 107 // str - result string 108 // errCode - eDecOK/eDecTruncate/eDecOverflow 109 func (d *MyDecimal) ToString() (str []byte) { 110 str = make([]byte, d.stringSize()) 111 digitsFrac := int(d.digitsFrac) 112 wordStartIdx, digitsInt := d.removeLeadingZeros() 113 if digitsInt+digitsFrac == 0 { 114 digitsInt = 1 115 wordStartIdx = 0 116 } 117 118 digitsIntLen := digitsInt 119 if digitsIntLen == 0 { 120 digitsIntLen = 1 121 } 122 digitsFracLen := digitsFrac 123 length := digitsIntLen + digitsFracLen 124 if d.negative { 125 length++ 126 } 127 if digitsFrac > 0 { 128 length++ 129 } 130 str = str[:length] 131 strIdx := 0 132 if d.negative { 133 str[strIdx] = '-' 134 strIdx++ 135 } 136 var fill int 137 if digitsFrac > 0 { 138 fracIdx := strIdx + digitsIntLen 139 fill = digitsFracLen - digitsFrac 140 wordIdx := wordStartIdx + digitsToWords(digitsInt) 141 str[fracIdx] = '.' 142 fracIdx++ 143 for ; digitsFrac > 0; digitsFrac -= digitsPerWord { 144 x := d.wordBuf[wordIdx] 145 wordIdx++ 146 for i := myMin(digitsFrac, digitsPerWord); i > 0; i-- { 147 y := x / digMask 148 str[fracIdx] = byte(y) + '0' 149 fracIdx++ 150 x -= y * digMask 151 x *= 10 152 } 153 } 154 for ; fill > 0; fill-- { 155 str[fracIdx] = '0' 156 fracIdx++ 157 } 158 } 159 fill = digitsIntLen - digitsInt 160 if digitsInt == 0 { 161 fill-- /* symbol 0 before digital point */ 162 } 163 for ; fill > 0; fill-- { 164 str[strIdx] = '0' 165 strIdx++ 166 } 167 if digitsInt > 0 { 168 strIdx += digitsInt 169 wordIdx := wordStartIdx + digitsToWords(digitsInt) 170 for ; digitsInt > 0; digitsInt -= digitsPerWord { 171 wordIdx-- 172 x := d.wordBuf[wordIdx] 173 for i := myMin(digitsInt, digitsPerWord); i > 0; i-- { 174 y := x / 10 175 strIdx-- 176 str[strIdx] = '0' + byte(x-y*10) 177 x = y 178 } 179 } 180 } else { 181 str[strIdx] = '0' 182 } 183 return 184 } 185 186 // FromString parses decimal from string. 187 func (d *MyDecimal) FromString(str []byte) error { 188 for i := 0; i < len(str); i++ { 189 if !isSpace(str[i]) { 190 str = str[i:] 191 break 192 } 193 } 194 if len(str) == 0 { 195 panic(panicInfo) 196 } 197 switch str[0] { 198 case '-': 199 d.negative = true 200 fallthrough 201 case '+': 202 str = str[1:] 203 } 204 var strIdx int 205 for strIdx < len(str) && isDigit(str[strIdx]) { 206 strIdx++ 207 } 208 digitsInt := strIdx 209 var digitsFrac int 210 var endIdx int 211 if strIdx < len(str) && str[strIdx] == '.' { 212 endIdx = strIdx + 1 213 for endIdx < len(str) && isDigit(str[endIdx]) { 214 endIdx++ 215 } 216 digitsFrac = endIdx - strIdx - 1 217 } else { 218 digitsFrac = 0 219 endIdx = strIdx 220 } 221 if digitsInt+digitsFrac == 0 { 222 panic(panicInfo) 223 } 224 wordsInt := digitsToWords(digitsInt) 225 wordsFrac := digitsToWords(digitsFrac) 226 wordsInt, _, err := fixWordCntError(wordsInt, wordsFrac) 227 if err != nil { 228 panic(panicInfo) 229 } 230 d.digitsInt = int8(digitsInt) 231 d.digitsFrac = int8(digitsFrac) 232 wordIdx := wordsInt 233 strIdxTmp := strIdx 234 var word int32 235 var innerIdx int 236 for digitsInt > 0 { 237 digitsInt-- 238 strIdx-- 239 word += int32(str[strIdx]-'0') * pow10(innerIdx) 240 innerIdx++ 241 if innerIdx == digitsPerWord { 242 wordIdx-- 243 d.wordBuf[wordIdx] = word 244 word = 0 245 innerIdx = 0 246 } 247 } 248 if innerIdx != 0 { 249 wordIdx-- 250 d.wordBuf[wordIdx] = word 251 } 252 253 wordIdx = wordsInt 254 strIdx = strIdxTmp 255 word = 0 256 innerIdx = 0 257 for digitsFrac > 0 { 258 digitsFrac-- 259 strIdx++ 260 word = int32(str[strIdx]-'0') + word*10 261 innerIdx++ 262 if innerIdx == digitsPerWord { 263 d.wordBuf[wordIdx] = word 264 wordIdx++ 265 word = 0 266 innerIdx = 0 267 } 268 } 269 if innerIdx != 0 { 270 d.wordBuf[wordIdx] = word * pow10(digitsPerWord-innerIdx) 271 } 272 if endIdx+1 <= len(str) && (str[endIdx] == 'e' || str[endIdx] == 'E') { 273 panic(panicInfo) 274 } 275 allZero := true 276 for i := 0; i < wordBufLen; i++ { 277 if d.wordBuf[i] != 0 { 278 allZero = false 279 break 280 } 281 } 282 if allZero { 283 d.negative = false 284 } 285 d.resultFrac = d.digitsFrac 286 return err 287 }