gitee.com/curryzheng/dm@v0.0.1/p.go (about) 1 /* 2 * Copyright (c) 2000-2018, 达梦数据库有限公司. 3 * All rights reserved. 4 */ 5 package dm 6 7 import ( 8 "database/sql/driver" 9 "math/big" 10 "reflect" 11 "strconv" 12 "strings" 13 ) 14 15 const ( 16 XDEC_MAX_PREC int = 38 17 XDEC_SIZE = 21 18 19 FLAG_ZERO int = 0x80 20 FLAG_POSITIVE int = 0xC1 21 FLAG_NEGTIVE int = 0x3E 22 EXP_MAX int = 0xFF - 1 - FLAG_POSITIVE 23 EXP_MIN int = FLAG_NEGTIVE + 1 - 0x7F 24 25 NUM_POSITIVE int = 1 26 NUM_NEGTIVE int = 101 27 ) 28 29 type DmDecimal struct { 30 sign int 31 weight int 32 prec int 33 scale int 34 digits string 35 36 Valid bool 37 } 38 39 func NewDecimalFromInt64(x int64) (*DmDecimal, error) { 40 return NewDecimalFromBigInt(big.NewInt(x)) 41 } 42 43 func (d DmDecimal) ToInt64() int64 { 44 return d.ToBigInt().Int64() 45 } 46 47 func NewDecimalFromFloat64(x float64) (*DmDecimal, error) { 48 return NewDecimalFromBigFloat(big.NewFloat(x)) 49 } 50 51 func (d DmDecimal) ToFloat64() float64 { 52 f, _ := d.ToBigFloat().Float64() 53 return f 54 } 55 56 func NewDecimalFromBigInt(bigInt *big.Int) (*DmDecimal, error) { 57 return newDecimal(bigInt, len(bigInt.String()), 0) 58 } 59 60 func (d DmDecimal) ToBigInt() *big.Int { 61 if d.isZero() { 62 return big.NewInt(0) 63 } 64 var digits = d.digits 65 if d.sign < 0 { 66 digits = "-" + digits 67 } 68 i1, ok := new(big.Int).SetString(digits, 10) 69 if !ok { 70 return nil 71 } 72 if d.weight > 0 { 73 i2, ok := new(big.Int).SetString("1"+strings.Repeat("0", d.weight), 10) 74 if !ok { 75 return nil 76 } 77 i1.Mul(i1, i2) 78 } else if d.weight < 0 { 79 i2, ok := new(big.Int).SetString("1"+strings.Repeat("0", -d.weight), 10) 80 if !ok { 81 return nil 82 } 83 i1.Quo(i1, i2) 84 } 85 return i1 86 } 87 88 func NewDecimalFromBigFloat(bigFloat *big.Float) (*DmDecimal, error) { 89 return newDecimal(bigFloat, int(bigFloat.Prec()), int(bigFloat.Prec())) 90 } 91 92 func (d DmDecimal) ToBigFloat() *big.Float { 93 if d.isZero() { 94 return big.NewFloat(0.0) 95 } 96 var digits = d.digits 97 if d.sign < 0 { 98 digits = "-" + digits 99 } 100 f1, ok := new(big.Float).SetString(digits) 101 if !ok { 102 return nil 103 } 104 if d.weight > 0 { 105 f2, ok := new(big.Float).SetString("1" + strings.Repeat("0", d.weight)) 106 if !ok { 107 return nil 108 } 109 f1.Mul(f1, f2) 110 } else if d.weight < 0 { 111 f2, ok := new(big.Float).SetString("1" + strings.Repeat("0", -d.weight)) 112 if !ok { 113 return nil 114 } 115 f1.Quo(f1, f2) 116 } 117 return f1 118 } 119 120 func NewDecimalFromString(s string) (*DmDecimal, error) { 121 num, ok := new(big.Float).SetString(strings.TrimSpace(s)) 122 if !ok { 123 return nil, ECGO_DATA_CONVERTION_ERROR.throw() 124 } 125 return NewDecimalFromBigFloat(num) 126 } 127 128 func (d DmDecimal) String() string { 129 130 if d.isZero() { 131 return "0" 132 } 133 digitsStr := d.digits 134 if d.weight > 0 { 135 digitsStr = digitsStr + strings.Repeat("0", d.weight) 136 } else if d.weight < 0 { 137 if len(digitsStr) < -d.weight { 138 digitsStr = strings.Repeat("0", -d.weight-len(digitsStr)+1) + digitsStr 139 } 140 indexOfDot := len(digitsStr) + d.weight 141 digitsStr = digitsStr[:indexOfDot] + "." + digitsStr[indexOfDot:] 142 } 143 144 if digitsStr[0] == '0' && digitsStr[1] != '.' { 145 digitsStr = digitsStr[1:] 146 } 147 148 if digitsStr[len(digitsStr)-1] == '0' && strings.IndexRune(digitsStr, '.') >= 0 { 149 digitsStr = digitsStr[0 : len(digitsStr)-1] 150 } 151 152 if d.sign < 0 { 153 digitsStr = "-" + digitsStr 154 } 155 156 return digitsStr 157 } 158 159 func (d DmDecimal) Sign() int { 160 return d.sign 161 } 162 163 func (dest *DmDecimal) Scan(src interface{}) error { 164 if dest == nil { 165 return ECGO_STORE_IN_NIL_POINTER.throw() 166 } 167 switch src := src.(type) { 168 case nil: 169 *dest = *new(DmDecimal) 170 171 (*dest).Valid = false 172 return nil 173 case int, int8, int16, int32, int64: 174 d, err := NewDecimalFromInt64(reflect.ValueOf(src).Int()) 175 if err != nil { 176 return err 177 } 178 *dest = *d 179 return nil 180 case uint, uint8, uint16, uint32, uint64: 181 d, err := NewDecimalFromBigInt(new(big.Int).SetUint64(reflect.ValueOf(src).Uint())) 182 if err != nil { 183 return err 184 } 185 *dest = *d 186 return nil 187 case string: 188 d, err := NewDecimalFromString(src) 189 if err != nil { 190 return err 191 } 192 *dest = *d 193 return nil 194 case *DmDecimal: 195 *dest = *src 196 return nil 197 default: 198 return UNSUPPORTED_SCAN 199 } 200 } 201 202 func (d DmDecimal) Value() (driver.Value, error) { 203 if !d.Valid { 204 return nil, nil 205 } 206 return d, nil 207 } 208 209 func newDecimal(dec interface{}, prec int, scale int) (*DmDecimal, error) { 210 d := &DmDecimal{ 211 prec: prec, 212 scale: scale, 213 Valid: true, 214 } 215 if isFloat(DECIMAL, scale) { 216 d.prec = getFloatPrec(prec) 217 d.scale = -1 218 } 219 switch de := dec.(type) { 220 case *big.Int: 221 d.sign = de.Sign() 222 223 if d.isZero() { 224 return d, nil 225 } 226 str := de.String() 227 228 if d.sign < 0 { 229 str = str[1:] 230 } 231 232 if err := checkPrec(len(str), prec); err != nil { 233 return d, err 234 } 235 i := 0 236 istart := len(str) - 1 237 238 for i = istart; i > 0; i-- { 239 if str[i] != '0' { 240 break 241 } 242 } 243 str = str[:i+1] 244 d.weight += istart - i 245 246 if isOdd(d.weight) { 247 str += "0" 248 d.weight -= 1 249 } 250 if isOdd(len(str)) { 251 str = "0" + str 252 } 253 d.digits = str 254 case *big.Float: 255 d.sign = de.Sign() 256 257 if d.isZero() { 258 return d, nil 259 } 260 str := de.Text('f', -1) 261 262 if d.sign < 0 { 263 str = str[1:] 264 } 265 266 pointIndex := strings.IndexByte(str, '.') 267 i, istart, length := 0, 0, len(str) 268 269 if pointIndex != -1 { 270 if str[0] == '0' { 271 272 istart = 2 273 for i = istart; i < length; i++ { 274 if str[i] != '0' { 275 break 276 } 277 } 278 str = str[i:] 279 d.weight -= i - istart + len(str) 280 } else { 281 str = str[:pointIndex] + str[pointIndex+1:] 282 d.weight -= length - pointIndex - 1 283 } 284 } 285 286 length = len(str) 287 istart = length - 1 288 for i = istart; i > 0; i-- { 289 if str[i] != '0' { 290 break 291 } 292 } 293 str = str[:i+1] + str[length:] 294 d.weight += istart - i 295 296 if isOdd(d.weight) { 297 str += "0" 298 d.weight -= 1 299 } 300 if isOdd(len(str)) { 301 str = "0" + str 302 } 303 d.digits = str 304 case []byte: 305 return decodeDecimal(de, prec, scale) 306 } 307 return d, nil 308 } 309 310 func (d DmDecimal) encodeDecimal() ([]byte, error) { 311 if d.isZero() { 312 return []byte{byte(FLAG_ZERO)}, nil 313 } 314 exp := (d.weight+len(d.digits))/2 - 1 315 if exp > EXP_MAX || exp < EXP_MIN { 316 return nil, ECGO_DATA_TOO_LONG.throw() 317 } 318 validLen := len(d.digits)/2 + 1 319 320 if d.sign < 0 && validLen >= XDEC_SIZE { 321 validLen = XDEC_SIZE - 1 322 } else if validLen > XDEC_SIZE { 323 validLen = XDEC_SIZE 324 } 325 retLen := validLen 326 if d.sign < 0 { 327 retLen = validLen + 1 328 } 329 retBytes := make([]byte, retLen) 330 if d.sign > 0 { 331 retBytes[0] = byte(exp + FLAG_POSITIVE) 332 } else { 333 retBytes[0] = byte(FLAG_NEGTIVE - exp) 334 } 335 336 ibytes := 1 337 for ichar := 0; ibytes < validLen; { 338 digit1, err := strconv.Atoi(string(d.digits[ichar])) 339 if err != nil { 340 return nil, err 341 } 342 ichar++ 343 digit2, err := strconv.Atoi(string(d.digits[ichar])) 344 ichar++ 345 if err != nil { 346 return nil, err 347 } 348 349 digit := digit1*10 + digit2 350 if d.sign > 0 { 351 retBytes[ibytes] = byte(digit + NUM_POSITIVE) 352 } else { 353 retBytes[ibytes] = byte(NUM_NEGTIVE - digit) 354 } 355 ibytes++ 356 } 357 if d.sign < 0 && ibytes < retLen { 358 retBytes[ibytes] = 0x66 359 ibytes++ 360 } 361 if ibytes < retLen { 362 retBytes[ibytes] = 0x00 363 } 364 return retBytes, nil 365 } 366 367 func decodeDecimal(values []byte, prec int, scale int) (*DmDecimal, error) { 368 var decimal = &DmDecimal{ 369 prec: prec, 370 scale: scale, 371 sign: 0, 372 weight: 0, 373 Valid: true, 374 } 375 if values == nil || len(values) == 0 || len(values) > XDEC_SIZE { 376 return nil, ECGO_FATAL_ERROR.throw() 377 } 378 if values[0] == byte(FLAG_ZERO) || len(values) == 1 { 379 return decimal, nil 380 } 381 if values[0]&byte(FLAG_ZERO) != 0 { 382 decimal.sign = 1 383 } else { 384 decimal.sign = -1 385 } 386 387 var flag = int(Dm_build_1.Dm_build_121(values, 0)) 388 var exp int 389 if decimal.sign > 0 { 390 exp = flag - FLAG_POSITIVE 391 } else { 392 exp = FLAG_NEGTIVE - flag 393 } 394 var digit = 0 395 var sf = "" 396 for ival := 1; ival < len(values); ival++ { 397 if decimal.sign > 0 { 398 digit = int(values[ival]) - NUM_POSITIVE 399 } else { 400 digit = NUM_NEGTIVE - int(values[ival]) 401 } 402 if digit < 0 || digit > 99 { 403 break 404 } 405 if digit < 10 { 406 sf += "0" 407 } 408 sf += strconv.Itoa(digit) 409 } 410 decimal.digits = sf 411 decimal.weight = exp*2 - (len(decimal.digits) - 2) 412 413 return decimal, nil 414 } 415 416 func (d DmDecimal) isZero() bool { 417 return d.sign == 0 418 } 419 420 func checkPrec(len int, prec int) error { 421 if prec > 0 && len > prec || len > XDEC_MAX_PREC { 422 return ECGO_DATA_TOO_LONG.throw() 423 } 424 return nil 425 } 426 427 func isOdd(val int) bool { 428 return val%2 != 0 429 } 430 431 func (d *DmDecimal) checkValid() error { 432 if !d.Valid { 433 return ECGO_IS_NULL.throw() 434 } 435 return nil 436 }