pkg/math: use higher precision implementations
Most functions where it makes sense have been converted
now.
Math lib is now no longer generated form Go code.
Closes #103
Change-Id: Ib4b89d4793d8257695ca3da763291ee3944e5ce3
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/3980
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/builtin_test.go b/cue/builtin_test.go
index 3752689..f68db8b 100644
--- a/cue/builtin_test.go
+++ b/cue/builtin_test.go
@@ -510,6 +510,55 @@
test("struct", `struct.MaxFields(2) & {a: 1}`),
`{a: 1}`,
}, {
+ test("math", `math.Pow(8, 4)`), `4096`,
+ }, {
+ test("math", `math.Pow10(4)`), `10000`,
+ }, {
+ test("math", `math.Signbit(-4)`), `true`,
+ }, {
+ test("math", `math.Round(2.5)`), `3`,
+ }, {
+ test("math", `math.Round(-2.5)`), `-3`,
+ }, {
+ test("math", `math.RoundToEven(2.5)`), `2`,
+ }, {
+ test("math", `math.RoundToEven(-2.5)`), `-2`,
+ }, {
+ test("math", `math.Abs(2.5)`), `2.5`,
+ }, {
+ test("math", `math.Abs(-2.2)`), `2.2`,
+ }, {
+ test("math", `math.Cbrt(2)`), `1.25992104989487316476721`,
+ }, {
+ test("math", `math.Copysign(5, -2.2)`), `-5`,
+ }, {
+ test("math", `math.Exp(3)`), `20.0855369231876677409285`,
+ }, {
+ test("math", `math.Exp2(3.5)`), `11.3137084989847603904135`,
+ }, {
+ test("math", `math.Log(4)`), `1.38629436111989061883446`,
+ }, {
+ test("math", `math.Log10(4)`), `0.602059991327962390427478`,
+ }, {
+ test("math", `math.Log2(5)`),
+ `2.32192809488736234787032`,
+ }, {
+ test("math", `math.Dim(3, 2.5)`), `0.5`,
+ }, {
+ test("math", `math.Dim(5, 7.2)`), `0`,
+ }, {
+ test("math", `math.Ceil(2.5)`), `3`,
+ }, {
+ test("math", `math.Ceil(-2.2)`), `-2`,
+ }, {
+ test("math", `math.Floor(2.9)`), `2`,
+ }, {
+ test("math", `math.Floor(-2.2)`), `-3`,
+ }, {
+ test("math", `math.Trunc(2.5)`), `2`,
+ }, {
+ test("math", `math.Trunc(-2.9)`), `-2`,
+ }, {
test("math/bits", `bits.Lsh(0x8, 4)`), `128`,
}, {
test("math/bits", `bits.Rsh(0x100, 4)`), `16`,
diff --git a/cue/builtins.go b/cue/builtins.go
index b1c215e..bc72a98 100644
--- a/cue/builtins.go
+++ b/cue/builtins.go
@@ -46,8 +46,20 @@
var _ io.Reader
+var roundTruncContext = apd.Context{Rounding: apd.RoundDown}
+
+var roundUpContext = apd.Context{Rounding: apd.RoundHalfUp}
+
+var roundEvenContext = apd.Context{Rounding: apd.RoundHalfEven}
+
var mulContext = apd.BaseContext.WithPrecision(1)
+var apdContext = apd.BaseContext.WithPrecision(24)
+
+var zero = apd.New(0, 0)
+
+var two = apd.New(2, 0)
+
var idnaProfile = idna.New(
idna.ValidateLabels(true),
idna.VerifyDNSLength(true),
@@ -1026,9 +1038,11 @@
Params: []kind{numKind},
Result: numKind,
Func: func(c *callCtxt) {
- x := c.float64(0)
- c.ret = func() interface{} {
- return math.Floor(x)
+ x := c.decimal(0)
+ c.ret, c.err = func() (interface{}, error) {
+ var d internal.Decimal
+ _, err := apdContext.Floor(&d, x)
+ return &d, err
}()
},
}, {
@@ -1036,9 +1050,11 @@
Params: []kind{numKind},
Result: numKind,
Func: func(c *callCtxt) {
- x := c.float64(0)
- c.ret = func() interface{} {
- return math.Ceil(x)
+ x := c.decimal(0)
+ c.ret, c.err = func() (interface{}, error) {
+ var d internal.Decimal
+ _, err := apdContext.Ceil(&d, x)
+ return &d, err
}()
},
}, {
@@ -1046,9 +1062,11 @@
Params: []kind{numKind},
Result: numKind,
Func: func(c *callCtxt) {
- x := c.float64(0)
- c.ret = func() interface{} {
- return math.Trunc(x)
+ x := c.decimal(0)
+ c.ret, c.err = func() (interface{}, error) {
+ var d internal.Decimal
+ _, err := roundTruncContext.RoundToIntegralExact(&d, x)
+ return &d, err
}()
},
}, {
@@ -1056,9 +1074,11 @@
Params: []kind{numKind},
Result: numKind,
Func: func(c *callCtxt) {
- x := c.float64(0)
- c.ret = func() interface{} {
- return math.Round(x)
+ x := c.decimal(0)
+ c.ret, c.err = func() (interface{}, error) {
+ var d internal.Decimal
+ _, err := roundUpContext.RoundToIntegralExact(&d, x)
+ return &d, err
}()
},
}, {
@@ -1066,9 +1086,11 @@
Params: []kind{numKind},
Result: numKind,
Func: func(c *callCtxt) {
- x := c.float64(0)
- c.ret = func() interface{} {
- return math.RoundToEven(x)
+ x := c.decimal(0)
+ c.ret, c.err = func() (interface{}, error) {
+ var d internal.Decimal
+ _, err := roundEvenContext.RoundToIntegralExact(&d, x)
+ return &d, err
}()
},
}, {
@@ -1088,9 +1110,11 @@
Params: []kind{numKind},
Result: numKind,
Func: func(c *callCtxt) {
- x := c.float64(0)
- c.ret = func() interface{} {
- return math.Abs(x)
+ x := c.decimal(0)
+ c.ret, c.err = func() (interface{}, error) {
+ var d internal.Decimal
+ _, err := apdContext.Abs(&d, x)
+ return &d, err
}()
},
}, {
@@ -1168,9 +1192,11 @@
Params: []kind{numKind},
Result: numKind,
Func: func(c *callCtxt) {
- x := c.float64(0)
- c.ret = func() interface{} {
- return math.Cbrt(x)
+ x := c.decimal(0)
+ c.ret, c.err = func() (interface{}, error) {
+ var d internal.Decimal
+ _, err := apdContext.Cbrt(&d, x)
+ return &d, err
}()
},
}, {
@@ -1211,9 +1237,12 @@
Params: []kind{numKind, numKind},
Result: numKind,
Func: func(c *callCtxt) {
- x, y := c.float64(0), c.float64(1)
+ x, y := c.decimal(0), c.decimal(1)
c.ret = func() interface{} {
- return math.Copysign(x, y)
+ var d internal.Decimal
+ d.Set(x)
+ d.Negative = y.Negative
+ return &d
}()
},
}, {
@@ -1221,9 +1250,17 @@
Params: []kind{numKind, numKind},
Result: numKind,
Func: func(c *callCtxt) {
- x, y := c.float64(0), c.float64(1)
- c.ret = func() interface{} {
- return math.Dim(x, y)
+ x, y := c.decimal(0), c.decimal(1)
+ c.ret, c.err = func() (interface{}, error) {
+ var d internal.Decimal
+ _, err := apdContext.Sub(&d, x, y)
+ if err != nil {
+ return nil, err
+ }
+ if d.Negative {
+ return zero, nil
+ }
+ return &d, nil
}()
},
}, {
@@ -1271,9 +1308,11 @@
Params: []kind{numKind},
Result: numKind,
Func: func(c *callCtxt) {
- x := c.float64(0)
- c.ret = func() interface{} {
- return math.Exp(x)
+ x := c.decimal(0)
+ c.ret, c.err = func() (interface{}, error) {
+ var d internal.Decimal
+ _, err := apdContext.Exp(&d, x)
+ return &d, err
}()
},
}, {
@@ -1281,9 +1320,11 @@
Params: []kind{numKind},
Result: numKind,
Func: func(c *callCtxt) {
- x := c.float64(0)
- c.ret = func() interface{} {
- return math.Exp2(x)
+ x := c.decimal(0)
+ c.ret, c.err = func() (interface{}, error) {
+ var d internal.Decimal
+ _, err := apdContext.Pow(&d, two, x)
+ return &d, err
}()
},
}, {
@@ -1391,9 +1432,11 @@
Params: []kind{numKind},
Result: numKind,
Func: func(c *callCtxt) {
- x := c.float64(0)
- c.ret = func() interface{} {
- return math.Log(x)
+ x := c.decimal(0)
+ c.ret, c.err = func() (interface{}, error) {
+ var d internal.Decimal
+ _, err := apdContext.Ln(&d, x)
+ return &d, err
}()
},
}, {
@@ -1401,9 +1444,11 @@
Params: []kind{numKind},
Result: numKind,
Func: func(c *callCtxt) {
- x := c.float64(0)
- c.ret = func() interface{} {
- return math.Log10(x)
+ x := c.decimal(0)
+ c.ret, c.err = func() (interface{}, error) {
+ var d internal.Decimal
+ _, err := apdContext.Log10(&d, x)
+ return &d, err
}()
},
}, {
@@ -1411,9 +1456,16 @@
Params: []kind{numKind},
Result: numKind,
Func: func(c *callCtxt) {
- x := c.float64(0)
- c.ret = func() interface{} {
- return math.Log2(x)
+ x := c.decimal(0)
+ c.ret, c.err = func() (interface{}, error) {
+ var d, ln2 internal.Decimal
+ _, _ = apdContext.Ln(&ln2, two)
+ _, err := apdContext.Ln(&d, x)
+ if err != nil {
+ return &d, err
+ }
+ _, err = apdContext.Quo(&d, &d, &ln2)
+ return &d, nil
}()
},
}, {
@@ -1461,9 +1513,11 @@
Params: []kind{numKind, numKind},
Result: numKind,
Func: func(c *callCtxt) {
- x, y := c.float64(0), c.float64(1)
- c.ret = func() interface{} {
- return math.Pow(x, y)
+ x, y := c.decimal(0), c.decimal(1)
+ c.ret, c.err = func() (interface{}, error) {
+ var d internal.Decimal
+ _, err := apdContext.Pow(&d, x, y)
+ return &d, err
}()
},
}, {
@@ -1471,9 +1525,9 @@
Params: []kind{intKind},
Result: numKind,
Func: func(c *callCtxt) {
- n := c.int(0)
+ n := c.int32(0)
c.ret = func() interface{} {
- return math.Pow10(n)
+ return apd.New(1, n)
}()
},
}, {
@@ -1491,9 +1545,9 @@
Params: []kind{numKind},
Result: boolKind,
Func: func(c *callCtxt) {
- x := c.float64(0)
+ x := c.decimal(0)
c.ret = func() interface{} {
- return math.Signbit(x)
+ return x.Negative
}()
},
}, {
diff --git a/pkg/math/manual.go b/pkg/math/manual.go
index 361a6c1..9c18f1c 100644
--- a/pkg/math/manual.go
+++ b/pkg/math/manual.go
@@ -15,22 +15,20 @@
package math
import (
- "math"
-
"cuelang.org/go/internal"
"github.com/cockroachdb/apd/v2"
)
-// TODO: use apd
-
// Floor returns the greatest integer value less than or equal to x.
//
// Special cases are:
// Floor(±0) = ±0
// Floor(±Inf) = ±Inf
// Floor(NaN) = NaN
-func Floor(x float64) float64 {
- return math.Floor(x)
+func Floor(x *internal.Decimal) (*internal.Decimal, error) {
+ var d internal.Decimal
+ _, err := apdContext.Floor(&d, x)
+ return &d, err
}
// Ceil returns the least integer value greater than or equal to x.
@@ -39,38 +37,52 @@
// Ceil(±0) = ±0
// Ceil(±Inf) = ±Inf
// Ceil(NaN) = NaN
-func Ceil(x float64) float64 {
- return math.Ceil(x)
+func Ceil(x *internal.Decimal) (*internal.Decimal, error) {
+ var d internal.Decimal
+ _, err := apdContext.Ceil(&d, x)
+ return &d, err
}
+var roundTruncContext = apd.Context{Rounding: apd.RoundDown}
+
// Trunc returns the integer value of x.
//
// Special cases are:
// Trunc(±0) = ±0
// Trunc(±Inf) = ±Inf
// Trunc(NaN) = NaN
-func Trunc(x float64) float64 {
- return math.Trunc(x)
+func Trunc(x *internal.Decimal) (*internal.Decimal, error) {
+ var d internal.Decimal
+ _, err := roundTruncContext.RoundToIntegralExact(&d, x)
+ return &d, err
}
+var roundUpContext = apd.Context{Rounding: apd.RoundHalfUp}
+
// Round returns the nearest integer, rounding half away from zero.
//
// Special cases are:
// Round(±0) = ±0
// Round(±Inf) = ±Inf
// Round(NaN) = NaN
-func Round(x float64) float64 {
- return math.Round(x)
+func Round(x *internal.Decimal) (*internal.Decimal, error) {
+ var d internal.Decimal
+ _, err := roundUpContext.RoundToIntegralExact(&d, x)
+ return &d, err
}
+var roundEvenContext = apd.Context{Rounding: apd.RoundHalfEven}
+
// RoundToEven returns the nearest integer, rounding ties to even.
//
// Special cases are:
// RoundToEven(±0) = ±0
// RoundToEven(±Inf) = ±Inf
// RoundToEven(NaN) = NaN
-func RoundToEven(x float64) float64 {
- return math.RoundToEven(x)
+func RoundToEven(x *internal.Decimal) (*internal.Decimal, error) {
+ var d internal.Decimal
+ _, err := roundEvenContext.RoundToIntegralExact(&d, x)
+ return &d, err
}
var mulContext = apd.BaseContext.WithPrecision(1)
diff --git a/pkg/math/math.go b/pkg/math/math.go
index a66f27a..9d55dc8 100644
--- a/pkg/math/math.go
+++ b/pkg/math/math.go
@@ -16,19 +16,24 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:generate qgo -exclude=32,^Next,^Max,^Smallest,^Min,bits,Inf,NaN,Round,Trunc,Ceil,Floor$ extract math
-
package math
-import "math"
+import (
+ "math"
+
+ "cuelang.org/go/internal"
+ "github.com/cockroachdb/apd/v2"
+)
+
+var apdContext = apd.BaseContext.WithPrecision(24)
// Abs returns the absolute value of x.
//
-// Special cases are:
-// Abs(±Inf) = +Inf
-// Abs(NaN) = NaN
-func Abs(x float64) float64 {
- return math.Abs(x)
+// Special case: Abs(±Inf) = +Inf
+func Abs(x *internal.Decimal) (*internal.Decimal, error) {
+ var d internal.Decimal
+ _, err := apdContext.Abs(&d, x)
+ return &d, err
}
// Acosh returns the inverse hyperbolic cosine of x.
@@ -121,8 +126,10 @@
// Cbrt(±0) = ±0
// Cbrt(±Inf) = ±Inf
// Cbrt(NaN) = NaN
-func Cbrt(x float64) float64 {
- return math.Cbrt(x)
+func Cbrt(x *internal.Decimal) (*internal.Decimal, error) {
+ var d internal.Decimal
+ _, err := apdContext.Cbrt(&d, x)
+ return &d, err
}
// Mathematical constants.
@@ -144,18 +151,31 @@
// Copysign returns a value with the magnitude
// of x and the sign of y.
-func Copysign(x, y float64) float64 {
- return math.Copysign(x, y)
+func Copysign(x, y *internal.Decimal) *internal.Decimal {
+ var d internal.Decimal
+ d.Set(x)
+ d.Negative = y.Negative
+ return &d
}
+var zero = apd.New(0, 0)
+
// Dim returns the maximum of x-y or 0.
//
// Special cases are:
// Dim(+Inf, +Inf) = NaN
// Dim(-Inf, -Inf) = NaN
// Dim(x, NaN) = Dim(NaN, x) = NaN
-func Dim(x, y float64) float64 {
- return math.Dim(x, y)
+func Dim(x, y *internal.Decimal) (*internal.Decimal, error) {
+ var d internal.Decimal
+ _, err := apdContext.Sub(&d, x, y)
+ if err != nil {
+ return nil, err
+ }
+ if d.Negative {
+ return zero, nil
+ }
+ return &d, nil
}
// Erf returns the error function of x.
@@ -207,15 +227,21 @@
// Exp(NaN) = NaN
// Very large values overflow to 0 or +Inf.
// Very small values underflow to 1.
-func Exp(x float64) float64 {
- return math.Exp(x)
+func Exp(x *internal.Decimal) (*internal.Decimal, error) {
+ var d internal.Decimal
+ _, err := apdContext.Exp(&d, x)
+ return &d, err
}
+var two = apd.New(2, 0)
+
// Exp2 returns 2**x, the base-2 exponential of x.
//
// Special cases are the same as Exp.
-func Exp2(x float64) float64 {
- return math.Exp2(x)
+func Exp2(x *internal.Decimal) (*internal.Decimal, error) {
+ var d internal.Decimal
+ _, err := apdContext.Pow(&d, two, x)
+ return &d, err
}
// Expm1 returns e**x - 1, the base-e exponential of x minus 1.
@@ -335,20 +361,31 @@
// Log(0) = -Inf
// Log(x < 0) = NaN
// Log(NaN) = NaN
-func Log(x float64) float64 {
- return math.Log(x)
+func Log(x *internal.Decimal) (*internal.Decimal, error) {
+ var d internal.Decimal
+ _, err := apdContext.Ln(&d, x)
+ return &d, err
}
// Log10 returns the decimal logarithm of x.
// The special cases are the same as for Log.
-func Log10(x float64) float64 {
- return math.Log10(x)
+func Log10(x *internal.Decimal) (*internal.Decimal, error) {
+ var d internal.Decimal
+ _, err := apdContext.Log10(&d, x)
+ return &d, err
}
// Log2 returns the binary logarithm of x.
// The special cases are the same as for Log.
-func Log2(x float64) float64 {
- return math.Log2(x)
+func Log2(x *internal.Decimal) (*internal.Decimal, error) {
+ var d, ln2 internal.Decimal
+ _, _ = apdContext.Ln(&ln2, two)
+ _, err := apdContext.Ln(&d, x)
+ if err != nil {
+ return &d, err
+ }
+ _, err = apdContext.Quo(&d, &d, &ln2)
+ return &d, nil
}
// Log1p returns the natural logarithm of 1 plus its argument x.
@@ -421,17 +458,16 @@
// Pow(+Inf, y) = +0 for y < 0
// Pow(-Inf, y) = Pow(-0, -y)
// Pow(x, y) = NaN for finite x < 0 and finite non-integer y
-func Pow(x, y float64) float64 {
- return math.Pow(x, y)
+func Pow(x, y *internal.Decimal) (*internal.Decimal, error) {
+ var d internal.Decimal
+ _, err := apdContext.Pow(&d, x, y)
+ return &d, err
}
// Pow10 returns 10**n, the base-10 exponential of n.
//
-// Special cases are:
-// Pow10(n) = 0 for n < -323
-// Pow10(n) = +Inf for n > 308
-func Pow10(n int) float64 {
- return math.Pow10(n)
+func Pow10(n int32) *internal.Decimal {
+ return apd.New(1, n)
}
// Remainder returns the IEEE 754 floating-point remainder of x/y.
@@ -447,8 +483,8 @@
}
// Signbit reports whether x is negative or negative zero.
-func Signbit(x float64) bool {
- return math.Signbit(x)
+func Signbit(x *internal.Decimal) bool {
+ return x.Negative
}
// Cos returns the cosine of the radian argument x.