// Licensed to the Apache Software Foundation (ASF) under one// or more contributor license agreements. See the NOTICE file// distributed with this work for additional information// regarding copyright ownership. The ASF licenses this file// to you under the Apache License, Version 2.0 (the// "License"); you may not use this file except in compliance// with the License. You may obtain a copy of the License at//// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.package decimal128import ()const (MaxPrecision = 38MaxScale = 38)var (MaxDecimal128 = New(542101086242752217, 687399551400673280-1))func ( int32) Num {returnscaleMultipliers[].Sub(FromU64(1))}// Num represents a signed 128-bit integer in two's complement.// Calculations wrap around and overflow is ignored.//// For a discussion of the algorithms, look at Knuth's volume 2,// Semi-numerical Algorithms section 4.3.1.//// Adapted from the Apache ORC C++ implementationtypeNumstruct { lo uint64// low bits hi int64// high bits}// New returns a new signed 128-bit integer value.func ( int64, uint64) Num {returnNum{lo: , hi: }}// FromU64 returns a new signed 128-bit integer value from the provided uint64 one.func ( uint64) Num {returnNew(0, )}// FromI64 returns a new signed 128-bit integer value from the provided int64 one.func ( int64) Num {switch {case > 0:returnNew(0, uint64())case < 0:returnNew(-1, uint64())default:returnNum{} }}// FromBigInt will convert a big.Int to a Num, if the value in v has a// BitLen > 128, this will panic.func ( *big.Int) ( Num) { := .BitLen()if > 127 {panic("arrow/decimal128: cannot represent value larger than 128bits") } elseif == 0 {// if bitlen is 0, then the value is 0 so return the default zeroed // out nreturn }// if the value is negative, then get the high and low bytes from // v, and then negate it. this is because Num uses a two's compliment // representation of values and big.Int stores the value as a bool for // the sign and the absolute value of the integer. This means that the // raw bytes are *always* the absolute value. := .Bits() .lo = uint64([0])iflen() > 1 { .hi = int64([1]) }if .Sign() < 0 {return .Negate() }return}// Negate returns a copy of this Decimal128 value but with the sign negatedfunc ( Num) () Num { .lo = ^.lo + 1 .hi = ^.hiif .lo == 0 { .hi += 1 }return}func ( Num) ( Num) Num { .hi += .hivaruint64 .lo, = bits.Add64(.lo, .lo, 0) .hi += int64()return}func ( Num) ( Num) Num { .hi -= .hivaruint64 .lo, = bits.Sub64(.lo, .lo, 0) .hi -= int64()return}func ( Num) ( Num) Num { , := bits.Mul64(.lo, .lo) += (uint64(.hi) * .lo) + (.lo * uint64(.hi))returnNum{hi: int64(), lo: }}func ( Num) ( Num) (, Num) { := .BigInt() , := .QuoRem(, .BigInt(), &big.Int{})returnFromBigInt(), FromBigInt()}func ( Num) ( Num) Num { := .BigInt()returnFromBigInt(.Exp(, .BigInt(), nil))}func scalePositiveFloat64( float64, , int32) (float64, error) {varfloat64if >= -38 && <= 38 { = float64PowersOfTen[+38] } else { = math.Pow10(int()) } *= = math.RoundToEven() := float64PowersOfTen[+38]if <= - || >= {return0, fmt.Errorf("cannot convert %f to decimal128(precision=%d, scale=%d): overflow", , , ) }return , nil}func fromPositiveFloat64( float64, , int32) (Num, error) { , := scalePositiveFloat64(, , )if != nil {returnNum{}, } := math.Floor(math.Ldexp(, -64)) := - math.Ldexp(, 64)returnNum{hi: int64(), lo: uint64()}, nil}// this has to exist despite sharing some code with fromPositiveFloat64// because if we don't do the casts back to float32 in between each// step, we end up with a significantly different answer!// Aren't floating point values so much fun?//// example value to use://// v := float32(1.8446746e+15)//// You'll end up with a different values if you do://// FromFloat64(float64(v), 20, 4)//// vs//// FromFloat32(v, 20, 4)//// because float64(v) == 1844674629206016 rather than 1844674600000000func fromPositiveFloat32( float32, , int32) (Num, error) { , := scalePositiveFloat64(float64(), , )if != nil {returnNum{}, } := float32(math.Floor(math.Ldexp(float64(float32()), -64))) := float32() - float32(math.Ldexp(float64(), 64))returnNum{hi: int64(), lo: uint64()}, nil}// FromFloat32 returns a new decimal128.Num constructed from the given float32// value using the provided precision and scale. Will return an error if the// value cannot be accurately represented with the desired precision and scale.func ( float32, , int32) (Num, error) {if < 0 { , := fromPositiveFloat32(-, , )if != nil {return , }return .Negate(), nil }returnfromPositiveFloat32(, , )}// FromFloat64 returns a new decimal128.Num constructed from the given float64// value using the provided precision and scale. Will return an error if the// value cannot be accurately represented with the desired precision and scale.func ( float64, , int32) (Num, error) {if < 0 { , := fromPositiveFloat64(-, , )if != nil {return , }return .Negate(), nil }returnfromPositiveFloat64(, , )}var pt5 = big.NewFloat(0.5)func ( string, , int32) ( Num, error) {// time for some math! // Our input precision means "number of digits of precision" but the // math/big library refers to precision in floating point terms // where it refers to the "number of bits of precision in the mantissa". // So we need to figure out how many bits we should use for precision, // based on the input precision. Too much precision and we aren't rounding // when we should. Too little precision and we round when we shouldn't. // // In general, the number of decimal digits you get from a given number // of bits will be: // // digits = log[base 10](2^nbits) // // it thus follows that: // // digits = nbits * log[base 10](2) // nbits = digits / log[base 10](2) // // So we need to account for our scale since we're going to be multiplying // by 10^scale in order to get the integral value we're actually going to use // So to get our number of bits we do: // // (prec + scale + 1) / log[base10](2) // // Finally, we still have a sign bit, so we -1 to account for the sign bit. // Aren't floating point numbers fun?var = uint(math.Round(float64(++1)/math.Log10(2))) + 1var *big.Float , _, = big.ParseFloat(, 10, 128, big.ToNearestEven)if != nil {return }if < 0 {varbig.Int , := .Int(&)if .BitLen() > 127 {returnNum{}, errors.New("bitlen too large for decimal128") } = FromBigInt() , _ = .Div(scaleMultipliers[-]) } else {// Since we're going to truncate this to get an integer, we need to round // the value instead because of edge cases so that we match how other implementations // (e.g. C++) handles Decimal values. So if we're negative we'll subtract 0.5 and if // we're positive we'll add 0.5. := (&big.Float{}).SetInt(scaleMultipliers[].BigInt()) .SetPrec().Mul(, )if .Signbit() { .Sub(, pt5) } else { .Add(, pt5) }varbig.Int , := .Int(&)if .BitLen() > 127 {returnNum{}, errors.New("bitlen too large for decimal128") } = FromBigInt() }if !.FitsInPrecision() { = fmt.Errorf("val %v doesn't fit in precision %d", , ) }return}// ToFloat32 returns a float32 value representative of this decimal128.Num,// but with the given scale.func ( Num) ( int32) float32 {returnfloat32(.ToFloat64())}func ( Num) ( int32) float64 {constfloat64 = 1.8446744073709552e+19 := float64(.hi) * += float64(.lo)if >= -38 && <= 38 {return * float64PowersOfTen[-+38] }return * math.Pow10(-int())}// ToFloat64 returns a float64 value representative of this decimal128.Num,// but with the given scale.func ( Num) ( int32) float64 {if .hi < 0 {return -.Negate().tofloat64Positive() }return .tofloat64Positive()}func ( Num) ( int32) *big.Float { := (&big.Float{}).SetInt(.BigInt())if < 0 { .SetPrec(128).Mul(, (&big.Float{}).SetInt(scaleMultipliers[-].BigInt())) } else { .SetPrec(128).Quo(, (&big.Float{}).SetInt(scaleMultipliers[].BigInt())) }return}// LowBits returns the low bits of the two's complement representation of the number.func ( Num) () uint64 { return .lo }// HighBits returns the high bits of the two's complement representation of the number.func ( Num) () int64 { return .hi }// Sign returns://// -1 if x < 0//// 0 if x == 0//// +1 if x > 0func ( Num) () int {if == (Num{}) {return0 }returnint(1 | (.hi >> 63))}func toBigIntPositive( Num) *big.Int {return (&big.Int{}).SetBits([]big.Word{big.Word(.lo), big.Word(.hi)})}// while the code would be simpler to just do lsh/rsh and add// it turns out from benchmarking that calling SetBits passing// in the words and negating ends up being >2x fasterfunc ( Num) () *big.Int {if .Sign() < 0 { := toBigIntPositive(.Negate())return .Neg() }returntoBigIntPositive()}// Greater returns true if the value represented by n is > otherfunc ( Num) ( Num) bool {return .Less()}// GreaterEqual returns true if the value represented by n is >= otherfunc ( Num) ( Num) bool {return !.Less()}// Less returns true if the value represented by n is < otherfunc ( Num) ( Num) bool {return .hi < .hi || (.hi == .hi && .lo < .lo)}// LessEqual returns true if the value represented by n is <= otherfunc ( Num) ( Num) bool {return !.Greater()}// Max returns the largest Decimal128 that was passed in the argumentsfunc ( Num, ...Num) Num { := for , := range {if .Greater() { = } }return}// Min returns the smallest Decimal128 that was passed in the argumentsfunc ( Num, ...Num) Num { := for , := range {if .Less() { = } }return}// Cmp compares the numbers represented by n and other and returns://// +1 if n > other// 0 if n == other// -1 if n < otherfunc ( Num) ( Num) int {switch {case .Greater():return1case .Less():return -1 }return0}// IncreaseScaleBy returns a new decimal128.Num with the value scaled up by// the desired amount. Must be 0 <= increase <= 38. Any data loss from scaling// is ignored. If you wish to prevent data loss, use Rescale which will// return an error if data loss is detected.func ( Num) ( int32) Num {debug.Assert( >= 0, "invalid increase scale for decimal128")debug.Assert( <= 38, "invalid increase scale for decimal128") := scaleMultipliers[].BigInt()returnFromBigInt(.Mul(.BigInt(), ))}// ReduceScaleBy returns a new decimal128.Num with the value scaled down by// the desired amount and, if 'round' is true, the value will be rounded// accordingly. Assumes 0 <= reduce <= 38. Any data loss from scaling// is ignored. If you wish to prevent data loss, use Rescale which will// return an error if data loss is detected.func ( Num) ( int32, bool) Num {debug.Assert( >= 0, "invalid reduce scale for decimal128")debug.Assert( <= 38, "invalid reduce scale for decimal128")if == 0 {return } := scaleMultipliers[].BigInt() , := .QuoRem(.BigInt(), , (&big.Int{}))if { := scaleMultipliersHalf[]if .Abs().Cmp(.BigInt()) != -1 { .Add(, big.NewInt(int64(.Sign()))) } }returnFromBigInt()}func ( Num) ( int32, Num) ( Num, bool) {var ( , , *big.Int ) = .BigInt()if < 0 {debug.Assert(.lo != 0 || .hi != 0, "multiplier needs to not be zero") , = (&big.Int{}).QuoRem(, .BigInt(), (&big.Int{}))returnFromBigInt(), .Cmp(big.NewInt(0)) != 0 } = (&big.Int{}).Mul(, .BigInt()) = FromBigInt() := .Cmp()if .Sign() < 0 { = == 1 } else { = == -1 }return}// Rescale returns a new decimal128.Num with the value updated assuming// the current value is scaled to originalScale with the new value scaled// to newScale. If rescaling this way would cause data loss, an error is// returned instead.func ( Num) (, int32) ( Num, error) {if == {return , nil } := - := int32(math.Abs(float64())) := scaleMultipliers[]varbool , = .rescaleWouldCauseDataLoss(, )if { = errors.New("rescale data loss") }return}// Abs returns a new decimal128.Num that contains the absolute value of nfunc ( Num) () Num {switch .Sign() {case -1:return .Negate() }return}// FitsInPrecision returns true or false if the value currently held by// n would fit within precision (0 < prec <= 38) without losing any data.func ( Num) ( int32) bool {debug.Assert( > 0, "precision must be > 0")debug.Assert( <= 38, "precision must be <= 38")return .Abs().Less(scaleMultipliers[])}func ( Num) ( int32) string { := (&big.Float{}).SetInt(.BigInt())if < 0 { .SetPrec(128).Mul(, (&big.Float{}).SetInt(scaleMultipliers[-].BigInt())) } else { .SetPrec(128).Quo(, (&big.Float{}).SetInt(scaleMultipliers[].BigInt())) }return .Text('f', int())}func ( int) Num { returnscaleMultipliers[] }func ( int) Num { returnscaleMultipliersHalf[] }var ( scaleMultipliers = [...]Num{FromU64(1),FromU64(10),FromU64(100),FromU64(1000),FromU64(10000),FromU64(100000),FromU64(1000000),FromU64(10000000),FromU64(100000000),FromU64(1000000000),FromU64(10000000000),FromU64(100000000000),FromU64(1000000000000),FromU64(10000000000000),FromU64(100000000000000),FromU64(1000000000000000),FromU64(10000000000000000),FromU64(100000000000000000),FromU64(1000000000000000000),New(0, 10000000000000000000),New(5, 7766279631452241920),New(54, 3875820019684212736),New(542, 1864712049423024128),New(5421, 200376420520689664),New(54210, 2003764205206896640),New(542101, 1590897978359414784),New(5421010, 15908979783594147840),New(54210108, 11515845246265065472),New(542101086, 4477988020393345024),New(5421010862, 7886392056514347008),New(54210108624, 5076944270305263616),New(542101086242, 13875954555633532928),New(5421010862427, 9632337040368467968),New(54210108624275, 4089650035136921600),New(542101086242752, 4003012203950112768),New(5421010862427522, 3136633892082024448),New(54210108624275221, 12919594847110692864),New(542101086242752217, 68739955140067328),New(5421010862427522170, 687399551400673280), } scaleMultipliersHalf = [...]Num{FromU64(0),FromU64(5),FromU64(50),FromU64(500),FromU64(5000),FromU64(50000),FromU64(500000),FromU64(5000000),FromU64(50000000),FromU64(500000000),FromU64(5000000000),FromU64(50000000000),FromU64(500000000000),FromU64(5000000000000),FromU64(50000000000000),FromU64(500000000000000),FromU64(5000000000000000),FromU64(50000000000000000),FromU64(500000000000000000),FromU64(5000000000000000000),New(2, 13106511852580896768),New(27, 1937910009842106368),New(271, 932356024711512064),New(2710, 9323560247115120640),New(27105, 1001882102603448320),New(271050, 10018821026034483200),New(2710505, 7954489891797073920),New(27105054, 5757922623132532736),New(271050543, 2238994010196672512),New(2710505431, 3943196028257173504),New(27105054312, 2538472135152631808),New(271050543121, 6937977277816766464),New(2710505431213, 14039540557039009792),New(27105054312137, 11268197054423236608),New(271050543121376, 2001506101975056384),New(2710505431213761, 1568316946041012224),New(27105054312137610, 15683169460410122240),New(271050543121376108, 9257742014424809472),New(2710505431213761085, 343699775700336640), } float64PowersOfTen = [...]float64{1e-38, 1e-37, 1e-36, 1e-35, 1e-34, 1e-33, 1e-32, 1e-31, 1e-30, 1e-29,1e-28, 1e-27, 1e-26, 1e-25, 1e-24, 1e-23, 1e-22, 1e-21, 1e-20, 1e-19,1e-18, 1e-17, 1e-16, 1e-15, 1e-14, 1e-13, 1e-12, 1e-11, 1e-10, 1e-9,1e-8, 1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 1e0, 1e1,1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11,1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21,1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29, 1e30, 1e31,1e32, 1e33, 1e34, 1e35, 1e36, 1e37, 1e38, })
The pages are generated with Goldsv0.8.2. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.