1// Licensed to the Apache Software Foundation (ASF) under one 2// or more contributor license agreements. See the NOTICE file 3// distributed with this work for additional information 4// regarding copyright ownership. The ASF licenses this file 5// to you under the Apache License, Version 2.0 (the 6// "License"); you may not use this file except in compliance 7// with the License. You may obtain a copy of the License at 8// 9// http://www.apache.org/licenses/LICENSE-2.0 10// 11// Unless required by applicable law or agreed to in writing, software 12// distributed under the License is distributed on an "AS IS" BASIS, 13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14// See the License for the specific language governing permissions and 15// limitations under the License. 16 17package decimal128 // import "github.com/apache/arrow/go/v6/arrow/decimal128" 18 19import ( 20 "math/big" 21) 22 23var ( 24 MaxDecimal128 = New(542101086242752217, 687399551400673280-1) 25) 26 27// Num represents a signed 128-bit integer in two's complement. 28// Calculations wrap around and overflow is ignored. 29// 30// For a discussion of the algorithms, look at Knuth's volume 2, 31// Semi-numerical Algorithms section 4.3.1. 32// 33// Adapted from the Apache ORC C++ implementation 34type Num struct { 35 lo uint64 // low bits 36 hi int64 // high bits 37} 38 39// New returns a new signed 128-bit integer value. 40func New(hi int64, lo uint64) Num { 41 return Num{lo: lo, hi: hi} 42} 43 44// FromU64 returns a new signed 128-bit integer value from the provided uint64 one. 45func FromU64(v uint64) Num { 46 return New(0, v) 47} 48 49// FromI64 returns a new signed 128-bit integer value from the provided int64 one. 50func FromI64(v int64) Num { 51 switch { 52 case v > 0: 53 return New(0, uint64(v)) 54 case v < 0: 55 return New(-1, uint64(v)) 56 default: 57 return Num{} 58 } 59} 60 61// FromBigInt will convert a big.Int to a Num, if the value in v has a 62// BitLen > 128, this will panic. 63func FromBigInt(v *big.Int) (n Num) { 64 bitlen := v.BitLen() 65 if bitlen > 128 { 66 panic("arrow/decimal128: cannot represent value larger than 128bits") 67 } else if bitlen == 0 { 68 // if bitlen is 0, then the value is 0 so return the default zeroed 69 // out n 70 return 71 } 72 73 // if the value is negative, then get the high and low bytes from 74 // v, and then negate it. this is because Num uses a two's compliment 75 // representation of values and big.Int stores the value as a bool for 76 // the sign and the absolute value of the integer. This means that the 77 // raw bytes are *always* the absolute value. 78 b := v.Bits() 79 n.lo = uint64(b[0]) 80 if len(b) > 1 { 81 n.hi = int64(b[1]) 82 } 83 if v.Sign() < 0 { 84 return n.negated() 85 } 86 return 87} 88 89func (n Num) negated() Num { 90 n.lo = ^n.lo + 1 91 n.hi = ^n.hi 92 if n.lo == 0 { 93 n.hi += 1 94 } 95 return n 96} 97 98// LowBits returns the low bits of the two's complement representation of the number. 99func (n Num) LowBits() uint64 { return n.lo } 100 101// HighBits returns the high bits of the two's complement representation of the number. 102func (n Num) HighBits() int64 { return n.hi } 103 104// Sign returns: 105// 106// -1 if x < 0 107// 0 if x == 0 108// +1 if x > 0 109func (n Num) Sign() int { 110 if n == (Num{}) { 111 return 0 112 } 113 return int(1 | (n.hi >> 63)) 114} 115 116func toBigIntPositive(n Num) *big.Int { 117 return (&big.Int{}).SetBits([]big.Word{big.Word(n.lo), big.Word(n.hi)}) 118} 119 120// while the code would be simpler to just do lsh/rsh and add 121// it turns out from benchmarking that calling SetBits passing 122// in the words and negating ends up being >2x faster 123func (n Num) BigInt() *big.Int { 124 if n.Sign() < 0 { 125 b := toBigIntPositive(n.negated()) 126 return b.Neg(b) 127 } 128 return toBigIntPositive(n) 129} 130