cuelang.org/go@v0.10.1/pkg/math/manual.go (about) 1 // Copyright 2018 The CUE Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package math 16 17 import ( 18 "math/big" 19 20 "github.com/cockroachdb/apd/v3" 21 22 "cuelang.org/go/internal" 23 ) 24 25 func roundContext(rounder apd.Rounder) internal.Context { 26 c := internal.BaseContext 27 c.Rounding = rounder 28 return c 29 } 30 31 // TODO: for now we convert Decimals to int. This allows the desired type to be 32 // conveyed. This has the disadvantage that a number like 1E10000 will need to be 33 // expanded. Eventually it would be better to unify number types and allow 34 // anything that results in an integer to pose as an integer type. 35 // TODO: this is likely buggy, as we discard d.Exponent entirely. 36 func toInt(d *internal.Decimal) *big.Int { 37 i := &d.Coeff 38 if d.Negative { 39 i.Neg(i) 40 } 41 return i.MathBigInt() 42 } 43 44 // Floor returns the greatest integer value less than or equal to x. 45 // 46 // Special cases are: 47 // 48 // Floor(±0) = ±0 49 // Floor(±Inf) = ±Inf 50 // Floor(NaN) = NaN 51 func Floor(x *internal.Decimal) (*big.Int, error) { 52 var d internal.Decimal 53 _, err := internal.BaseContext.Floor(&d, x) 54 _, _ = internal.BaseContext.Quantize(&d, &d, 0) 55 return toInt(&d), err 56 } 57 58 // Ceil returns the least integer value greater than or equal to x. 59 // 60 // Special cases are: 61 // 62 // Ceil(±0) = ±0 63 // Ceil(±Inf) = ±Inf 64 // Ceil(NaN) = NaN 65 func Ceil(x *internal.Decimal) (*big.Int, error) { 66 var d internal.Decimal 67 _, err := internal.BaseContext.Ceil(&d, x) 68 _, _ = internal.BaseContext.Quantize(&d, &d, 0) 69 return toInt(&d), err 70 } 71 72 var roundTruncContext = roundContext(apd.RoundDown) 73 74 // Trunc returns the integer value of x. 75 // 76 // Special cases are: 77 // 78 // Trunc(±0) = ±0 79 // Trunc(±Inf) = ±Inf 80 // Trunc(NaN) = NaN 81 func Trunc(x *internal.Decimal) (*big.Int, error) { 82 var d internal.Decimal 83 _, err := roundTruncContext.RoundToIntegralExact(&d, x) 84 return toInt(&d), err 85 } 86 87 var roundUpContext = roundContext(apd.RoundHalfUp) 88 89 // Round returns the nearest integer, rounding half away from zero. 90 // 91 // Special cases are: 92 // 93 // Round(±0) = ±0 94 // Round(±Inf) = ±Inf 95 // Round(NaN) = NaN 96 func Round(x *internal.Decimal) (*big.Int, error) { 97 var d internal.Decimal 98 _, err := roundUpContext.RoundToIntegralExact(&d, x) 99 return toInt(&d), err 100 } 101 102 var roundEvenContext = roundContext(apd.RoundHalfEven) 103 104 // RoundToEven returns the nearest integer, rounding ties to even. 105 // 106 // Special cases are: 107 // 108 // RoundToEven(±0) = ±0 109 // RoundToEven(±Inf) = ±Inf 110 // RoundToEven(NaN) = NaN 111 func RoundToEven(x *internal.Decimal) (*big.Int, error) { 112 var d internal.Decimal 113 _, err := roundEvenContext.RoundToIntegralExact(&d, x) 114 return toInt(&d), err 115 } 116 117 // MultipleOf reports whether x is a multiple of y. 118 func MultipleOf(x, y *internal.Decimal) (bool, error) { 119 var d apd.Decimal 120 121 // TODO: It would be preferable to use internal.BaseContext.Rem here, and directly 122 // check the result for 0. However, this currently fails with "division impossible". 123 // Fix this when https://github.com/cockroachdb/apd/issues/134 is resolved. 124 _, err := internal.BaseContext.Quo(&d, x, y) 125 if err != nil { 126 return false, err 127 } 128 var frac apd.Decimal 129 d.Modf(nil, &frac) 130 return frac.IsZero(), nil 131 }