Reimplement some math functions to be exactness-preserving.

This addresses #1300.
This commit is contained in:
Qi Xiao 2021-05-09 20:03:21 +01:00
parent 79fe6947cf
commit f7a82908e2
3 changed files with 326 additions and 165 deletions

View File

@ -4,8 +4,11 @@ package math
import (
"math"
"math/big"
"src.elv.sh/pkg/eval"
"src.elv.sh/pkg/eval/errs"
"src.elv.sh/pkg/eval/vals"
"src.elv.sh/pkg/eval/vars"
)
@ -16,34 +19,34 @@ var Ns = eval.NsBuilder{
}.AddGoFns("math:", fns).Ns()
var fns = map[string]interface{}{
"abs": math.Abs,
"abs": abs,
"acos": math.Acos,
"acosh": math.Acosh,
"asin": math.Asin,
"asinh": math.Asinh,
"atan": math.Atan,
"atanh": math.Atanh,
"ceil": math.Ceil,
"ceil": math.Ceil, // TODO: Make exactness-preserving
"cos": math.Cos,
"cosh": math.Cosh,
"floor": math.Floor,
"floor": math.Floor, // TODO: Make exactness-preserving
"is-inf": isInf,
"is-nan": math.IsNaN,
"is-nan": isNaN,
"log": math.Log,
"log10": math.Log10,
"log2": math.Log2,
"max": max,
"min": min,
"pow": math.Pow,
"pow10": math.Pow10,
"round": math.Round,
"round-to-even": math.RoundToEven,
"pow": math.Pow, // TODO: Make exactness-preserving for integer exponents
"pow10": math.Pow10, // TODO: Make exactness-preserving for integer exponents
"round": math.Round, // TODO: Make exactness-preserving
"round-to-even": math.RoundToEven, // TODO: Make exactness-preserving
"sin": math.Sin,
"sinh": math.Sinh,
"sqrt": math.Sqrt,
"tan": math.Tan,
"tanh": math.Tanh,
"trunc": math.Trunc,
"trunc": math.Trunc, // TODO: Make exactness-preserving
}
//elvdoc:var e
@ -52,7 +55,7 @@ var fns = map[string]interface{}{
// $math:e
// ```
//
// The value of
// Approximate value of
// [`e`](https://en.wikipedia.org/wiki/E_(mathematical_constant)):
// 2.718281.... This variable is read-only.
@ -62,7 +65,7 @@ var fns = map[string]interface{}{
// $math:pi
// ```
//
// The value of [`π`](https://en.wikipedia.org/wiki/Pi): 3.141592.... This
// Approximate value of [`π`](https://en.wikipedia.org/wiki/Pi): 3.141592.... This
// variable is read-only.
//elvdoc:fn abs
@ -71,15 +74,62 @@ var fns = map[string]interface{}{
// math:abs $number
// ```
//
// Computes the absolute value `$number`. Examples:
// Computes the absolute value `$number`. This function is exactness-preserving.
// Examples:
//
// ```elvish-transcript
// ~> math:abs 1.2
// ▶ (float64 1.2)
// ~> math:abs -5.3
// ▶ (float64 5.3)
// ~> math:abs 2
// ▶ (num 2)
// ~> math:abs -2
// ▶ (num 2)
// ~> math:abs 10000000000000000000
// ▶ (num 10000000000000000000)
// ~> math:abs -10000000000000000000
// ▶ (num 10000000000000000000)
// ~> math:abs 1/2
// ▶ (num 1/2)
// ~> math:abs -1/2
// ▶ (num 1/2)
// ~> math:abs 1.23
// ▶ (num 1.23)
// ~> math:abs -1.23
// ▶ (num 1.23)
// ```
const (
maxInt = int(^uint(0) >> 1)
minInt = -maxInt - 1
)
var absMinInt = new(big.Int).Abs(big.NewInt(int64(minInt)))
func abs(n vals.Num) vals.Num {
switch n := n.(type) {
case int:
if n < 0 {
if n == minInt {
return absMinInt
}
return -n
}
return n
case *big.Int:
if n.Sign() < 0 {
return new(big.Int).Abs(n)
}
return n
case *big.Rat:
if n.Sign() < 0 {
return new(big.Rat).Abs(n)
}
return n
case float64:
return math.Abs(n)
default:
panic("unreachable")
}
}
//elvdoc:fn ceil
//
// ```elvish
@ -264,8 +314,11 @@ type isInfOpts struct{ Sign int }
func (opts *isInfOpts) SetDefaultOptions() { opts.Sign = 0 }
func isInf(opts isInfOpts, arg float64) bool {
return math.IsInf(arg, opts.Sign)
func isInf(opts isInfOpts, n vals.Num) bool {
if f, ok := n.(float64); ok {
return math.IsInf(f, opts.Sign)
}
return false
}
//elvdoc:fn is-nan
@ -285,6 +338,13 @@ func isInf(opts isInfOpts, arg float64) bool {
// ▶ $true
// ```
func isNaN(n vals.Num) bool {
if f, ok := n.(float64); ok {
return math.IsNaN(f)
}
return false
}
//elvdoc:fn log
//
// ```elvish
@ -336,27 +396,61 @@ func isInf(opts isInfOpts, arg float64) bool {
// math:max $number...
// ```
//
// Outputs the maximum number in the arguments. If there are no arguments
// an exception is thrown. If any number is NaN then NaN is output.
// Outputs the maximum number in the arguments. If there are no arguments,
// an exception is thrown. If any number is NaN then NaN is output. This
// function is exactness-preserving.
//
// Examples:
//
// ```elvish-transcript
// ~> put ?(math:max)
// ▶ ?(fail 'arity mismatch: arguments here must be 1 or more values, but is 0 values')
// ~> math:max 3
// ▶ (float 3)
// ~> math:max 3 5 2
// ▶ (float 5)
// ~> range 100 | math:max (all)
// ▶ (float 99)
// ▶ (num 5)
// ~> math:max (range 100)
// ▶ (num 99)
// ~> math:max 1/2 1/3 2/3
// ▶ (num 2/3)
// ```
func max(num float64, nums ...float64) float64 {
for i := 0; i < len(nums); i++ {
num = math.Max(num, nums[i])
func max(rawNums ...vals.Num) (vals.Num, error) {
if len(rawNums) == 0 {
return nil, errs.ArityMismatch{
What: "arguments here", ValidLow: 1, ValidHigh: -1, Actual: 0}
}
nums := vals.UnifyNums(rawNums, 0)
switch nums := nums.(type) {
case []int:
n := nums[0]
for i := 1; i < len(nums); i++ {
if n < nums[i] {
n = nums[i]
}
}
return n, nil
case []*big.Int:
n := nums[0]
for i := 1; i < len(nums); i++ {
if n.Cmp(nums[i]) < 0 {
n = nums[i]
}
}
return n, nil
case []*big.Rat:
n := nums[0]
for i := 1; i < len(nums); i++ {
if n.Cmp(nums[i]) < 0 {
n = nums[i]
}
}
return n, nil
case []float64:
n := nums[0]
for i := 1; i < len(nums); i++ {
n = math.Max(n, nums[i])
}
return n, nil
default:
panic("unreachable")
}
return num
}
//elvdoc:fn min
@ -366,26 +460,61 @@ func max(num float64, nums ...float64) float64 {
// ```
//
// Outputs the minimum number in the arguments. If there are no arguments
// an exception is thrown. If any number is NaN then NaN is output.
// an exception is thrown. If any number is NaN then NaN is output. This
// function is exactness-preserving.
//
// Examples:
//
// ```elvish-transcript
// ~> put ?(math:min)
// ▶ ?(fail 'arity mismatch: arguments here must be 1 or more values, but is 0 values')
// ~> math:min 3
// ▶ (float 3)
// ~> math:min
// Exception: arity mismatch: arguments here must be 1 or more values, but is 0 values
// [tty 17], line 1: math:min
// ~> math:min 3 5 2
// ▶ (float 2)
// ~> range 100 | math:min (all)
// ▶ (float 0)
// ▶ (num 2)
// ~> math:min 1/2 1/3 2/3
// ▶ (num 1/3)
// ```
func min(num float64, nums ...float64) float64 {
for i := 0; i < len(nums); i++ {
num = math.Min(num, nums[i])
func min(rawNums ...vals.Num) (vals.Num, error) {
if len(rawNums) == 0 {
return nil, errs.ArityMismatch{
What: "arguments here", ValidLow: 1, ValidHigh: -1, Actual: 0}
}
nums := vals.UnifyNums(rawNums, 0)
switch nums := nums.(type) {
case []int:
n := nums[0]
for i := 1; i < len(nums); i++ {
if n > nums[i] {
n = nums[i]
}
}
return n, nil
case []*big.Int:
n := nums[0]
for i := 1; i < len(nums); i++ {
if n.Cmp(nums[i]) > 0 {
n = nums[i]
}
}
return n, nil
case []*big.Rat:
n := nums[0]
for i := 1; i < len(nums); i++ {
if n.Cmp(nums[i]) > 0 {
n = nums[i]
}
}
return n, nil
case []float64:
n := nums[0]
for i := 1; i < len(nums); i++ {
n = math.Min(n, nums[i])
}
return n, nil
default:
panic("unreachable")
}
return num
}
//elvdoc:fn pow

View File

@ -2,6 +2,8 @@ package math
import (
"math"
"math/big"
"strconv"
"testing"
"src.elv.sh/pkg/eval"
@ -9,170 +11,200 @@ import (
. "src.elv.sh/pkg/eval/evaltest"
)
const (
zeros = "0000000000000000000"
// Values that exceed the range of int64, used for testing BigInt.
z = "1" + zeros + "0"
z1 = "1" + zeros + "1" // z+1
z2 = "1" + zeros + "2" // z+2
)
var minIntString = strconv.Itoa(minInt)
func TestMath(t *testing.T) {
setup := func(ev *eval.Evaler) {
ev.AddGlobal(eval.NsBuilder{}.AddNs("math", Ns).Ns())
}
TestWithSetup(t, setup,
That(`math:is-inf 1.3`).Puts(false),
That(`math:is-inf &sign=0 inf`).Puts(true),
That(`math:is-inf &sign=1 inf`).Puts(true),
That(`math:is-inf &sign=-1 -inf`).Puts(true),
That(`math:is-inf &sign=1 -inf`).Puts(false),
That(`math:is-inf -inf`).Puts(true),
That(`math:is-inf nan`).Puts(false),
That(`math:is-inf &sign=0 (float64 inf)`).Puts(true),
That(`math:is-inf &sign=1 (float64 inf)`).Puts(true),
That(`math:is-inf &sign=-1 (float64 -inf)`).Puts(true),
That(`math:is-inf &sign=1 (float64 -inf)`).Puts(false),
That(`math:is-inf (float64 -inf)`).Puts(true),
That(`math:is-inf (float64 nan)`).Puts(false),
That(`math:is-inf (float64 1.3)`).Puts(false),
That("math:abs 2").Puts(2),
That("math:abs -2").Puts(2),
That("math:abs "+minIntString).Puts(bigInt(minIntString[1:])),
That("math:abs "+z).Puts(bigInt(z)),
That("math:abs -"+z).Puts(bigInt(z)),
That("math:abs -1/2").Puts(big.NewRat(1, 2)),
That("math:abs 1/2").Puts(big.NewRat(1, 2)),
That("math:abs 2.1").Puts(2.1),
That("math:abs -2.1").Puts(2.1),
That(`math:max`).Throws(
That("math:is-inf 1.3").Puts(false),
That("math:is-inf &sign=0 inf").Puts(true),
That("math:is-inf &sign=1 inf").Puts(true),
That("math:is-inf &sign=-1 -inf").Puts(true),
That("math:is-inf &sign=1 -inf").Puts(false),
That("math:is-inf -inf").Puts(true),
That("math:is-inf nan").Puts(false),
That("math:is-inf 1").Puts(false),
That("math:is-inf "+z).Puts(false),
That("math:is-inf 1/2").Puts(false),
That("math:is-nan 1.3").Puts(false),
That("math:is-nan inf").Puts(false),
That("math:is-nan nan").Puts(true),
That("math:is-nan 1").Puts(false),
That("math:is-nan "+z).Puts(false),
That("math:is-nan 1/2").Puts(false),
That("math:max").Throws(
errs.ArityMismatch{What: "arguments here", ValidLow: 1, ValidHigh: -1, Actual: 0},
"math:max"),
That(`math:max 42`).Puts(float64(42)),
That(`math:max 11 -3 1 7`).Puts(float64(11)),
That(`math:max 3 NaN 5`).Puts(math.NaN()),
That("math:max 42").Puts(42),
That("math:max -3 3 10 -4").Puts(10),
That("math:max 2 10 "+z).Puts(bigInt(z)),
That("math:max "+z1+" "+z2+" "+z).Puts(bigInt(z2)),
That("math:max 1/2 1/3 2/3").Puts(big.NewRat(2, 3)),
That("math:max 1.0 2.0").Puts(2.0),
That("math:max 3 NaN 5").Puts(math.NaN()),
That(`math:min`).Throws(
That("math:min").Throws(
errs.ArityMismatch{What: "arguments here", ValidLow: 1, ValidHigh: -1, Actual: 0},
"math:min"),
That(`math:min 42`).Puts(float64(42)),
That(`math:min 11 -3 1 7`).Puts(float64(-3)),
That(`math:min 3 NaN 5`).Puts(math.NaN()),
That("math:min 42").Puts(42),
That("math:min -3 3 10 -4").Puts(-4),
That("math:min 2 10 "+z).Puts(2),
That("math:min "+z1+" "+z2+" "+z).Puts(bigInt(z)),
That("math:min 1/2 1/3 2/3").Puts(big.NewRat(1, 3)),
That("math:min 1.0 2.0").Puts(1.0),
That("math:min 3 NaN 5").Puts(math.NaN()),
// Tests below this line are tests against simple bindings for Go's math package.
That(`put $math:pi`).Puts(math.Pi),
That(`put $math:e`).Puts(math.E),
That("put $math:pi").Puts(math.Pi),
That("put $math:e").Puts(math.E),
That(`math:abs 2.1`).Puts(2.1),
That(`math:abs -2.1`).Puts(2.1),
That("math:ceil 2.1").Puts(3.0),
That("math:ceil -2.1").Puts(-2.0),
That(`math:ceil 2.1`).Puts(3.0),
That(`math:ceil -2.1`).Puts(-2.0),
That("math:floor 2.1").Puts(2.0),
That("math:floor -2.1").Puts(-3.0),
That(`math:floor 2.1`).Puts(2.0),
That(`math:floor -2.1`).Puts(-3.0),
That("math:round 2.1").Puts(2.0),
That("math:round -2.1").Puts(-2.0),
That("math:round 2.5").Puts(3.0),
That("math:round -2.5").Puts(-3.0),
That("math:round (float64 Inf)").Puts(math.Inf(1)),
That("math:round (float64 NaN)").Puts(math.NaN()),
That(`math:is-nan 1.3`).Puts(false),
That(`math:is-nan inf`).Puts(false),
That(`math:is-nan nan`).Puts(true),
That(`math:is-nan (float64 inf)`).Puts(false),
That(`math:is-nan (float64 nan)`).Puts(true),
That("math:round-to-even 2.1").Puts(2.0),
That("math:round-to-even -2.1").Puts(-2.0),
That("math:round-to-even 2.5").Puts(2.0),
That("math:round-to-even -2.5").Puts(-2.0),
That("math:round-to-even (float64 Inf)").Puts(math.Inf(1)),
That("math:round-to-even (float64 NaN)").Puts(math.NaN()),
That(`math:round 2.1`).Puts(2.0),
That(`math:round -2.1`).Puts(-2.0),
That(`math:round 2.5`).Puts(3.0),
That(`math:round -2.5`).Puts(-3.0),
That(`math:round (float64 Inf)`).Puts(math.Inf(1)),
That(`math:round (float64 NaN)`).Puts(math.NaN()),
That("math:trunc 2.1").Puts(2.0),
That("math:trunc -2.1").Puts(-2.0),
That("math:trunc 2.5").Puts(2.0),
That("math:trunc -2.5").Puts(-2.0),
That("math:trunc (float64 Inf)").Puts(math.Inf(1)),
That("math:trunc (float64 NaN)").Puts(math.NaN()),
That(`math:round-to-even 2.1`).Puts(2.0),
That(`math:round-to-even -2.1`).Puts(-2.0),
That(`math:round-to-even 2.5`).Puts(2.0),
That(`math:round-to-even -2.5`).Puts(-2.0),
That(`math:round-to-even (float64 Inf)`).Puts(math.Inf(1)),
That(`math:round-to-even (float64 NaN)`).Puts(math.NaN()),
That("math:log $math:e").Puts(1.0),
That("math:log 1").Puts(0.0),
That("math:log 0").Puts(math.Inf(-1)),
That("math:log -1").Puts(math.NaN()),
That(`math:trunc 2.1`).Puts(2.0),
That(`math:trunc -2.1`).Puts(-2.0),
That(`math:trunc 2.5`).Puts(2.0),
That(`math:trunc -2.5`).Puts(-2.0),
That(`math:trunc (float64 Inf)`).Puts(math.Inf(1)),
That(`math:trunc (float64 NaN)`).Puts(math.NaN()),
That("math:log10 10.0").Puts(1.0),
That("math:log10 100.0").Puts(2.0),
That("math:log10 1").Puts(0.0),
That("math:log10 0").Puts(math.Inf(-1)),
That("math:log10 -1").Puts(math.NaN()),
That(`math:log $math:e`).Puts(1.0),
That(`math:log 1`).Puts(0.0),
That(`math:log 0`).Puts(math.Inf(-1)),
That(`math:log -1`).Puts(math.NaN()),
That("math:log2 8").Puts(3.0),
That("math:log2 1024.0").Puts(10.0),
That("math:log2 1").Puts(0.0),
That("math:log2 0").Puts(math.Inf(-1)),
That("math:log2 -1").Puts(math.NaN()),
That(`math:log10 10.0`).Puts(1.0),
That(`math:log10 100.0`).Puts(2.0),
That(`math:log10 1`).Puts(0.0),
That(`math:log10 0`).Puts(math.Inf(-1)),
That(`math:log10 -1`).Puts(math.NaN()),
That("math:cos 0").Puts(1.0),
That("math:cos 1").Puts(math.Cos(1.0)),
That("math:cos $math:pi").Puts(-1.0),
That(`math:log2 8`).Puts(3.0),
That(`math:log2 1024.0`).Puts(10.0),
That(`math:log2 1`).Puts(0.0),
That(`math:log2 0`).Puts(math.Inf(-1)),
That(`math:log2 -1`).Puts(math.NaN()),
That("math:cosh 0").Puts(1.0),
That("math:cosh inf").Puts(math.Inf(1)),
That("math:cosh nan").Puts(math.NaN()),
That(`math:cos 0`).Puts(1.0),
That(`math:cos 1`).Puts(math.Cos(1.0)),
That(`math:cos $math:pi`).Puts(-1.0),
That("math:sin 0").Puts(0.0),
That("math:sin 1").Puts(math.Sin(1.0)),
That("math:sin $math:pi").Puts(math.Sin(math.Pi)),
That(`math:cosh 0`).Puts(1.0),
That(`math:cosh inf`).Puts(math.Inf(1)),
That(`math:cosh nan`).Puts(math.NaN()),
That("math:sinh 0").Puts(0.0),
That("math:sinh inf").Puts(math.Inf(1)),
That("math:sinh nan").Puts(math.NaN()),
That(`math:sin 0`).Puts(0.0),
That(`math:sin 1`).Puts(math.Sin(1.0)),
That(`math:sin $math:pi`).Puts(math.Sin(math.Pi)),
That("math:tan 0").Puts(0.0),
That("math:tan 1").Puts(math.Tan(1.0)),
That("math:tan $math:pi").Puts(math.Tan(math.Pi)),
That(`math:sinh 0`).Puts(0.0),
That(`math:sinh inf`).Puts(math.Inf(1)),
That(`math:sinh nan`).Puts(math.NaN()),
That(`math:tan 0`).Puts(0.0),
That(`math:tan 1`).Puts(math.Tan(1.0)),
That(`math:tan $math:pi`).Puts(math.Tan(math.Pi)),
That(`math:tanh 0`).Puts(0.0),
That(`math:tanh inf`).Puts(1.0),
That(`math:tanh nan`).Puts(math.NaN()),
That("math:tanh 0").Puts(0.0),
That("math:tanh inf").Puts(1.0),
That("math:tanh nan").Puts(math.NaN()),
// This block of tests isn't strictly speaking necessary. But it helps
// ensure that we're not just confirming Go statements such as
// math.Tan(math.Pi) == math.Tan(math.Pi)
// are true. The ops that should return a zero value do not actually
// do so. Which illustrates why an approximate match is needed.
That(`math:cos 1`).Puts(Approximately{F: 0.5403023058681397174}),
That(`math:sin 1`).Puts(Approximately{F: 0.8414709848078965066}),
That(`math:sin $math:pi`).Puts(Approximately{F: 0.0}),
That(`math:tan 1`).Puts(Approximately{F: 1.5574077246549023}),
That(`math:tan $math:pi`).Puts(Approximately{F: 0.0}),
That("math:cos 1").Puts(Approximately{F: 0.5403023058681397174}),
That("math:sin 1").Puts(Approximately{F: 0.8414709848078965066}),
That("math:sin $math:pi").Puts(Approximately{F: 0.0}),
That("math:tan 1").Puts(Approximately{F: 1.5574077246549023}),
That("math:tan $math:pi").Puts(Approximately{F: 0.0}),
That(`math:sqrt 0`).Puts(0.0),
That(`math:sqrt 4`).Puts(2.0),
That(`math:sqrt -4`).Puts(math.NaN()),
That("math:sqrt 0").Puts(0.0),
That("math:sqrt 4").Puts(2.0),
That("math:sqrt -4").Puts(math.NaN()),
// Test the inverse trigonometric block of functions.
That(`math:acos 0`).Puts(math.Acos(0)),
That(`math:acos 1`).Puts(math.Acos(1)),
That(`math:acos 1.00001`).Puts(math.NaN()),
That("math:acos 0").Puts(math.Acos(0)),
That("math:acos 1").Puts(math.Acos(1)),
That("math:acos 1.00001").Puts(math.NaN()),
That(`math:asin 0`).Puts(math.Asin(0)),
That(`math:asin 1`).Puts(math.Asin(1)),
That(`math:asin 1.00001`).Puts(math.NaN()),
That("math:asin 0").Puts(math.Asin(0)),
That("math:asin 1").Puts(math.Asin(1)),
That("math:asin 1.00001").Puts(math.NaN()),
That(`math:atan 0`).Puts(math.Atan(0)),
That(`math:atan 1`).Puts(math.Atan(1)),
That(`math:atan inf`).Puts(math.Pi/2),
That("math:atan 0").Puts(math.Atan(0)),
That("math:atan 1").Puts(math.Atan(1)),
That("math:atan inf").Puts(math.Pi/2),
// Test the inverse hyperbolic trigonometric block of functions.
That(`math:acosh 0`).Puts(math.Acosh(0)),
That(`math:acosh 1`).Puts(math.Acosh(1)),
That(`math:acosh nan`).Puts(math.NaN()),
That("math:acosh 0").Puts(math.Acosh(0)),
That("math:acosh 1").Puts(math.Acosh(1)),
That("math:acosh nan").Puts(math.NaN()),
That(`math:asinh 0`).Puts(math.Asinh(0)),
That(`math:asinh 1`).Puts(math.Asinh(1)),
That(`math:asinh inf`).Puts(math.Inf(1)),
That("math:asinh 0").Puts(math.Asinh(0)),
That("math:asinh 1").Puts(math.Asinh(1)),
That("math:asinh inf").Puts(math.Inf(1)),
That(`math:atanh 0`).Puts(math.Atanh(0)),
That(`math:atanh 1`).Puts(math.Inf(1)),
That("math:atanh 0").Puts(math.Atanh(0)),
That("math:atanh 1").Puts(math.Inf(1)),
That(`math:pow nan 2`).Puts(math.NaN()),
That(`math:pow inf 2`).Puts(math.Inf(1)),
That(`math:pow 1 3`).Puts(1.0),
That(`math:pow 2 3`).Puts(8.0),
That(`math:pow -2 2`).Puts(4.0),
That("math:pow nan 2").Puts(math.NaN()),
That("math:pow inf 2").Puts(math.Inf(1)),
That("math:pow 1 3").Puts(1.0),
That("math:pow 2 3").Puts(8.0),
That("math:pow -2 2").Puts(4.0),
That(`math:pow10 0`).Puts(1.0),
That(`math:pow10 3`).Puts(1000.0),
That(`math:pow10 -3`).Puts(0.001),
That("math:pow10 0").Puts(1.0),
That("math:pow10 3").Puts(1000.0),
That("math:pow10 -3").Puts(0.001),
)
}
func bigInt(s string) *big.Int {
z, ok := new(big.Int).SetString(s, 0)
if !ok {
panic("cannot parse as big int: " + s)
}
return z
}

View File

@ -6,7 +6,7 @@ The `math:` module provides mathematical functions and constants.
Function usages are given in the same format as in the reference doc for the
[builtin module](builtin.html). In particular, all the commands in this module
conform to the pattern of
[commands that operate on numbers](builtin.html#commands-that-operate-on-numbers).
conform to the convention of
[numeric commands](builtin.html#numeric-commands).
@elvdoc -ns math: -dir ../pkg/eval/mods/math