github.com/ydb-platform/ydb-go-sdk/v3@v3.89.2/internal/decimal/decimal.go (about) 1 package decimal 2 3 import ( 4 "math/big" 5 "math/bits" 6 7 "github.com/ydb-platform/ydb-go-sdk/v3/internal/xstring" 8 ) 9 10 const ( 11 wordSize = bits.UintSize / 8 12 bufferSize = 40 13 negMask = 0x80 14 ) 15 16 var ( 17 ten = big.NewInt(10) //nolint:gomnd 18 zero = big.NewInt(0) 19 one = big.NewInt(1) 20 inf = big.NewInt(0).Mul( 21 big.NewInt(100000000000000000), //nolint:gomnd 22 big.NewInt(1000000000000000000), //nolint:gomnd 23 ) 24 nan = big.NewInt(0).Add(inf, one) 25 err = big.NewInt(0).Add(nan, one) 26 neginf = big.NewInt(0).Neg(inf) 27 negnan = big.NewInt(0).Neg(nan) 28 ) 29 30 const ( 31 errorTag = "<error>" 32 ) 33 34 // IsInf reports whether x is an infinity. 35 func IsInf(x *big.Int) bool { return x.CmpAbs(inf) == 0 } 36 37 // IsNaN reports whether x is a "not-a-number" value. 38 func IsNaN(x *big.Int) bool { return x.CmpAbs(nan) == 0 } 39 40 // IsErr reports whether x is an "error" value. 41 func IsErr(x *big.Int) bool { return x.Cmp(err) == 0 } 42 43 // Inf returns infinity value. 44 func Inf() *big.Int { return big.NewInt(0).Set(inf) } 45 46 // NaN returns "not-a-number" value. 47 func NaN() *big.Int { return big.NewInt(0).Set(nan) } 48 49 // Err returns "error" value. 50 func Err() *big.Int { return big.NewInt(0).Set(err) } 51 52 // FromBytes converts bytes representation of decimal to big integer. 53 // Most callers should use FromInt128(). 54 // 55 // If given bytes contains value that is greater than given precision it 56 // returns infinity or negative infinity value accordingly the bytes sign. 57 func FromBytes(bts []byte, precision, scale uint32) *big.Int { 58 v := big.NewInt(0) 59 if len(bts) == 0 { 60 return v 61 } 62 63 v.SetBytes(bts) 64 neg := bts[0]&negMask != 0 //nolint:ifshort 65 if neg { 66 // Given bytes contains negative value. 67 // Interpret is as two's complement. 68 not(v) 69 v.Add(v, one) 70 v.Neg(v) 71 } 72 if v.CmpAbs(pow(ten, precision)) >= 0 { 73 if neg { 74 v.Set(neginf) 75 } else { 76 v.Set(inf) 77 } 78 } 79 80 return v 81 } 82 83 // FromInt128 returns big integer from given array. That is, it interprets 84 // 16-byte array as 128-bit integer. 85 func FromInt128(p [16]byte, precision, scale uint32) *big.Int { 86 return FromBytes(p[:], precision, scale) 87 } 88 89 // Parse interprets a string s with the given precision and scale and returns 90 // the corresponding big integer. 91 // 92 //nolint:funlen 93 func Parse(s string, precision, scale uint32) (*big.Int, error) { 94 if scale > precision { 95 return nil, precisionError(s, precision, scale) 96 } 97 98 v := big.NewInt(0) 99 if s == "" { 100 return v, nil 101 } 102 103 neg := s[0] == '-' //nolint:ifshort,nolintlint 104 if neg || s[0] == '+' { 105 s = s[1:] 106 } 107 if isInf(s) { 108 if neg { 109 return v.Set(neginf), nil 110 } 111 112 return v.Set(inf), nil 113 } 114 if isNaN(s) { 115 if neg { 116 return v.Set(negnan), nil 117 } 118 119 return v.Set(nan), nil 120 } 121 122 integral := precision - scale 123 124 var dot bool 125 for ; len(s) > 0; s = s[1:] { 126 c := s[0] 127 if c == '.' { 128 if dot { 129 return nil, syntaxError(s) 130 } 131 dot = true 132 133 continue 134 } 135 if dot { 136 if scale > 0 { 137 scale-- 138 } else { 139 break 140 } 141 } 142 143 if !isDigit(c) { 144 return nil, syntaxError(s) 145 } 146 147 v.Mul(v, ten) 148 v.Add(v, big.NewInt(int64(c-'0'))) 149 150 if !dot && v.Cmp(zero) > 0 && integral == 0 { 151 if neg { 152 return neginf, nil 153 } 154 155 return inf, nil 156 } 157 integral-- 158 } 159 //nolint:nestif 160 if len(s) > 0 { // Characters remaining. 161 c := s[0] 162 if !isDigit(c) { 163 return nil, syntaxError(s) 164 } 165 plus := c > '5' 166 if !plus && c == '5' { 167 var x big.Int 168 plus = x.And(v, one).Cmp(zero) != 0 // Last digit is not a zero. 169 for !plus && len(s) > 1 { 170 s = s[1:] 171 c := s[0] 172 if !isDigit(c) { 173 return nil, syntaxError(s) 174 } 175 plus = c != '0' 176 } 177 } 178 if plus { 179 v.Add(v, one) 180 if v.Cmp(pow(ten, precision)) >= 0 { 181 v.Set(inf) 182 } 183 } 184 } 185 v.Mul(v, pow(ten, scale)) 186 if neg { 187 v.Neg(v) 188 } 189 190 return v, nil 191 } 192 193 // Format returns the string representation of x with the given precision and 194 // scale. 195 // 196 //nolint:funlen 197 func Format(x *big.Int, precision, scale uint32) string { 198 switch { 199 case x.CmpAbs(inf) == 0: 200 if x.Sign() < 0 { 201 return "-inf" 202 } 203 204 return "inf" 205 206 case x.CmpAbs(nan) == 0: 207 if x.Sign() < 0 { 208 return "-nan" 209 } 210 211 return "nan" 212 213 case x == nil: 214 return "0" 215 } 216 217 v := big.NewInt(0).Set(x) 218 neg := x.Sign() < 0 //nolint:ifshort,nolintlint 219 if neg { 220 // Convert negative to positive. 221 v.Neg(x) 222 } 223 224 // log_{10}(2^120) ~= 36.12, 37 decimal places 225 // plus dot, zero before dot, sign. 226 bts := make([]byte, bufferSize) 227 pos := len(bts) 228 229 var digit big.Int 230 for ; v.Cmp(zero) > 0; v.Div(v, ten) { 231 if precision == 0 { 232 return errorTag 233 } 234 precision-- 235 236 digit.Mod(v, ten) 237 d := int(digit.Int64()) 238 if d != 0 || scale == 0 || pos > 0 { 239 const numbers = "0123456789" 240 pos-- 241 bts[pos] = numbers[d] 242 } 243 if scale > 0 { 244 scale-- 245 if scale == 0 && pos > 0 { 246 pos-- 247 bts[pos] = '.' 248 } 249 } 250 } 251 if scale > 0 { 252 for ; scale > 0; scale-- { 253 if precision == 0 { 254 return errorTag 255 } 256 precision-- 257 pos-- 258 bts[pos] = '0' 259 } 260 261 pos-- 262 bts[pos] = '.' 263 } 264 if bts[pos] == '.' { 265 pos-- 266 bts[pos] = '0' 267 } 268 if neg { 269 pos-- 270 bts[pos] = '-' 271 } 272 273 return xstring.FromBytes(bts[pos:]) 274 } 275 276 // BigIntToByte returns the 16-byte array representation of x. 277 // 278 // If x value does not fit in 16 bytes with given precision, it returns 16-byte 279 // representation of infinity or negative infinity value accordingly to x's sign. 280 func BigIntToByte(x *big.Int, precision, scale uint32) (p [16]byte) { 281 if !IsInf(x) && !IsNaN(x) && !IsErr(x) && x.CmpAbs(pow(ten, precision)) >= 0 { 282 if x.Sign() < 0 { 283 x = neginf 284 } else { 285 x = inf 286 } 287 } 288 put(x, p[:]) 289 290 return p 291 } 292 293 func put(x *big.Int, p []byte) { 294 neg := x.Sign() < 0 //nolint:ifshort 295 if neg { 296 x = complement(x) 297 } 298 i := len(p) 299 for _, d := range x.Bits() { 300 for j := 0; j < wordSize; j++ { 301 i-- 302 p[i] = byte(d) 303 d >>= 8 304 } 305 } 306 var pad byte 307 if neg { 308 pad = 0xff 309 } 310 for 0 < i && i < len(p) { 311 i-- 312 p[i] = pad 313 } 314 } 315 316 func Append(p []byte, x *big.Int) []byte { 317 n := len(p) 318 p = ensure(p, size(x)) 319 put(x, p[n:]) 320 321 return p 322 } 323 324 func size(x *big.Int) int { 325 if x.Sign() < 0 { 326 x = complement(x) 327 } 328 329 return len(x.Bits()) * wordSize 330 } 331 332 func ensure(p []byte, n int) []byte { 333 var ( 334 l = len(p) 335 c = cap(p) 336 ) 337 if c-l < n { 338 cp := make([]byte, l+n) 339 copy(cp, p) 340 p = cp 341 } 342 343 return p[:l+n] 344 } 345 346 // not is almost the same as x.Not() but without handling the sign of x. 347 // That is, it more similar to x.Xor(ones) where ones is x bits all set to 1. 348 func not(x *big.Int) { 349 abs := x.Bits() 350 for i, d := range abs { 351 abs[i] = ^d 352 } 353 } 354 355 // pow returns new instance of big.Int equal to x^n. 356 func pow(x *big.Int, n uint32) *big.Int { 357 var ( 358 v = big.NewInt(1) 359 m = big.NewInt(0).Set(x) 360 ) 361 for n > 0 { 362 if n&1 != 0 { 363 v.Mul(v, m) 364 } 365 n >>= 1 366 m.Mul(m, m) 367 } 368 369 return v 370 } 371 372 // complement returns two's complement of x. 373 // x must be negative. 374 func complement(x *big.Int) *big.Int { 375 x = big.NewInt(0).Set(x) 376 not(x) 377 x.Neg(x) 378 x.Add(x, one) 379 380 return x 381 } 382 383 func isInf(s string) bool { 384 return len(s) >= 3 && (s[0] == 'i' || s[0] == 'I') && (s[1] == 'n' || s[1] == 'N') && (s[2] == 'f' || s[2] == 'F') 385 } 386 387 func isNaN(s string) bool { 388 return len(s) >= 3 && (s[0] == 'n' || s[0] == 'N') && (s[1] == 'a' || s[1] == 'A') && (s[2] == 'n' || s[2] == 'N') 389 } 390 391 func isDigit(c byte) bool { 392 return '0' <= c && c <= '9' 393 }