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