mirror of
https://github.com/go-sylixos/elvish.git
synced 2024-12-13 01:40:31 +08:00
Reimplement some math functions to be exactness-preserving.
This addresses #1300.
This commit is contained in:
parent
79fe6947cf
commit
f7a82908e2
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue
Block a user