mirror of
https://github.com/shopspring/decimal.git
synced 2024-11-23 12:50:49 +01:00
cd690d0c9e
* Make NewFromFloat respect the precision of the input Restores the previous behaviour where input converted from float is truncated at the precision of the float. The precision is depending on the actual value. Simply making it 15 digits would make it faster, but would in some cases lose some precision. So the code from the stdlib that does this calculation (very well) has been included. Lots of good articles here: https://www.exploringbinary.com/decimal-precision-of-binary-floating-point-numbers/ Performance is around the same as the previous string roundtrip since it basically does the same, but allocations are a bit less. `BenchmarkNewFromStringFloat` is the old method, `BenchmarkNewFromFloat` is the new. ``` BenchmarkNewFromFloatWithExponent-8 10000000 260 ns/op 174 B/op 4 allocs/op BenchmarkNewFromFloat-8 2000000 744 ns/op 90 B/op 2 allocs/op BenchmarkNewFromStringFloat-8 2000000 822 ns/op 258 B/op 6 allocs/op ``` * Update Sin/Tan/Cos tests.
2598 lines
74 KiB
Go
2598 lines
74 KiB
Go
package decimal
|
|
|
|
import (
|
|
"database/sql/driver"
|
|
"encoding/json"
|
|
"encoding/xml"
|
|
"fmt"
|
|
"math"
|
|
"math/big"
|
|
"math/rand"
|
|
"reflect"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
"testing/quick"
|
|
"time"
|
|
)
|
|
|
|
type testEnt struct {
|
|
float float64
|
|
short string
|
|
exact string
|
|
inexact string
|
|
}
|
|
|
|
var testTable = []*testEnt{
|
|
{3.141592653589793, "3.141592653589793", "", "3.14159265358979300000000000000000000000000000000000004"},
|
|
{3, "3", "", "3.0000000000000000000000002"},
|
|
{1234567890123456, "1234567890123456", "", "1234567890123456.00000000000000002"},
|
|
{1234567890123456000, "1234567890123456000", "", "1234567890123456000.0000000000000008"},
|
|
{1234.567890123456, "1234.567890123456", "", "1234.5678901234560000000000000009"},
|
|
{.1234567890123456, "0.1234567890123456", "", "0.12345678901234560000000000006"},
|
|
{0, "0", "", "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"},
|
|
{.1111111111111110, "0.111111111111111", "", "0.111111111111111000000000000000009"},
|
|
{.1111111111111111, "0.1111111111111111", "", "0.111111111111111100000000000000000000023423545644534234"},
|
|
{.1111111111111119, "0.1111111111111119", "", "0.111111111111111900000000000000000000000000000000000134123984192834"},
|
|
{.000000000000000001, "0.000000000000000001", "", "0.00000000000000000100000000000000000000000000000000012341234"},
|
|
{.000000000000000002, "0.000000000000000002", "", "0.0000000000000000020000000000000000000012341234123"},
|
|
{.000000000000000003, "0.000000000000000003", "", "0.00000000000000000299999999999999999999999900000000000123412341234"},
|
|
{.000000000000000005, "0.000000000000000005", "", "0.00000000000000000500000000000000000023412341234"},
|
|
{.000000000000000008, "0.000000000000000008", "", "0.0000000000000000080000000000000000001241234432"},
|
|
{.1000000000000001, "0.1000000000000001", "", "0.10000000000000010000000000000012341234"},
|
|
{.1000000000000002, "0.1000000000000002", "", "0.10000000000000020000000000001234123412"},
|
|
{.1000000000000003, "0.1000000000000003", "", "0.1000000000000003000000000000001234123412"},
|
|
{.1000000000000005, "0.1000000000000005", "", "0.1000000000000005000000000000000006441234"},
|
|
{.1000000000000008, "0.1000000000000008", "", "0.100000000000000800000000000000000009999999999999999999999999999"},
|
|
{1e25, "10000000000000000000000000", "", ""},
|
|
{1.5e14, "150000000000000", "", ""},
|
|
{1.5e15, "1500000000000000", "", ""},
|
|
{1.5e16, "15000000000000000", "", ""},
|
|
{1.0001e25, "10001000000000000000000000", "", ""},
|
|
{1.0001000000000000033e25, "10001000000000000000000000", "", ""},
|
|
{2e25, "20000000000000000000000000", "", ""},
|
|
{4e25, "40000000000000000000000000", "", ""},
|
|
{8e25, "80000000000000000000000000", "", ""},
|
|
{1e250, "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "", ""},
|
|
{2e250, "20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "", ""},
|
|
{math.MaxInt64, strconv.FormatFloat(float64(math.MaxInt64), 'f', -1, 64), "", strconv.FormatInt(math.MaxInt64, 10)},
|
|
{1.29067116156722e-309, "0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000129067116156722", "", "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001290671161567218558822290567835270536800098852722416870074139002112543896676308448335063375297788379444685193974290737962187240854947838776604607190387984577130572928111657710645015086812756013489109884753559084166516937690932698276436869274093950997935137476803610007959500457935217950764794724766740819156974617155861568214427828145972181876775307023388139991104942469299524961281641158436752347582767153796914843896176260096039358494077706152272661453132497761307744086665088096215425146090058519888494342944692629602847826300550628670375451325582843627504604013541465361435761965354140678551369499812124085312128659002910905639984075064968459581691226705666561364681985266583563078466180095375402399087817404368974165082030458595596655868575908243656158447265625000000000000000000000000000000000000004440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"},
|
|
}
|
|
|
|
var testTableScientificNotation = map[string]string{
|
|
"1e9": "1000000000",
|
|
"2.41E-3": "0.00241",
|
|
"24.2E-4": "0.00242",
|
|
"243E-5": "0.00243",
|
|
"1e-5": "0.00001",
|
|
"245E3": "245000",
|
|
"1.2345E-1": "0.12345",
|
|
"0e5": "0",
|
|
"0e-5": "0",
|
|
"123.456e0": "123.456",
|
|
"123.456e2": "12345.6",
|
|
"123.456e10": "1234560000000",
|
|
}
|
|
|
|
func init() {
|
|
for _, s := range testTable {
|
|
s.exact = strconv.FormatFloat(s.float, 'f', 1500, 64)
|
|
if strings.ContainsRune(s.exact, '.') {
|
|
s.exact = strings.TrimRight(s.exact, "0")
|
|
s.exact = strings.TrimRight(s.exact, ".")
|
|
}
|
|
}
|
|
|
|
// add negatives
|
|
withNeg := testTable[:]
|
|
for _, s := range testTable {
|
|
if s.float > 0 && s.short != "0" && s.exact != "0" {
|
|
withNeg = append(withNeg, &testEnt{-s.float, "-" + s.short, "-" + s.exact, "-" + s.inexact})
|
|
}
|
|
}
|
|
testTable = withNeg
|
|
|
|
for e, s := range testTableScientificNotation {
|
|
if string(e[0]) != "-" && s != "0" {
|
|
testTableScientificNotation["-"+e] = "-" + s
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestNewFromFloat(t *testing.T) {
|
|
for _, x := range testTable {
|
|
s := x.short
|
|
d := NewFromFloat(x.float)
|
|
if d.String() != s {
|
|
t.Errorf("expected %s, got %s (float: %v) (%s, %d)",
|
|
s, d.String(), x.float,
|
|
d.value.String(), d.exp)
|
|
}
|
|
}
|
|
|
|
shouldPanicOn := []float64{
|
|
math.NaN(),
|
|
math.Inf(1),
|
|
math.Inf(-1),
|
|
}
|
|
|
|
for _, n := range shouldPanicOn {
|
|
var d Decimal
|
|
if !didPanic(func() { d = NewFromFloat(n) }) {
|
|
t.Fatalf("Expected panic when creating a Decimal from %v, got %v instead", n, d.String())
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestNewFromFloatRandom(t *testing.T) {
|
|
n := 0
|
|
rng := rand.New(rand.NewSource(0xdead1337))
|
|
for {
|
|
n++
|
|
if n == 10 {
|
|
break
|
|
}
|
|
in := (rng.Float64() - 0.5) * math.MaxFloat64 * 2
|
|
want, err := NewFromString(strconv.FormatFloat(in, 'f', -1, 64))
|
|
if err != nil {
|
|
t.Error(err)
|
|
continue
|
|
}
|
|
got := NewFromFloat(in)
|
|
if !want.Equal(got) {
|
|
t.Errorf("in: %v, expected %s (%s, %d), got %s (%s, %d) ",
|
|
in, want.String(), want.value.String(), want.exp,
|
|
got.String(), got.value.String(), got.exp)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestNewFromFloatQuick(t *testing.T) {
|
|
err := quick.Check(func(f float64) bool {
|
|
want, werr := NewFromString(strconv.FormatFloat(f, 'f', -1, 64))
|
|
if werr != nil {
|
|
return true
|
|
}
|
|
got := NewFromFloat(f)
|
|
return got.Equal(want)
|
|
}, &quick.Config{})
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
}
|
|
|
|
func TestNewFromFloat32Random(t *testing.T) {
|
|
n := 0
|
|
rng := rand.New(rand.NewSource(0xdead1337))
|
|
for {
|
|
n++
|
|
if n == 10 {
|
|
break
|
|
}
|
|
in := float32((rng.Float64() - 0.5) * math.MaxFloat32 * 2)
|
|
want, err := NewFromString(strconv.FormatFloat(float64(in), 'f', -1, 32))
|
|
if err != nil {
|
|
t.Error(err)
|
|
continue
|
|
}
|
|
got := NewFromFloat32(in)
|
|
if !want.Equal(got) {
|
|
t.Errorf("in: %v, expected %s (%s, %d), got %s (%s, %d) ",
|
|
in, want.String(), want.value.String(), want.exp,
|
|
got.String(), got.value.String(), got.exp)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestNewFromFloat32Quick(t *testing.T) {
|
|
err := quick.Check(func(f float32) bool {
|
|
want, werr := NewFromString(strconv.FormatFloat(float64(f), 'f', -1, 32))
|
|
if werr != nil {
|
|
return true
|
|
}
|
|
got := NewFromFloat32(f)
|
|
return got.Equal(want)
|
|
}, &quick.Config{})
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
}
|
|
|
|
func BenchmarkNewFromFloatWithExponent(b *testing.B) {
|
|
rng := rand.New(rand.NewSource(0xdead1337))
|
|
in := make([]float64, b.N)
|
|
for i := range in {
|
|
in[i] = rng.NormFloat64() * 10e20
|
|
}
|
|
b.ReportAllocs()
|
|
b.StartTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
in := rng.NormFloat64() * 10e20
|
|
_ = NewFromFloatWithExponent(in, math.MinInt32)
|
|
}
|
|
}
|
|
|
|
func BenchmarkNewFromFloat(b *testing.B) {
|
|
rng := rand.New(rand.NewSource(0xdead1337))
|
|
in := make([]float64, b.N)
|
|
for i := range in {
|
|
in[i] = rng.NormFloat64() * 10e20
|
|
}
|
|
b.ReportAllocs()
|
|
b.StartTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_ = NewFromFloat(in[i])
|
|
}
|
|
}
|
|
|
|
func BenchmarkNewFromStringFloat(b *testing.B) {
|
|
rng := rand.New(rand.NewSource(0xdead1337))
|
|
in := make([]float64, b.N)
|
|
for i := range in {
|
|
in[i] = rng.NormFloat64() * 10e20
|
|
}
|
|
b.ReportAllocs()
|
|
b.StartTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
in := strconv.FormatFloat(in[i], 'f', -1, 64)
|
|
_, _ = NewFromString(in)
|
|
}
|
|
}
|
|
|
|
func TestNewFromString(t *testing.T) {
|
|
for _, x := range testTable {
|
|
s := x.short
|
|
d, err := NewFromString(s)
|
|
if err != nil {
|
|
t.Errorf("error while parsing %s", s)
|
|
} else if d.String() != s {
|
|
t.Errorf("expected %s, got %s (%s, %d)",
|
|
s, d.String(),
|
|
d.value.String(), d.exp)
|
|
}
|
|
}
|
|
|
|
for _, x := range testTable {
|
|
s := x.exact
|
|
d, err := NewFromString(s)
|
|
if err != nil {
|
|
t.Errorf("error while parsing %s", s)
|
|
} else if d.String() != s {
|
|
t.Errorf("expected %s, got %s (%s, %d)",
|
|
s, d.String(),
|
|
d.value.String(), d.exp)
|
|
}
|
|
}
|
|
|
|
for e, s := range testTableScientificNotation {
|
|
d, err := NewFromString(e)
|
|
if err != nil {
|
|
t.Errorf("error while parsing %s", e)
|
|
} else if d.String() != s {
|
|
t.Errorf("expected %s, got %s (%s, %d)",
|
|
s, d.String(),
|
|
d.value.String(), d.exp)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestFloat64(t *testing.T) {
|
|
for _, x := range testTable {
|
|
if x.inexact == "" || x.inexact == "-" {
|
|
continue
|
|
}
|
|
s := x.exact
|
|
d, err := NewFromString(s)
|
|
if err != nil {
|
|
t.Errorf("error while parsing %s", s)
|
|
} else if f, exact := d.Float64(); !exact || f != x.float {
|
|
t.Errorf("cannot represent exactly %s", s)
|
|
}
|
|
s = x.inexact
|
|
d, err = NewFromString(s)
|
|
if err != nil {
|
|
t.Errorf("error while parsing %s", s)
|
|
} else if f, exact := d.Float64(); exact || f != x.float {
|
|
t.Errorf("%s should be represented inexactly", s)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestNewFromStringErrs(t *testing.T) {
|
|
tests := []string{
|
|
"",
|
|
"qwert",
|
|
"-",
|
|
".",
|
|
"-.",
|
|
".-",
|
|
"234-.56",
|
|
"234-56",
|
|
"2-",
|
|
"..",
|
|
"2..",
|
|
"..2",
|
|
".5.2",
|
|
"8..2",
|
|
"8.1.",
|
|
"1e",
|
|
"1-e",
|
|
"1e9e",
|
|
"1ee9",
|
|
"1ee",
|
|
"1eE",
|
|
"1e-",
|
|
"1e-.",
|
|
"1e1.2",
|
|
"123.456e1.3",
|
|
"1e-1.2",
|
|
"123.456e-1.3",
|
|
"123.456Easdf",
|
|
"123.456e" + strconv.FormatInt(math.MinInt64, 10),
|
|
"123.456e" + strconv.FormatInt(math.MinInt32, 10),
|
|
}
|
|
|
|
for _, s := range tests {
|
|
_, err := NewFromString(s)
|
|
|
|
if err == nil {
|
|
t.Errorf("error expected when parsing %s", s)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestNewFromStringDeepEquals(t *testing.T) {
|
|
type StrCmp struct {
|
|
str1 string
|
|
str2 string
|
|
expected bool
|
|
}
|
|
tests := []StrCmp{
|
|
{"1", "1", true},
|
|
{"10", "10.0", true},
|
|
{"1.1", "1.10", true},
|
|
{"1.001", "1.01", false},
|
|
}
|
|
|
|
for _, cmp := range tests {
|
|
d1, err1 := NewFromString(cmp.str1)
|
|
d2, err2 := NewFromString(cmp.str2)
|
|
|
|
if err1 != nil || err2 != nil {
|
|
t.Errorf("error parsing strings to decimals")
|
|
}
|
|
|
|
if reflect.DeepEqual(d1, d2) != cmp.expected {
|
|
t.Errorf("comparison result is different from expected results for %s and %s",
|
|
cmp.str1, cmp.str2)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRequireFromString(t *testing.T) {
|
|
s := "1.23"
|
|
defer func() {
|
|
err := recover()
|
|
if err != nil {
|
|
t.Errorf("error while parsing %s", s)
|
|
}
|
|
}()
|
|
|
|
d := RequireFromString(s)
|
|
if d.String() != s {
|
|
t.Errorf("expected %s, got %s (%s, %d)",
|
|
s, d.String(),
|
|
d.value.String(), d.exp)
|
|
}
|
|
}
|
|
|
|
func TestRequireFromStringErrs(t *testing.T) {
|
|
s := "qwert"
|
|
var d Decimal
|
|
var err interface{}
|
|
|
|
func(d Decimal) {
|
|
defer func() {
|
|
err = recover()
|
|
}()
|
|
|
|
d = RequireFromString(s)
|
|
}(d)
|
|
|
|
if err == nil {
|
|
t.Errorf("panic expected when parsing %s", s)
|
|
}
|
|
}
|
|
|
|
func TestNewFromFloatWithExponent(t *testing.T) {
|
|
type Inp struct {
|
|
float float64
|
|
exp int32
|
|
}
|
|
// some tests are taken from here https://www.cockroachlabs.com/blog/rounding-implementations-in-go/
|
|
tests := map[Inp]string{
|
|
Inp{123.4, -3}: "123.4",
|
|
Inp{123.4, -1}: "123.4",
|
|
Inp{123.412345, 1}: "120",
|
|
Inp{123.412345, 0}: "123",
|
|
Inp{123.412345, -5}: "123.41235",
|
|
Inp{123.412345, -6}: "123.412345",
|
|
Inp{123.412345, -7}: "123.412345",
|
|
Inp{123.412345, -28}: "123.4123450000000019599610823207",
|
|
Inp{1230000000, 3}: "1230000000",
|
|
Inp{123.9999999999999999, -7}: "124",
|
|
Inp{123.8989898999999999, -7}: "123.8989899",
|
|
Inp{0.49999999999999994, 0}: "0",
|
|
Inp{0.5, 0}: "1",
|
|
Inp{0., -1000}: "0",
|
|
Inp{0.5000000000000001, 0}: "1",
|
|
Inp{1.390671161567e-309, 0}: "0",
|
|
Inp{4.503599627370497e+15, 0}: "4503599627370497",
|
|
Inp{4.503599627370497e+60, 0}: "4503599627370497110902645731364739935039854989106233267453952",
|
|
Inp{4.503599627370497e+60, 1}: "4503599627370497110902645731364739935039854989106233267453950",
|
|
Inp{4.503599627370497e+60, -1}: "4503599627370497110902645731364739935039854989106233267453952",
|
|
Inp{50, 2}: "100",
|
|
Inp{49, 2}: "0",
|
|
Inp{50, 3}: "0",
|
|
// subnormals
|
|
Inp{1.390671161567e-309, -2000}: "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001390671161567000864431395448332752540137009987788957394095829635554502771758698872408926974382819387852542087331897381878220271350970912568035007740861074263206736245957501456549756342151614772544950978154339064833880234531754156635411349342950306987480369774780312897442981323940546749863054846093718407237782253156822124910364044261653195961209878120072488178603782495270845071470243842997312255994555557251870400944414666445871039673491570643357351279578519863428540219295076767898526278029257129758694673164251056158277568765100904638511604478844087596428177947970563689475826736810456067108202083804368114484417399279328807983736233036662284338182105684628835292230438999173947056675615385756827890872955322265625",
|
|
Inp{1.390671161567e-309, -862}: "0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013906711615670008644313954483327525401370099877889573940958296355545027717586988724089269743828193878525420873318973818782202713509709125680350077408610742632067362459575014565497563421516147725449509781543390648338802345317541566354113493429503069874803697747803128974429813239405467498630548460937184072377822531568221249103640442616531959612098781200724881786037824952708450714702438429973122559945555572518704009444146664458710396734915706433573512795785198634285402192950767678985262780292571297586946731642510561582775687651009046385116044788440876",
|
|
Inp{1.390671161567e-309, -863}: "0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013906711615670008644313954483327525401370099877889573940958296355545027717586988724089269743828193878525420873318973818782202713509709125680350077408610742632067362459575014565497563421516147725449509781543390648338802345317541566354113493429503069874803697747803128974429813239405467498630548460937184072377822531568221249103640442616531959612098781200724881786037824952708450714702438429973122559945555572518704009444146664458710396734915706433573512795785198634285402192950767678985262780292571297586946731642510561582775687651009046385116044788440876",
|
|
}
|
|
|
|
// add negatives
|
|
for p, s := range tests {
|
|
if p.float > 0 {
|
|
if s != "0" {
|
|
tests[Inp{-p.float, p.exp}] = "-" + s
|
|
} else {
|
|
tests[Inp{-p.float, p.exp}] = "0"
|
|
}
|
|
}
|
|
}
|
|
|
|
for input, s := range tests {
|
|
d := NewFromFloatWithExponent(input.float, input.exp)
|
|
if d.String() != s {
|
|
t.Errorf("expected %s, got %s (%s, %d)",
|
|
s, d.String(),
|
|
d.value.String(), d.exp)
|
|
}
|
|
}
|
|
|
|
shouldPanicOn := []float64{
|
|
math.NaN(),
|
|
math.Inf(1),
|
|
math.Inf(-1),
|
|
}
|
|
|
|
for _, n := range shouldPanicOn {
|
|
var d Decimal
|
|
if !didPanic(func() { d = NewFromFloatWithExponent(n, 0) }) {
|
|
t.Fatalf("Expected panic when creating a Decimal from %v, got %v instead", n, d.String())
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestNewFromBigIntWithExponent(t *testing.T) {
|
|
type Inp struct {
|
|
val *big.Int
|
|
exp int32
|
|
}
|
|
tests := map[Inp]string{
|
|
Inp{big.NewInt(123412345), -3}: "123412.345",
|
|
Inp{big.NewInt(2234), -1}: "223.4",
|
|
Inp{big.NewInt(323412345), 1}: "3234123450",
|
|
Inp{big.NewInt(423412345), 0}: "423412345",
|
|
Inp{big.NewInt(52341235), -5}: "523.41235",
|
|
Inp{big.NewInt(623412345), -6}: "623.412345",
|
|
Inp{big.NewInt(723412345), -7}: "72.3412345",
|
|
}
|
|
|
|
// add negatives
|
|
for p, s := range tests {
|
|
if p.val.Cmp(Zero.value) > 0 {
|
|
tests[Inp{p.val.Neg(p.val), p.exp}] = "-" + s
|
|
}
|
|
}
|
|
|
|
for input, s := range tests {
|
|
d := NewFromBigInt(input.val, input.exp)
|
|
if d.String() != s {
|
|
t.Errorf("expected %s, got %s (%s, %d)",
|
|
s, d.String(),
|
|
d.value.String(), d.exp)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestJSON(t *testing.T) {
|
|
for _, x := range testTable {
|
|
s := x.short
|
|
var doc struct {
|
|
Amount Decimal `json:"amount"`
|
|
}
|
|
docStr := `{"amount":"` + s + `"}`
|
|
docStrNumber := `{"amount":` + s + `}`
|
|
err := json.Unmarshal([]byte(docStr), &doc)
|
|
if err != nil {
|
|
t.Errorf("error unmarshaling %s: %v", docStr, err)
|
|
} else if doc.Amount.String() != s {
|
|
t.Errorf("expected %s, got %s (%s, %d)",
|
|
s, doc.Amount.String(),
|
|
doc.Amount.value.String(), doc.Amount.exp)
|
|
}
|
|
|
|
out, err := json.Marshal(&doc)
|
|
if err != nil {
|
|
t.Errorf("error marshaling %+v: %v", doc, err)
|
|
} else if string(out) != docStr {
|
|
t.Errorf("expected %s, got %s", docStr, string(out))
|
|
}
|
|
|
|
// make sure unquoted marshalling works too
|
|
MarshalJSONWithoutQuotes = true
|
|
out, err = json.Marshal(&doc)
|
|
if err != nil {
|
|
t.Errorf("error marshaling %+v: %v", doc, err)
|
|
} else if string(out) != docStrNumber {
|
|
t.Errorf("expected %s, got %s", docStrNumber, string(out))
|
|
}
|
|
MarshalJSONWithoutQuotes = false
|
|
}
|
|
}
|
|
|
|
func TestUnmarshalJSONNull(t *testing.T) {
|
|
var doc struct {
|
|
Amount Decimal `json:"amount"`
|
|
}
|
|
docStr := `{"amount": null}`
|
|
err := json.Unmarshal([]byte(docStr), &doc)
|
|
if err != nil {
|
|
t.Errorf("error unmarshaling %s: %v", docStr, err)
|
|
} else if !doc.Amount.Equal(Zero) {
|
|
t.Errorf("expected Zero, got %s (%s, %d)",
|
|
doc.Amount.String(),
|
|
doc.Amount.value.String(), doc.Amount.exp)
|
|
}
|
|
}
|
|
|
|
func TestBadJSON(t *testing.T) {
|
|
for _, testCase := range []string{
|
|
"]o_o[",
|
|
"{",
|
|
`{"amount":""`,
|
|
`{"amount":""}`,
|
|
`{"amount":"nope"}`,
|
|
`0.333`,
|
|
} {
|
|
var doc struct {
|
|
Amount Decimal `json:"amount"`
|
|
}
|
|
err := json.Unmarshal([]byte(testCase), &doc)
|
|
if err == nil {
|
|
t.Errorf("expected error, got %+v", doc)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestNullDecimalJSON(t *testing.T) {
|
|
for _, x := range testTable {
|
|
s := x.short
|
|
var doc struct {
|
|
Amount NullDecimal `json:"amount"`
|
|
}
|
|
docStr := `{"amount":"` + s + `"}`
|
|
docStrNumber := `{"amount":` + s + `}`
|
|
err := json.Unmarshal([]byte(docStr), &doc)
|
|
if err != nil {
|
|
t.Errorf("error unmarshaling %s: %v", docStr, err)
|
|
} else {
|
|
if !doc.Amount.Valid {
|
|
t.Errorf("expected %s to be valid (not NULL), got Valid = false", s)
|
|
}
|
|
if doc.Amount.Decimal.String() != s {
|
|
t.Errorf("expected %s, got %s (%s, %d)",
|
|
s, doc.Amount.Decimal.String(),
|
|
doc.Amount.Decimal.value.String(), doc.Amount.Decimal.exp)
|
|
}
|
|
}
|
|
|
|
out, err := json.Marshal(&doc)
|
|
if err != nil {
|
|
t.Errorf("error marshaling %+v: %v", doc, err)
|
|
} else if string(out) != docStr {
|
|
t.Errorf("expected %s, got %s", docStr, string(out))
|
|
}
|
|
|
|
// make sure unquoted marshalling works too
|
|
MarshalJSONWithoutQuotes = true
|
|
out, err = json.Marshal(&doc)
|
|
if err != nil {
|
|
t.Errorf("error marshaling %+v: %v", doc, err)
|
|
} else if string(out) != docStrNumber {
|
|
t.Errorf("expected %s, got %s", docStrNumber, string(out))
|
|
}
|
|
MarshalJSONWithoutQuotes = false
|
|
}
|
|
|
|
var doc struct {
|
|
Amount NullDecimal `json:"amount"`
|
|
}
|
|
docStr := `{"amount": null}`
|
|
err := json.Unmarshal([]byte(docStr), &doc)
|
|
if err != nil {
|
|
t.Errorf("error unmarshaling %s: %v", docStr, err)
|
|
} else if doc.Amount.Valid {
|
|
t.Errorf("expected null value to have Valid = false, got Valid = true and Decimal = %s (%s, %d)",
|
|
doc.Amount.Decimal.String(),
|
|
doc.Amount.Decimal.value.String(), doc.Amount.Decimal.exp)
|
|
}
|
|
|
|
expected := `{"amount":null}`
|
|
out, err := json.Marshal(&doc)
|
|
if err != nil {
|
|
t.Errorf("error marshaling %+v: %v", doc, err)
|
|
} else if string(out) != expected {
|
|
t.Errorf("expected %s, got %s", expected, string(out))
|
|
}
|
|
|
|
// make sure unquoted marshalling works too
|
|
MarshalJSONWithoutQuotes = true
|
|
expectedUnquoted := `{"amount":null}`
|
|
out, err = json.Marshal(&doc)
|
|
if err != nil {
|
|
t.Errorf("error marshaling %+v: %v", doc, err)
|
|
} else if string(out) != expectedUnquoted {
|
|
t.Errorf("expected %s, got %s", expectedUnquoted, string(out))
|
|
}
|
|
MarshalJSONWithoutQuotes = false
|
|
}
|
|
|
|
func TestNullDecimalBadJSON(t *testing.T) {
|
|
for _, testCase := range []string{
|
|
"]o_o[",
|
|
"{",
|
|
`{"amount":""`,
|
|
`{"amount":""}`,
|
|
`{"amount":"nope"}`,
|
|
`{"amount":nope}`,
|
|
`0.333`,
|
|
} {
|
|
var doc struct {
|
|
Amount NullDecimal `json:"amount"`
|
|
}
|
|
err := json.Unmarshal([]byte(testCase), &doc)
|
|
if err == nil {
|
|
t.Errorf("expected error, got %+v", doc)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestXML(t *testing.T) {
|
|
for _, x := range testTable {
|
|
s := x.short
|
|
var doc struct {
|
|
XMLName xml.Name `xml:"account"`
|
|
Amount Decimal `xml:"amount"`
|
|
}
|
|
docStr := `<account><amount>` + s + `</amount></account>`
|
|
err := xml.Unmarshal([]byte(docStr), &doc)
|
|
if err != nil {
|
|
t.Errorf("error unmarshaling %s: %v", docStr, err)
|
|
} else if doc.Amount.String() != s {
|
|
t.Errorf("expected %s, got %s (%s, %d)",
|
|
s, doc.Amount.String(),
|
|
doc.Amount.value.String(), doc.Amount.exp)
|
|
}
|
|
|
|
out, err := xml.Marshal(&doc)
|
|
if err != nil {
|
|
t.Errorf("error marshaling %+v: %v", doc, err)
|
|
} else if string(out) != docStr {
|
|
t.Errorf("expected %s, got %s", docStr, string(out))
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestBadXML(t *testing.T) {
|
|
for _, testCase := range []string{
|
|
"o_o",
|
|
"<abc",
|
|
"<account><amount>7",
|
|
`<html><body></body></html>`,
|
|
`<account><amount></amount></account>`,
|
|
`<account><amount>nope</amount></account>`,
|
|
`0.333`,
|
|
} {
|
|
var doc struct {
|
|
XMLName xml.Name `xml:"account"`
|
|
Amount Decimal `xml:"amount"`
|
|
}
|
|
err := xml.Unmarshal([]byte(testCase), &doc)
|
|
if err == nil {
|
|
t.Errorf("expected error, got %+v", doc)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDecimal_rescale(t *testing.T) {
|
|
type Inp struct {
|
|
int int64
|
|
exp int32
|
|
rescale int32
|
|
}
|
|
tests := map[Inp]string{
|
|
Inp{1234, -3, -5}: "1.234",
|
|
Inp{1234, -3, 0}: "1",
|
|
Inp{1234, 3, 0}: "1234000",
|
|
Inp{1234, -4, -4}: "0.1234",
|
|
}
|
|
|
|
// add negatives
|
|
for p, s := range tests {
|
|
if p.int > 0 {
|
|
tests[Inp{-p.int, p.exp, p.rescale}] = "-" + s
|
|
}
|
|
}
|
|
|
|
for input, s := range tests {
|
|
d := New(input.int, input.exp).rescale(input.rescale)
|
|
|
|
if d.String() != s {
|
|
t.Errorf("expected %s, got %s (%s, %d)",
|
|
s, d.String(),
|
|
d.value.String(), d.exp)
|
|
}
|
|
|
|
// test StringScaled
|
|
s2 := New(input.int, input.exp).StringScaled(input.rescale)
|
|
if s2 != s {
|
|
t.Errorf("expected %s, got %s", s, s2)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDecimal_Floor(t *testing.T) {
|
|
assertFloor := func(input, expected Decimal) {
|
|
got := input.Floor()
|
|
if !got.Equal(expected) {
|
|
t.Errorf("Floor(%s): got %s, expected %s", input, got, expected)
|
|
}
|
|
}
|
|
type testDataString struct {
|
|
input string
|
|
expected string
|
|
}
|
|
testsWithStrings := []testDataString{
|
|
{"1.999", "1"},
|
|
{"1", "1"},
|
|
{"1.01", "1"},
|
|
{"0", "0"},
|
|
{"0.9", "0"},
|
|
{"0.1", "0"},
|
|
{"-0.9", "-1"},
|
|
{"-0.1", "-1"},
|
|
{"-1.00", "-1"},
|
|
{"-1.01", "-2"},
|
|
{"-1.999", "-2"},
|
|
}
|
|
for _, test := range testsWithStrings {
|
|
expected, _ := NewFromString(test.expected)
|
|
input, _ := NewFromString(test.input)
|
|
assertFloor(input, expected)
|
|
}
|
|
|
|
type testDataDecimal struct {
|
|
input Decimal
|
|
expected string
|
|
}
|
|
testsWithDecimals := []testDataDecimal{
|
|
{New(100, -1), "10"},
|
|
{New(10, 0), "10"},
|
|
{New(1, 1), "10"},
|
|
{New(1999, -3), "1"},
|
|
{New(101, -2), "1"},
|
|
{New(1, 0), "1"},
|
|
{New(0, 0), "0"},
|
|
{New(9, -1), "0"},
|
|
{New(1, -1), "0"},
|
|
{New(-1, -1), "-1"},
|
|
{New(-9, -1), "-1"},
|
|
{New(-1, 0), "-1"},
|
|
{New(-101, -2), "-2"},
|
|
{New(-1999, -3), "-2"},
|
|
}
|
|
for _, test := range testsWithDecimals {
|
|
expected, _ := NewFromString(test.expected)
|
|
assertFloor(test.input, expected)
|
|
}
|
|
}
|
|
|
|
func Benchmark_FloorFast(b *testing.B) {
|
|
input := New(200, 2)
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
input.Floor()
|
|
}
|
|
}
|
|
|
|
func Benchmark_FloorRegular(b *testing.B) {
|
|
input := New(200, -2)
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
input.Floor()
|
|
}
|
|
}
|
|
|
|
func TestDecimal_Ceil(t *testing.T) {
|
|
assertCeil := func(input, expected Decimal) {
|
|
got := input.Ceil()
|
|
if !got.Equal(expected) {
|
|
t.Errorf("Ceil(%s): got %s, expected %s", input, got, expected)
|
|
}
|
|
}
|
|
type testDataString struct {
|
|
input string
|
|
expected string
|
|
}
|
|
testsWithStrings := []testDataString{
|
|
{"1.999", "2"},
|
|
{"1", "1"},
|
|
{"1.01", "2"},
|
|
{"0", "0"},
|
|
{"0.9", "1"},
|
|
{"0.1", "1"},
|
|
{"-0.9", "0"},
|
|
{"-0.1", "0"},
|
|
{"-1.00", "-1"},
|
|
{"-1.01", "-1"},
|
|
{"-1.999", "-1"},
|
|
}
|
|
for _, test := range testsWithStrings {
|
|
expected, _ := NewFromString(test.expected)
|
|
input, _ := NewFromString(test.input)
|
|
assertCeil(input, expected)
|
|
}
|
|
|
|
type testDataDecimal struct {
|
|
input Decimal
|
|
expected string
|
|
}
|
|
testsWithDecimals := []testDataDecimal{
|
|
{New(100, -1), "10"},
|
|
{New(10, 0), "10"},
|
|
{New(1, 1), "10"},
|
|
{New(1999, -3), "2"},
|
|
{New(101, -2), "2"},
|
|
{New(1, 0), "1"},
|
|
{New(0, 0), "0"},
|
|
{New(9, -1), "1"},
|
|
{New(1, -1), "1"},
|
|
{New(-1, -1), "0"},
|
|
{New(-9, -1), "0"},
|
|
{New(-1, 0), "-1"},
|
|
{New(-101, -2), "-1"},
|
|
{New(-1999, -3), "-1"},
|
|
}
|
|
for _, test := range testsWithDecimals {
|
|
expected, _ := NewFromString(test.expected)
|
|
assertCeil(test.input, expected)
|
|
}
|
|
}
|
|
|
|
func TestDecimal_RoundAndStringFixed(t *testing.T) {
|
|
type testData struct {
|
|
input string
|
|
places int32
|
|
expected string
|
|
expectedFixed string
|
|
}
|
|
tests := []testData{
|
|
{"1.454", 0, "1", ""},
|
|
{"1.454", 1, "1.5", ""},
|
|
{"1.454", 2, "1.45", ""},
|
|
{"1.454", 3, "1.454", ""},
|
|
{"1.454", 4, "1.454", "1.4540"},
|
|
{"1.454", 5, "1.454", "1.45400"},
|
|
{"1.554", 0, "2", ""},
|
|
{"1.554", 1, "1.6", ""},
|
|
{"1.554", 2, "1.55", ""},
|
|
{"0.554", 0, "1", ""},
|
|
{"0.454", 0, "0", ""},
|
|
{"0.454", 5, "0.454", "0.45400"},
|
|
{"0", 0, "0", ""},
|
|
{"0", 1, "0", "0.0"},
|
|
{"0", 2, "0", "0.00"},
|
|
{"0", -1, "0", ""},
|
|
{"5", 2, "5", "5.00"},
|
|
{"5", 1, "5", "5.0"},
|
|
{"5", 0, "5", ""},
|
|
{"500", 2, "500", "500.00"},
|
|
{"545", -1, "550", ""},
|
|
{"545", -2, "500", ""},
|
|
{"545", -3, "1000", ""},
|
|
{"545", -4, "0", ""},
|
|
{"499", -3, "0", ""},
|
|
{"499", -4, "0", ""},
|
|
}
|
|
|
|
// add negative number tests
|
|
for _, test := range tests {
|
|
expected := test.expected
|
|
if expected != "0" {
|
|
expected = "-" + expected
|
|
}
|
|
expectedStr := test.expectedFixed
|
|
if strings.ContainsAny(expectedStr, "123456789") && expectedStr != "" {
|
|
expectedStr = "-" + expectedStr
|
|
}
|
|
tests = append(tests,
|
|
testData{"-" + test.input, test.places, expected, expectedStr})
|
|
}
|
|
|
|
for _, test := range tests {
|
|
d, err := NewFromString(test.input)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
// test Round
|
|
expected, err := NewFromString(test.expected)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
got := d.Round(test.places)
|
|
if !got.Equal(expected) {
|
|
t.Errorf("Rounding %s to %d places, got %s, expected %s",
|
|
d, test.places, got, expected)
|
|
}
|
|
|
|
// test StringFixed
|
|
if test.expectedFixed == "" {
|
|
test.expectedFixed = test.expected
|
|
}
|
|
gotStr := d.StringFixed(test.places)
|
|
if gotStr != test.expectedFixed {
|
|
t.Errorf("(%s).StringFixed(%d): got %s, expected %s",
|
|
d, test.places, gotStr, test.expectedFixed)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDecimal_BankRoundAndStringFixed(t *testing.T) {
|
|
type testData struct {
|
|
input string
|
|
places int32
|
|
expected string
|
|
expectedFixed string
|
|
}
|
|
tests := []testData{
|
|
{"1.454", 0, "1", ""},
|
|
{"1.454", 1, "1.5", ""},
|
|
{"1.454", 2, "1.45", ""},
|
|
{"1.454", 3, "1.454", ""},
|
|
{"1.454", 4, "1.454", "1.4540"},
|
|
{"1.454", 5, "1.454", "1.45400"},
|
|
{"1.554", 0, "2", ""},
|
|
{"1.554", 1, "1.6", ""},
|
|
{"1.554", 2, "1.55", ""},
|
|
{"0.554", 0, "1", ""},
|
|
{"0.454", 0, "0", ""},
|
|
{"0.454", 5, "0.454", "0.45400"},
|
|
{"0", 0, "0", ""},
|
|
{"0", 1, "0", "0.0"},
|
|
{"0", 2, "0", "0.00"},
|
|
{"0", -1, "0", ""},
|
|
{"5", 2, "5", "5.00"},
|
|
{"5", 1, "5", "5.0"},
|
|
{"5", 0, "5", ""},
|
|
{"500", 2, "500", "500.00"},
|
|
{"545", -2, "500", ""},
|
|
{"545", -3, "1000", ""},
|
|
{"545", -4, "0", ""},
|
|
{"499", -3, "0", ""},
|
|
{"499", -4, "0", ""},
|
|
{"1.45", 1, "1.4", ""},
|
|
{"1.55", 1, "1.6", ""},
|
|
{"1.65", 1, "1.6", ""},
|
|
{"545", -1, "540", ""},
|
|
{"565", -1, "560", ""},
|
|
{"555", -1, "560", ""},
|
|
}
|
|
|
|
// add negative number tests
|
|
for _, test := range tests {
|
|
expected := test.expected
|
|
if expected != "0" {
|
|
expected = "-" + expected
|
|
}
|
|
expectedStr := test.expectedFixed
|
|
if strings.ContainsAny(expectedStr, "123456789") && expectedStr != "" {
|
|
expectedStr = "-" + expectedStr
|
|
}
|
|
tests = append(tests,
|
|
testData{"-" + test.input, test.places, expected, expectedStr})
|
|
}
|
|
|
|
for _, test := range tests {
|
|
d, err := NewFromString(test.input)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
// test Round
|
|
expected, err := NewFromString(test.expected)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
got := d.RoundBank(test.places)
|
|
if !got.Equal(expected) {
|
|
t.Errorf("Bank Rounding %s to %d places, got %s, expected %s",
|
|
d, test.places, got, expected)
|
|
}
|
|
|
|
// test StringFixed
|
|
if test.expectedFixed == "" {
|
|
test.expectedFixed = test.expected
|
|
}
|
|
gotStr := d.StringFixedBank(test.places)
|
|
if gotStr != test.expectedFixed {
|
|
t.Errorf("(%s).StringFixed(%d): got %s, expected %s",
|
|
d, test.places, gotStr, test.expectedFixed)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDecimal_Uninitialized(t *testing.T) {
|
|
a := Decimal{}
|
|
b := Decimal{}
|
|
|
|
decs := []Decimal{
|
|
a,
|
|
a.rescale(10),
|
|
a.Abs(),
|
|
a.Add(b),
|
|
a.Sub(b),
|
|
a.Mul(b),
|
|
a.Shift(0),
|
|
a.Div(New(1, -1)),
|
|
a.Round(2),
|
|
a.Floor(),
|
|
a.Ceil(),
|
|
a.Truncate(2),
|
|
}
|
|
|
|
for _, d := range decs {
|
|
if d.String() != "0" {
|
|
t.Errorf("expected 0, got %s", d.String())
|
|
}
|
|
if d.StringFixed(3) != "0.000" {
|
|
t.Errorf("expected 0, got %s", d.StringFixed(3))
|
|
}
|
|
if d.StringScaled(-2) != "0" {
|
|
t.Errorf("expected 0, got %s", d.StringScaled(-2))
|
|
}
|
|
}
|
|
|
|
if a.Cmp(b) != 0 {
|
|
t.Errorf("a != b")
|
|
}
|
|
if a.Sign() != 0 {
|
|
t.Errorf("a.Sign() != 0")
|
|
}
|
|
if a.Exponent() != 0 {
|
|
t.Errorf("a.Exponent() != 0")
|
|
}
|
|
if a.IntPart() != 0 {
|
|
t.Errorf("a.IntPar() != 0")
|
|
}
|
|
f, _ := a.Float64()
|
|
if f != 0 {
|
|
t.Errorf("a.Float64() != 0")
|
|
}
|
|
if a.Rat().RatString() != "0" {
|
|
t.Errorf("a.Rat() != 0, got %s", a.Rat().RatString())
|
|
}
|
|
}
|
|
|
|
func TestDecimal_Add(t *testing.T) {
|
|
type Inp struct {
|
|
a string
|
|
b string
|
|
}
|
|
|
|
inputs := map[Inp]string{
|
|
Inp{"2", "3"}: "5",
|
|
Inp{"2454495034", "3451204593"}: "5905699627",
|
|
Inp{"24544.95034", ".3451204593"}: "24545.2954604593",
|
|
Inp{".1", ".1"}: "0.2",
|
|
Inp{".1", "-.1"}: "0",
|
|
Inp{"0", "1.001"}: "1.001",
|
|
}
|
|
|
|
for inp, res := range inputs {
|
|
a, err := NewFromString(inp.a)
|
|
if err != nil {
|
|
t.FailNow()
|
|
}
|
|
b, err := NewFromString(inp.b)
|
|
if err != nil {
|
|
t.FailNow()
|
|
}
|
|
c := a.Add(b)
|
|
if c.String() != res {
|
|
t.Errorf("expected %s, got %s", res, c.String())
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDecimal_Sub(t *testing.T) {
|
|
type Inp struct {
|
|
a string
|
|
b string
|
|
}
|
|
|
|
inputs := map[Inp]string{
|
|
Inp{"2", "3"}: "-1",
|
|
Inp{"12", "3"}: "9",
|
|
Inp{"-2", "9"}: "-11",
|
|
Inp{"2454495034", "3451204593"}: "-996709559",
|
|
Inp{"24544.95034", ".3451204593"}: "24544.6052195407",
|
|
Inp{".1", "-.1"}: "0.2",
|
|
Inp{".1", ".1"}: "0",
|
|
Inp{"0", "1.001"}: "-1.001",
|
|
Inp{"1.001", "0"}: "1.001",
|
|
Inp{"2.3", ".3"}: "2",
|
|
}
|
|
|
|
for inp, res := range inputs {
|
|
a, err := NewFromString(inp.a)
|
|
if err != nil {
|
|
t.FailNow()
|
|
}
|
|
b, err := NewFromString(inp.b)
|
|
if err != nil {
|
|
t.FailNow()
|
|
}
|
|
c := a.Sub(b)
|
|
if c.String() != res {
|
|
t.Errorf("expected %s, got %s", res, c.String())
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDecimal_Neg(t *testing.T) {
|
|
inputs := map[string]string{
|
|
"0": "0",
|
|
"10": "-10",
|
|
"5.56": "-5.56",
|
|
"-10": "10",
|
|
"-5.56": "5.56",
|
|
}
|
|
|
|
for inp, res := range inputs {
|
|
a, err := NewFromString(inp)
|
|
if err != nil {
|
|
t.FailNow()
|
|
}
|
|
b := a.Neg()
|
|
if b.String() != res {
|
|
t.Errorf("expected %s, got %s", res, b.String())
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDecimal_NegFromEmpty(t *testing.T) {
|
|
a := Decimal{}
|
|
b := a.Neg()
|
|
if b.String() != "0" {
|
|
t.Errorf("expected %s, got %s", "0", b)
|
|
}
|
|
}
|
|
|
|
func TestDecimal_Mul(t *testing.T) {
|
|
type Inp struct {
|
|
a string
|
|
b string
|
|
}
|
|
|
|
inputs := map[Inp]string{
|
|
Inp{"2", "3"}: "6",
|
|
Inp{"2454495034", "3451204593"}: "8470964534836491162",
|
|
Inp{"24544.95034", ".3451204593"}: "8470.964534836491162",
|
|
Inp{".1", ".1"}: "0.01",
|
|
Inp{"0", "1.001"}: "0",
|
|
}
|
|
|
|
for inp, res := range inputs {
|
|
a, err := NewFromString(inp.a)
|
|
if err != nil {
|
|
t.FailNow()
|
|
}
|
|
b, err := NewFromString(inp.b)
|
|
if err != nil {
|
|
t.FailNow()
|
|
}
|
|
c := a.Mul(b)
|
|
if c.String() != res {
|
|
t.Errorf("expected %s, got %s", res, c.String())
|
|
}
|
|
}
|
|
|
|
// positive scale
|
|
c := New(1234, 5).Mul(New(45, -1))
|
|
if c.String() != "555300000" {
|
|
t.Errorf("Expected %s, got %s", "555300000", c.String())
|
|
}
|
|
}
|
|
|
|
func TestDecimal_Shift(t *testing.T) {
|
|
type Inp struct {
|
|
a string
|
|
b int32
|
|
}
|
|
|
|
inputs := map[Inp]string{
|
|
Inp{"6", 3}: "6000",
|
|
Inp{"10", -2}: "0.1",
|
|
Inp{"2.2", 1}: "22",
|
|
Inp{"-2.2", -1}: "-0.22",
|
|
Inp{"12.88", 5}: "1288000",
|
|
Inp{"-10234274355545544493", -3}: "-10234274355545544.493",
|
|
Inp{"-4612301402398.4753343454", 5}: "-461230140239847533.43454",
|
|
}
|
|
|
|
for inp, expectedStr := range inputs {
|
|
num, _ := NewFromString(inp.a)
|
|
|
|
got := num.Shift(inp.b)
|
|
expected, _ := NewFromString(expectedStr)
|
|
if !got.Equal(expected) {
|
|
t.Errorf("expected %v when shifting %v by %v, got %v",
|
|
expected, num, inp.b, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDecimal_Div(t *testing.T) {
|
|
type Inp struct {
|
|
a string
|
|
b string
|
|
}
|
|
|
|
inputs := map[Inp]string{
|
|
Inp{"6", "3"}: "2",
|
|
Inp{"10", "2"}: "5",
|
|
Inp{"2.2", "1.1"}: "2",
|
|
Inp{"-2.2", "-1.1"}: "2",
|
|
Inp{"12.88", "5.6"}: "2.3",
|
|
Inp{"1023427554493", "43432632"}: "23563.5628642767953828", // rounded
|
|
Inp{"1", "434324545566634"}: "0.0000000000000023",
|
|
Inp{"1", "3"}: "0.3333333333333333",
|
|
Inp{"2", "3"}: "0.6666666666666667", // rounded
|
|
Inp{"10000", "3"}: "3333.3333333333333333",
|
|
Inp{"10234274355545544493", "-3"}: "-3411424785181848164.3333333333333333",
|
|
Inp{"-4612301402398.4753343454", "23.5"}: "-196268144782.9138440146978723",
|
|
}
|
|
|
|
for inp, expectedStr := range inputs {
|
|
num, err := NewFromString(inp.a)
|
|
if err != nil {
|
|
t.FailNow()
|
|
}
|
|
denom, err := NewFromString(inp.b)
|
|
if err != nil {
|
|
t.FailNow()
|
|
}
|
|
got := num.Div(denom)
|
|
expected, _ := NewFromString(expectedStr)
|
|
if !got.Equal(expected) {
|
|
t.Errorf("expected %v when dividing %v by %v, got %v",
|
|
expected, num, denom, got)
|
|
}
|
|
got2 := num.DivRound(denom, int32(DivisionPrecision))
|
|
if !got2.Equal(expected) {
|
|
t.Errorf("expected %v on DivRound (%v,%v), got %v", expected, num, denom, got2)
|
|
}
|
|
}
|
|
|
|
type Inp2 struct {
|
|
n int64
|
|
exp int32
|
|
n2 int64
|
|
exp2 int32
|
|
}
|
|
|
|
// test code path where exp > 0
|
|
inputs2 := map[Inp2]string{
|
|
Inp2{124, 10, 3, 1}: "41333333333.3333333333333333",
|
|
Inp2{124, 10, 3, 0}: "413333333333.3333333333333333",
|
|
Inp2{124, 10, 6, 1}: "20666666666.6666666666666667",
|
|
Inp2{124, 10, 6, 0}: "206666666666.6666666666666667",
|
|
Inp2{10, 10, 10, 1}: "1000000000",
|
|
}
|
|
|
|
for inp, expectedAbs := range inputs2 {
|
|
for i := -1; i <= 1; i += 2 {
|
|
for j := -1; j <= 1; j += 2 {
|
|
n := inp.n * int64(i)
|
|
n2 := inp.n2 * int64(j)
|
|
num := New(n, inp.exp)
|
|
denom := New(n2, inp.exp2)
|
|
expected := expectedAbs
|
|
if i != j {
|
|
expected = "-" + expectedAbs
|
|
}
|
|
got := num.Div(denom)
|
|
if got.String() != expected {
|
|
t.Errorf("expected %s when dividing %v by %v, got %v",
|
|
expected, num, denom, got)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDecimal_QuoRem(t *testing.T) {
|
|
type Inp4 struct {
|
|
d string
|
|
d2 string
|
|
exp int32
|
|
q string
|
|
r string
|
|
}
|
|
cases := []Inp4{
|
|
{"10", "1", 0, "10", "0"},
|
|
{"1", "10", 0, "0", "1"},
|
|
{"1", "4", 2, "0.25", "0"},
|
|
{"1", "8", 2, "0.12", "0.04"},
|
|
{"10", "3", 1, "3.3", "0.1"},
|
|
{"100", "3", 1, "33.3", "0.1"},
|
|
{"1000", "10", -3, "0", "1000"},
|
|
{"1e-3", "2e-5", 0, "50", "0"},
|
|
{"1e-3", "2e-3", 1, "0.5", "0"},
|
|
{"4e-3", "0.8", 4, "5e-3", "0"},
|
|
{"4.1e-3", "0.8", 3, "5e-3", "1e-4"},
|
|
{"-4", "-3", 0, "1", "-1"},
|
|
{"-4", "3", 0, "-1", "-1"},
|
|
}
|
|
|
|
for _, inp4 := range cases {
|
|
d, _ := NewFromString(inp4.d)
|
|
d2, _ := NewFromString(inp4.d2)
|
|
prec := inp4.exp
|
|
q, r := d.QuoRem(d2, prec)
|
|
expectedQ, _ := NewFromString(inp4.q)
|
|
expectedR, _ := NewFromString(inp4.r)
|
|
if !q.Equal(expectedQ) || !r.Equal(expectedR) {
|
|
t.Errorf("bad QuoRem division %s , %s , %d got %v, %v expected %s , %s",
|
|
inp4.d, inp4.d2, prec, q, r, inp4.q, inp4.r)
|
|
}
|
|
if !d.Equal(d2.Mul(q).Add(r)) {
|
|
t.Errorf("not fitting: d=%v, d2= %v, prec=%d, q=%v, r=%v",
|
|
d, d2, prec, q, r)
|
|
}
|
|
if !q.Equal(q.Truncate(prec)) {
|
|
t.Errorf("quotient wrong precision: d=%v, d2= %v, prec=%d, q=%v, r=%v",
|
|
d, d2, prec, q, r)
|
|
}
|
|
if r.Abs().Cmp(d2.Abs().Mul(New(1, -prec))) >= 0 {
|
|
t.Errorf("remainder too large: d=%v, d2= %v, prec=%d, q=%v, r=%v",
|
|
d, d2, prec, q, r)
|
|
}
|
|
if r.value.Sign()*d.value.Sign() < 0 {
|
|
t.Errorf("signum of divisor and rest do not match: d=%v, d2= %v, prec=%d, q=%v, r=%v",
|
|
d, d2, prec, q, r)
|
|
}
|
|
}
|
|
}
|
|
|
|
type DivTestCase struct {
|
|
d Decimal
|
|
d2 Decimal
|
|
prec int32
|
|
}
|
|
|
|
func createDivTestCases() []DivTestCase {
|
|
res := make([]DivTestCase, 0)
|
|
var n int32 = 5
|
|
a := []int{1, 2, 3, 6, 7, 10, 100, 14, 5, 400, 0, 1000000, 1000000 + 1, 1000000 - 1}
|
|
for s := -1; s < 2; s = s + 2 { // 2
|
|
for s2 := -1; s2 < 2; s2 = s2 + 2 { // 2
|
|
for e1 := -n; e1 <= n; e1++ { // 2n+1
|
|
for e2 := -n; e2 <= n; e2++ { // 2n+1
|
|
var prec int32
|
|
for prec = -n; prec <= n; prec++ { // 2n+1
|
|
for _, v1 := range a { // 11
|
|
for _, v2 := range a { // 11, even if 0 is skipped
|
|
sign1 := New(int64(s), 0)
|
|
sign2 := New(int64(s2), 0)
|
|
d := sign1.Mul(New(int64(v1), int32(e1)))
|
|
d2 := sign2.Mul(New(int64(v2), int32(e2)))
|
|
res = append(res, DivTestCase{d, d2, prec})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return res
|
|
}
|
|
|
|
func TestDecimal_QuoRem2(t *testing.T) {
|
|
for _, tc := range createDivTestCases() {
|
|
d := tc.d
|
|
if sign(tc.d2) == 0 {
|
|
continue
|
|
}
|
|
d2 := tc.d2
|
|
prec := tc.prec
|
|
q, r := d.QuoRem(d2, prec)
|
|
// rule 1: d = d2*q +r
|
|
if !d.Equal(d2.Mul(q).Add(r)) {
|
|
t.Errorf("not fitting, d=%v, d2=%v, prec=%d, q=%v, r=%v",
|
|
d, d2, prec, q, r)
|
|
}
|
|
// rule 2: q is integral multiple of 10^(-prec)
|
|
if !q.Equal(q.Truncate(prec)) {
|
|
t.Errorf("quotient wrong precision, d=%v, d2=%v, prec=%d, q=%v, r=%v",
|
|
d, d2, prec, q, r)
|
|
}
|
|
// rule 3: abs(r)<abs(d) * 10^(-prec)
|
|
if r.Abs().Cmp(d2.Abs().Mul(New(1, -prec))) >= 0 {
|
|
t.Errorf("remainder too large, d=%v, d2=%v, prec=%d, q=%v, r=%v",
|
|
d, d2, prec, q, r)
|
|
}
|
|
// rule 4: r and d have the same sign
|
|
if r.value.Sign()*d.value.Sign() < 0 {
|
|
t.Errorf("signum of divisor and rest do not match, "+
|
|
"d=%v, d2=%v, prec=%d, q=%v, r=%v",
|
|
d, d2, prec, q, r)
|
|
}
|
|
}
|
|
}
|
|
|
|
// this is the old Div method from decimal
|
|
// Div returns d / d2. If it doesn't divide exactly, the result will have
|
|
// DivisionPrecision digits after the decimal point.
|
|
func (d Decimal) DivOld(d2 Decimal, prec int) Decimal {
|
|
// NOTE(vadim): division is hard, use Rat to do it
|
|
ratNum := d.Rat()
|
|
ratDenom := d2.Rat()
|
|
|
|
quoRat := big.NewRat(0, 1).Quo(ratNum, ratDenom)
|
|
|
|
// HACK(vadim): converting from Rat to Decimal inefficiently for now
|
|
ret, err := NewFromString(quoRat.FloatString(prec))
|
|
if err != nil {
|
|
panic(err) // this should never happen
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func Benchmark_DivideOriginal(b *testing.B) {
|
|
tcs := createDivTestCases()
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
for _, tc := range tcs {
|
|
d := tc.d
|
|
if sign(tc.d2) == 0 {
|
|
continue
|
|
}
|
|
d2 := tc.d2
|
|
prec := tc.prec
|
|
a := d.DivOld(d2, int(prec))
|
|
if sign(a) > 2 {
|
|
panic("dummy panic")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func Benchmark_DivideNew(b *testing.B) {
|
|
tcs := createDivTestCases()
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
for _, tc := range tcs {
|
|
d := tc.d
|
|
if sign(tc.d2) == 0 {
|
|
continue
|
|
}
|
|
d2 := tc.d2
|
|
prec := tc.prec
|
|
a := d.DivRound(d2, prec)
|
|
if sign(a) > 2 {
|
|
panic("dummy panic")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func sign(d Decimal) int {
|
|
return d.value.Sign()
|
|
}
|
|
|
|
// rules for rounded divide, rounded to integer
|
|
// rounded_divide(d,d2) = q
|
|
// sign q * sign (d/d2) >= 0
|
|
// for d and d2 >0 :
|
|
// q is already rounded
|
|
// q = d/d2 + r , with r > -0.5 and r <= 0.5
|
|
// thus q-d/d2 = r, with r > -0.5 and r <= 0.5
|
|
// and d2 q -d = r d2 with r d2 > -d2/2 and r d2 <= d2/2
|
|
// and 2 (d2 q -d) = x with x > -d2 and x <= d2
|
|
// if we factor in precision then x > -d2 * 10^(-precision) and x <= d2 * 10(-precision)
|
|
|
|
func TestDecimal_DivRound(t *testing.T) {
|
|
cases := []struct {
|
|
d string
|
|
d2 string
|
|
prec int32
|
|
result string
|
|
}{
|
|
{"2", "2", 0, "1"},
|
|
{"1", "2", 0, "1"},
|
|
{"-1", "2", 0, "-1"},
|
|
{"-1", "-2", 0, "1"},
|
|
{"1", "-2", 0, "-1"},
|
|
{"1", "-20", 1, "-0.1"},
|
|
{"1", "-20", 2, "-0.05"},
|
|
{"1", "20.0000000000000000001", 1, "0"},
|
|
{"1", "19.9999999999999999999", 1, "0.1"},
|
|
}
|
|
for _, s := range cases {
|
|
d, _ := NewFromString(s.d)
|
|
d2, _ := NewFromString(s.d2)
|
|
result, _ := NewFromString(s.result)
|
|
prec := s.prec
|
|
q := d.DivRound(d2, prec)
|
|
if sign(q)*sign(d)*sign(d2) < 0 {
|
|
t.Errorf("sign of quotient wrong, got: %v/%v is about %v", d, d2, q)
|
|
}
|
|
x := q.Mul(d2).Abs().Sub(d.Abs()).Mul(New(2, 0))
|
|
if x.Cmp(d2.Abs().Mul(New(1, -prec))) > 0 {
|
|
t.Errorf("wrong rounding, got: %v/%v prec=%d is about %v", d, d2, prec, q)
|
|
}
|
|
if x.Cmp(d2.Abs().Mul(New(-1, -prec))) <= 0 {
|
|
t.Errorf("wrong rounding, got: %v/%v prec=%d is about %v", d, d2, prec, q)
|
|
}
|
|
if !q.Equal(result) {
|
|
t.Errorf("rounded division wrong %s / %s scale %d = %s, got %v", s.d, s.d2, prec, s.result, q)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDecimal_DivRound2(t *testing.T) {
|
|
for _, tc := range createDivTestCases() {
|
|
d := tc.d
|
|
if sign(tc.d2) == 0 {
|
|
continue
|
|
}
|
|
d2 := tc.d2
|
|
prec := tc.prec
|
|
q := d.DivRound(d2, prec)
|
|
if sign(q)*sign(d)*sign(d2) < 0 {
|
|
t.Errorf("sign of quotient wrong, got: %v/%v is about %v", d, d2, q)
|
|
}
|
|
x := q.Mul(d2).Abs().Sub(d.Abs()).Mul(New(2, 0))
|
|
if x.Cmp(d2.Abs().Mul(New(1, -prec))) > 0 {
|
|
t.Errorf("wrong rounding, got: %v/%v prec=%d is about %v", d, d2, prec, q)
|
|
}
|
|
if x.Cmp(d2.Abs().Mul(New(-1, -prec))) <= 0 {
|
|
t.Errorf("wrong rounding, got: %v/%v prec=%d is about %v", d, d2, prec, q)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDecimal_RoundCash(t *testing.T) {
|
|
tests := []struct {
|
|
d string
|
|
interval uint8
|
|
result string
|
|
}{
|
|
{"3.44", 5, "3.45"},
|
|
{"3.43", 5, "3.45"},
|
|
{"3.42", 5, "3.40"},
|
|
{"3.425", 5, "3.45"},
|
|
{"3.47", 5, "3.45"},
|
|
{"3.478", 5, "3.50"},
|
|
{"3.48", 5, "3.50"},
|
|
{"348", 5, "348"},
|
|
|
|
{"3.23", 10, "3.20"},
|
|
{"3.33", 10, "3.30"},
|
|
{"3.53", 10, "3.50"},
|
|
{"3.949", 10, "3.90"},
|
|
{"3.95", 10, "4.00"},
|
|
{"395", 10, "395"},
|
|
|
|
{"6.42", 15, "6.40"},
|
|
{"6.39", 15, "6.40"},
|
|
{"6.35", 15, "6.30"},
|
|
{"6.36", 15, "6.40"},
|
|
{"6.349", 15, "6.30"},
|
|
{"6.30", 15, "6.30"},
|
|
{"666", 15, "666"},
|
|
|
|
{"3.23", 25, "3.25"},
|
|
{"3.33", 25, "3.25"},
|
|
{"3.53", 25, "3.50"},
|
|
{"3.93", 25, "4.00"},
|
|
{"3.41", 25, "3.50"},
|
|
|
|
{"3.249", 50, "3.00"},
|
|
{"3.33", 50, "3.50"},
|
|
{"3.749999999", 50, "3.50"},
|
|
{"3.75", 50, "4.00"},
|
|
{"3.93", 50, "4.00"},
|
|
{"393", 50, "393"},
|
|
|
|
{"3.249", 100, "3.00"},
|
|
{"3.49999", 100, "3.00"},
|
|
{"3.50", 100, "4.00"},
|
|
{"3.75", 100, "4.00"},
|
|
{"3.93", 100, "4.00"},
|
|
{"393", 100, "393"},
|
|
}
|
|
for i, test := range tests {
|
|
d, _ := NewFromString(test.d)
|
|
haveRounded := d.RoundCash(test.interval)
|
|
result, _ := NewFromString(test.result)
|
|
|
|
if !haveRounded.Equal(result) {
|
|
t.Errorf("Index %d: Cash rounding for %q interval %d want %q, have %q", i, test.d, test.interval, test.result, haveRounded)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDecimal_RoundCash_Panic(t *testing.T) {
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
if have, ok := r.(string); ok {
|
|
const want = "Decimal does not support this Cash rounding interval `231`. Supported: 5, 10, 15, 25, 50, 100"
|
|
if want != have {
|
|
t.Errorf("\nWant: %q\nHave: %q", want, have)
|
|
}
|
|
} else {
|
|
t.Errorf("Panic should contain an error string but got:\n%+v", r)
|
|
}
|
|
} else {
|
|
t.Error("Expecting a panic but got nothing")
|
|
}
|
|
}()
|
|
d, _ := NewFromString("1")
|
|
d.RoundCash(231)
|
|
}
|
|
|
|
func BenchmarkDecimal_RoundCash_Five(b *testing.B) {
|
|
const want = "3.50"
|
|
for i := 0; i < b.N; i++ {
|
|
val := New(3478, -3)
|
|
if have := val.StringFixedCash(5); have != want {
|
|
b.Fatalf("\nHave: %q\nWant: %q", have, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkDecimal_RoundCash_Fifteen(b *testing.B) {
|
|
const want = "6.30"
|
|
for i := 0; i < b.N; i++ {
|
|
val := New(635, -2)
|
|
if have := val.StringFixedCash(15); have != want {
|
|
b.Fatalf("\nHave: %q\nWant: %q", have, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDecimal_Mod(t *testing.T) {
|
|
type Inp struct {
|
|
a string
|
|
b string
|
|
}
|
|
|
|
inputs := map[Inp]string{
|
|
Inp{"3", "2"}: "1",
|
|
Inp{"3451204593", "2454495034"}: "996709559",
|
|
Inp{"24544.95034", ".3451204593"}: "0.3283950433",
|
|
Inp{".1", ".1"}: "0",
|
|
Inp{"0", "1.001"}: "0",
|
|
Inp{"-7.5", "2"}: "-1.5",
|
|
Inp{"7.5", "-2"}: "1.5",
|
|
Inp{"-7.5", "-2"}: "-1.5",
|
|
}
|
|
|
|
for inp, res := range inputs {
|
|
a, err := NewFromString(inp.a)
|
|
if err != nil {
|
|
t.FailNow()
|
|
}
|
|
b, err := NewFromString(inp.b)
|
|
if err != nil {
|
|
t.FailNow()
|
|
}
|
|
c := a.Mod(b)
|
|
if c.String() != res {
|
|
t.Errorf("expected %s, got %s", res, c.String())
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDecimal_Overflow(t *testing.T) {
|
|
if !didPanic(func() { New(1, math.MinInt32).Mul(New(1, math.MinInt32)) }) {
|
|
t.Fatalf("should have gotten an overflow panic")
|
|
}
|
|
if !didPanic(func() { New(1, math.MaxInt32).Mul(New(1, math.MaxInt32)) }) {
|
|
t.Fatalf("should have gotten an overflow panic")
|
|
}
|
|
}
|
|
|
|
func TestDecimal_ExtremeValues(t *testing.T) {
|
|
// NOTE(vadim): this test takes pretty much forever
|
|
if testing.Short() {
|
|
t.Skip()
|
|
}
|
|
|
|
// NOTE(vadim): Seriously, the numbers involved are so large that this
|
|
// test will take way too long, so mark it as success if it takes over
|
|
// 1 second. The way this test typically fails (integer overflow) is that
|
|
// a wrong result appears quickly, so if it takes a long time then it is
|
|
// probably working properly.
|
|
// Why even bother testing this? Completeness, I guess. -Vadim
|
|
const timeLimit = 1 * time.Second
|
|
test := func(f func()) {
|
|
c := make(chan bool)
|
|
go func() {
|
|
f()
|
|
close(c)
|
|
}()
|
|
select {
|
|
case <-c:
|
|
case <-time.After(timeLimit):
|
|
}
|
|
}
|
|
|
|
test(func() {
|
|
got := New(123, math.MinInt32).Floor()
|
|
if !got.Equal(NewFromFloat(0)) {
|
|
t.Errorf("Error: got %s, expected 0", got)
|
|
}
|
|
})
|
|
test(func() {
|
|
got := New(123, math.MinInt32).Ceil()
|
|
if !got.Equal(NewFromFloat(1)) {
|
|
t.Errorf("Error: got %s, expected 1", got)
|
|
}
|
|
})
|
|
test(func() {
|
|
got := New(123, math.MinInt32).Rat().FloatString(10)
|
|
expected := "0.0000000000"
|
|
if got != expected {
|
|
t.Errorf("Error: got %s, expected %s", got, expected)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestIntPart(t *testing.T) {
|
|
for _, testCase := range []struct {
|
|
Dec string
|
|
IntPart int64
|
|
}{
|
|
{"0.01", 0},
|
|
{"12.1", 12},
|
|
{"9999.999", 9999},
|
|
{"-32768.01234", -32768},
|
|
} {
|
|
d, err := NewFromString(testCase.Dec)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if d.IntPart() != testCase.IntPart {
|
|
t.Errorf("expect %d, got %d", testCase.IntPart, d.IntPart())
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDecimal_Min(t *testing.T) {
|
|
// the first element in the array is the expected answer, rest are inputs
|
|
testCases := [][]float64{
|
|
{0, 0},
|
|
{1, 1},
|
|
{-1, -1},
|
|
{1, 1, 2},
|
|
{-2, 1, 2, -2},
|
|
{-3, 0, 2, -2, -3},
|
|
}
|
|
|
|
for _, test := range testCases {
|
|
expected, input := test[0], test[1:]
|
|
expectedDecimal := NewFromFloat(expected)
|
|
decimalInput := []Decimal{}
|
|
for _, inp := range input {
|
|
d := NewFromFloat(inp)
|
|
decimalInput = append(decimalInput, d)
|
|
}
|
|
got := Min(decimalInput[0], decimalInput[1:]...)
|
|
if !got.Equal(expectedDecimal) {
|
|
t.Errorf("Expected %v, got %v, input=%+v", expectedDecimal, got,
|
|
decimalInput)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDecimal_Max(t *testing.T) {
|
|
// the first element in the array is the expected answer, rest are inputs
|
|
testCases := [][]float64{
|
|
{0, 0},
|
|
{1, 1},
|
|
{-1, -1},
|
|
{2, 1, 2},
|
|
{2, 1, 2, -2},
|
|
{3, 0, 3, -2},
|
|
{-2, -3, -2},
|
|
}
|
|
|
|
for _, test := range testCases {
|
|
expected, input := test[0], test[1:]
|
|
expectedDecimal := NewFromFloat(expected)
|
|
decimalInput := []Decimal{}
|
|
for _, inp := range input {
|
|
d := NewFromFloat(inp)
|
|
decimalInput = append(decimalInput, d)
|
|
}
|
|
got := Max(decimalInput[0], decimalInput[1:]...)
|
|
if !got.Equal(expectedDecimal) {
|
|
t.Errorf("Expected %v, got %v, input=%+v", expectedDecimal, got,
|
|
decimalInput)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDecimal_Scan(t *testing.T) {
|
|
// test the Scan method that implements the
|
|
// sql.Scanner interface
|
|
// check for the for different type of values
|
|
// that are possible to be received from the database
|
|
// drivers
|
|
|
|
// in normal operations the db driver (sqlite at least)
|
|
// will return an int64 if you specified a numeric format
|
|
a := Decimal{}
|
|
dbvalue := float64(54.33)
|
|
expected := NewFromFloat(dbvalue)
|
|
|
|
err := a.Scan(dbvalue)
|
|
if err != nil {
|
|
// Scan failed... no need to test result value
|
|
t.Errorf("a.Scan(54.33) failed with message: %s", err)
|
|
|
|
} else {
|
|
// Scan succeeded... test resulting values
|
|
if !a.Equal(expected) {
|
|
t.Errorf("%s does not equal to %s", a, expected)
|
|
}
|
|
}
|
|
|
|
// apparently MySQL 5.7.16 and returns these as float32 so we need
|
|
// to handle these as well
|
|
dbvalueFloat32 := float32(54.33)
|
|
expected = NewFromFloat(float64(dbvalueFloat32))
|
|
|
|
err = a.Scan(dbvalueFloat32)
|
|
if err != nil {
|
|
// Scan failed... no need to test result value
|
|
t.Errorf("a.Scan(54.33) failed with message: %s", err)
|
|
|
|
} else {
|
|
// Scan succeeded... test resulting values
|
|
if !a.Equal(expected) {
|
|
t.Errorf("%s does not equal to %s", a, expected)
|
|
}
|
|
}
|
|
|
|
// at least SQLite returns an int64 when 0 is stored in the db
|
|
// and you specified a numeric format on the schema
|
|
dbvalueInt := int64(0)
|
|
expected = New(dbvalueInt, 0)
|
|
|
|
err = a.Scan(dbvalueInt)
|
|
if err != nil {
|
|
// Scan failed... no need to test result value
|
|
t.Errorf("a.Scan(0) failed with message: %s", err)
|
|
|
|
} else {
|
|
// Scan succeeded... test resulting values
|
|
if !a.Equal(expected) {
|
|
t.Errorf("%s does not equal to %s", a, expected)
|
|
}
|
|
}
|
|
|
|
// in case you specified a varchar in your SQL schema,
|
|
// the database driver will return byte slice []byte
|
|
valueStr := "535.666"
|
|
dbvalueStr := []byte(valueStr)
|
|
expected, err = NewFromString(valueStr)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
err = a.Scan(dbvalueStr)
|
|
if err != nil {
|
|
// Scan failed... no need to test result value
|
|
t.Errorf("a.Scan('535.666') failed with message: %s", err)
|
|
|
|
} else {
|
|
// Scan succeeded... test resulting values
|
|
if !a.Equal(expected) {
|
|
t.Errorf("%s does not equal to %s", a, expected)
|
|
}
|
|
}
|
|
|
|
// lib/pq can also return strings
|
|
expected, err = NewFromString(valueStr)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
err = a.Scan(valueStr)
|
|
if err != nil {
|
|
// Scan failed... no need to test result value
|
|
t.Errorf("a.Scan('535.666') failed with message: %s", err)
|
|
} else {
|
|
// Scan succeeded... test resulting values
|
|
if !a.Equal(expected) {
|
|
t.Errorf("%s does not equal to %s", a, expected)
|
|
}
|
|
}
|
|
|
|
type foo struct{}
|
|
err = a.Scan(foo{})
|
|
if err == nil {
|
|
t.Errorf("a.Scan(Foo{}) should have thrown an error but did not")
|
|
}
|
|
}
|
|
|
|
func TestDecimal_Value(t *testing.T) {
|
|
// Make sure this does implement the database/sql's driver.Valuer interface
|
|
var d Decimal
|
|
if _, ok := interface{}(d).(driver.Valuer); !ok {
|
|
t.Error("Decimal does not implement driver.Valuer")
|
|
}
|
|
|
|
// check that normal case is handled appropriately
|
|
a := New(1234, -2)
|
|
expected := "12.34"
|
|
value, err := a.Value()
|
|
if err != nil {
|
|
t.Errorf("Decimal(12.34).Value() failed with message: %s", err)
|
|
} else if value.(string) != expected {
|
|
t.Errorf("%s does not equal to %s", a, expected)
|
|
}
|
|
}
|
|
|
|
// old tests after this line
|
|
|
|
func TestDecimal_Scale(t *testing.T) {
|
|
a := New(1234, -3)
|
|
if a.Exponent() != -3 {
|
|
t.Errorf("error")
|
|
}
|
|
}
|
|
|
|
func TestDecimal_Abs1(t *testing.T) {
|
|
a := New(-1234, -4)
|
|
b := New(1234, -4)
|
|
|
|
c := a.Abs()
|
|
if c.Cmp(b) != 0 {
|
|
t.Errorf("error")
|
|
}
|
|
}
|
|
|
|
func TestDecimal_Abs2(t *testing.T) {
|
|
a := New(-1234, -4)
|
|
b := New(1234, -4)
|
|
|
|
c := b.Abs()
|
|
if c.Cmp(a) == 0 {
|
|
t.Errorf("error")
|
|
}
|
|
}
|
|
|
|
func TestDecimal_Equalities(t *testing.T) {
|
|
a := New(1234, 3)
|
|
b := New(1234, 3)
|
|
c := New(1234, 4)
|
|
|
|
if !a.Equal(b) {
|
|
t.Errorf("%q should equal %q", a, b)
|
|
}
|
|
if a.Equal(c) {
|
|
t.Errorf("%q should not equal %q", a, c)
|
|
}
|
|
|
|
// note, this block should be deprecated, here for backwards compatibility
|
|
if !a.Equals(b) {
|
|
t.Errorf("%q should equal %q", a, b)
|
|
}
|
|
|
|
if !c.GreaterThan(b) {
|
|
t.Errorf("%q should be greater than %q", c, b)
|
|
}
|
|
if b.GreaterThan(c) {
|
|
t.Errorf("%q should not be greater than %q", b, c)
|
|
}
|
|
if !a.GreaterThanOrEqual(b) {
|
|
t.Errorf("%q should be greater or equal %q", a, b)
|
|
}
|
|
if !c.GreaterThanOrEqual(b) {
|
|
t.Errorf("%q should be greater or equal %q", c, b)
|
|
}
|
|
if b.GreaterThanOrEqual(c) {
|
|
t.Errorf("%q should not be greater or equal %q", b, c)
|
|
}
|
|
if !b.LessThan(c) {
|
|
t.Errorf("%q should be less than %q", a, b)
|
|
}
|
|
if c.LessThan(b) {
|
|
t.Errorf("%q should not be less than %q", a, b)
|
|
}
|
|
if !a.LessThanOrEqual(b) {
|
|
t.Errorf("%q should be less than or equal %q", a, b)
|
|
}
|
|
if !b.LessThanOrEqual(c) {
|
|
t.Errorf("%q should be less than or equal %q", a, b)
|
|
}
|
|
if c.LessThanOrEqual(b) {
|
|
t.Errorf("%q should not be less than or equal %q", a, b)
|
|
}
|
|
}
|
|
|
|
func TestDecimal_ScalesNotEqual(t *testing.T) {
|
|
a := New(1234, 2)
|
|
b := New(1234, 3)
|
|
if a.Equal(b) {
|
|
t.Errorf("%q should not equal %q", a, b)
|
|
}
|
|
}
|
|
|
|
func TestDecimal_Cmp1(t *testing.T) {
|
|
a := New(123, 3)
|
|
b := New(-1234, 2)
|
|
|
|
if a.Cmp(b) != 1 {
|
|
t.Errorf("Error")
|
|
}
|
|
}
|
|
|
|
func TestDecimal_Cmp2(t *testing.T) {
|
|
a := New(123, 3)
|
|
b := New(1234, 2)
|
|
|
|
if a.Cmp(b) != -1 {
|
|
t.Errorf("Error")
|
|
}
|
|
}
|
|
|
|
func TestPow(t *testing.T) {
|
|
a := New(4, 0)
|
|
b := New(2, 0)
|
|
x := a.Pow(b)
|
|
if x.String() != "16" {
|
|
t.Errorf("Error, saw %s", x.String())
|
|
}
|
|
}
|
|
|
|
func TestNegativePow(t *testing.T) {
|
|
a := New(4, 0)
|
|
b := New(-2, 0)
|
|
x := a.Pow(b)
|
|
if x.String() != "0.0625" {
|
|
t.Errorf("Error, saw %s", x.String())
|
|
}
|
|
}
|
|
|
|
func TestDecimal_Sign(t *testing.T) {
|
|
if Zero.Sign() != 0 {
|
|
t.Errorf("%q should have sign 0", Zero)
|
|
}
|
|
|
|
one := New(1, 0)
|
|
if one.Sign() != 1 {
|
|
t.Errorf("%q should have sign 1", one)
|
|
}
|
|
|
|
mone := New(-1, 0)
|
|
if mone.Sign() != -1 {
|
|
t.Errorf("%q should have sign -1", mone)
|
|
}
|
|
}
|
|
|
|
func didPanic(f func()) bool {
|
|
ret := false
|
|
func() {
|
|
|
|
defer func() {
|
|
if message := recover(); message != nil {
|
|
ret = true
|
|
}
|
|
}()
|
|
|
|
// call the target function
|
|
f()
|
|
|
|
}()
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
func TestDecimal_Coefficient(t *testing.T) {
|
|
d := New(123, 0)
|
|
co := d.Coefficient()
|
|
if co.Int64() != 123 {
|
|
t.Error("Coefficient should be 123; Got:", co)
|
|
}
|
|
co.Set(big.NewInt(0))
|
|
if d.IntPart() != 123 {
|
|
t.Error("Modifying coefficient modified Decimal; Got:", d)
|
|
}
|
|
}
|
|
|
|
type DecimalSlice []Decimal
|
|
|
|
func (p DecimalSlice) Len() int { return len(p) }
|
|
func (p DecimalSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
|
func (p DecimalSlice) Less(i, j int) bool { return p[i].Cmp(p[j]) < 0 }
|
|
func Benchmark_Cmp(b *testing.B) {
|
|
decimals := DecimalSlice([]Decimal{})
|
|
for i := 0; i < 1000000; i++ {
|
|
decimals = append(decimals, New(int64(i), 0))
|
|
}
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
sort.Sort(decimals)
|
|
}
|
|
}
|
|
|
|
func TestNullDecimal_Scan(t *testing.T) {
|
|
// test the Scan method that implements the
|
|
// sql.Scanner interface
|
|
// check for the for different type of values
|
|
// that are possible to be received from the database
|
|
// drivers
|
|
|
|
// in normal operations the db driver (sqlite at least)
|
|
// will return an int64 if you specified a numeric format
|
|
|
|
// Make sure handles nil values
|
|
a := NullDecimal{}
|
|
var dbvaluePtr interface{}
|
|
err := a.Scan(dbvaluePtr)
|
|
if err != nil {
|
|
// Scan failed... no need to test result value
|
|
t.Errorf("a.Scan(nil) failed with message: %s", err)
|
|
} else {
|
|
if a.Valid {
|
|
t.Errorf("%s is not null", a.Decimal)
|
|
}
|
|
}
|
|
|
|
dbvalue := float64(54.33)
|
|
expected := NewFromFloat(dbvalue)
|
|
|
|
err = a.Scan(dbvalue)
|
|
if err != nil {
|
|
// Scan failed... no need to test result value
|
|
t.Errorf("a.Scan(54.33) failed with message: %s", err)
|
|
|
|
} else {
|
|
// Scan succeeded... test resulting values
|
|
if !a.Valid {
|
|
t.Errorf("%s is null", a.Decimal)
|
|
} else if !a.Decimal.Equals(expected) {
|
|
t.Errorf("%s does not equal to %s", a.Decimal, expected)
|
|
}
|
|
}
|
|
|
|
// at least SQLite returns an int64 when 0 is stored in the db
|
|
// and you specified a numeric format on the schema
|
|
dbvalueInt := int64(0)
|
|
expected = New(dbvalueInt, 0)
|
|
|
|
err = a.Scan(dbvalueInt)
|
|
if err != nil {
|
|
// Scan failed... no need to test result value
|
|
t.Errorf("a.Scan(0) failed with message: %s", err)
|
|
|
|
} else {
|
|
// Scan succeeded... test resulting values
|
|
if !a.Valid {
|
|
t.Errorf("%s is null", a.Decimal)
|
|
} else if !a.Decimal.Equals(expected) {
|
|
t.Errorf("%v does not equal %v", a, expected)
|
|
}
|
|
}
|
|
|
|
// in case you specified a varchar in your SQL schema,
|
|
// the database driver will return byte slice []byte
|
|
valueStr := "535.666"
|
|
dbvalueStr := []byte(valueStr)
|
|
expected, err = NewFromString(valueStr)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
err = a.Scan(dbvalueStr)
|
|
if err != nil {
|
|
// Scan failed... no need to test result value
|
|
t.Errorf("a.Scan('535.666') failed with message: %s", err)
|
|
|
|
} else {
|
|
// Scan succeeded... test resulting values
|
|
if !a.Valid {
|
|
t.Errorf("%s is null", a.Decimal)
|
|
} else if !a.Decimal.Equals(expected) {
|
|
t.Errorf("%v does not equal %v", a, expected)
|
|
}
|
|
}
|
|
|
|
// lib/pq can also return strings
|
|
expected, err = NewFromString(valueStr)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
err = a.Scan(valueStr)
|
|
if err != nil {
|
|
// Scan failed... no need to test result value
|
|
t.Errorf("a.Scan('535.666') failed with message: %s", err)
|
|
} else {
|
|
// Scan succeeded... test resulting values
|
|
if !a.Valid {
|
|
t.Errorf("%s is null", a.Decimal)
|
|
} else if !a.Decimal.Equals(expected) {
|
|
t.Errorf("%v does not equal %v", a, expected)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestNullDecimal_Value(t *testing.T) {
|
|
// Make sure this does implement the database/sql's driver.Valuer interface
|
|
var nullDecimal NullDecimal
|
|
if _, ok := interface{}(nullDecimal).(driver.Valuer); !ok {
|
|
t.Error("NullDecimal does not implement driver.Valuer")
|
|
}
|
|
|
|
// check that null is handled appropriately
|
|
value, err := nullDecimal.Value()
|
|
if err != nil {
|
|
t.Errorf("NullDecimal{}.Valid() failed with message: %s", err)
|
|
} else if value != nil {
|
|
t.Errorf("%v is not nil", value)
|
|
}
|
|
|
|
// check that normal case is handled appropriately
|
|
a := NullDecimal{Decimal: New(1234, -2), Valid: true}
|
|
expected := "12.34"
|
|
value, err = a.Value()
|
|
if err != nil {
|
|
t.Errorf("NullDecimal(12.34).Value() failed with message: %s", err)
|
|
} else if value.(string) != expected {
|
|
t.Errorf("%v does not equal %v", a, expected)
|
|
}
|
|
}
|
|
|
|
func TestBinary(t *testing.T) {
|
|
for _, y := range testTable {
|
|
x := y.float
|
|
|
|
// Create the decimal
|
|
d1 := NewFromFloat(x)
|
|
|
|
// Encode to binary
|
|
b, err := d1.MarshalBinary()
|
|
if err != nil {
|
|
t.Errorf("error marshalling %v to binary: %v", d1, err)
|
|
}
|
|
|
|
// Restore from binary
|
|
var d2 Decimal
|
|
err = (&d2).UnmarshalBinary(b)
|
|
if err != nil {
|
|
t.Errorf("error unmarshalling from binary: %v", err)
|
|
}
|
|
|
|
// The restored decimal should equal the original
|
|
if !d1.Equals(d2) {
|
|
t.Errorf("expected %v when restoring, got %v", d1, d2)
|
|
}
|
|
}
|
|
}
|
|
|
|
func slicesEqual(a, b []byte) bool {
|
|
for i, val := range a {
|
|
if b[i] != val {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func TestGobEncode(t *testing.T) {
|
|
for _, y := range testTable {
|
|
x := y.float
|
|
d1 := NewFromFloat(x)
|
|
|
|
b1, err := d1.GobEncode()
|
|
if err != nil {
|
|
t.Errorf("error encoding %v to binary: %v", d1, err)
|
|
}
|
|
|
|
d2 := NewFromFloat(x)
|
|
|
|
b2, err := d2.GobEncode()
|
|
if err != nil {
|
|
t.Errorf("error encoding %v to binary: %v", d2, err)
|
|
}
|
|
|
|
if !slicesEqual(b1, b2) {
|
|
t.Errorf("something about the gobencode is not working properly \n%v\n%v", b1, b2)
|
|
}
|
|
|
|
var d3 Decimal
|
|
err = d3.GobDecode(b1)
|
|
if err != nil {
|
|
t.Errorf("Error gobdecoding %v, got %v", b1, d3)
|
|
}
|
|
var d4 Decimal
|
|
err = d4.GobDecode(b2)
|
|
if err != nil {
|
|
t.Errorf("Error gobdecoding %v, got %v", b2, d4)
|
|
}
|
|
|
|
eq := d3.Equal(d4)
|
|
if eq != true {
|
|
t.Errorf("Encoding then decoding mutated Decimal")
|
|
}
|
|
|
|
eq = d1.Equal(d3)
|
|
if eq != true {
|
|
t.Errorf("Error gobencoding/decoding %v, got %v", d1, d3)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSum(t *testing.T) {
|
|
vals := make([]Decimal, 10)
|
|
var i = int64(0)
|
|
|
|
for key := range vals {
|
|
vals[key] = New(i, 0)
|
|
i++
|
|
}
|
|
|
|
sum := Sum(vals[0], vals[1:]...)
|
|
if !sum.Equal(New(45, 0)) {
|
|
t.Errorf("Failed to calculate sum, expected %s got %s", New(45, 0), sum)
|
|
}
|
|
}
|
|
|
|
func TestAvg(t *testing.T) {
|
|
vals := make([]Decimal, 10)
|
|
var i = int64(0)
|
|
|
|
for key := range vals {
|
|
vals[key] = New(i, 0)
|
|
i++
|
|
}
|
|
|
|
avg := Avg(vals[0], vals[1:]...)
|
|
if !avg.Equal(NewFromFloat(4.5)) {
|
|
t.Errorf("Failed to calculate average, expected %s got %s", NewFromFloat(4.5).String(), avg.String())
|
|
}
|
|
}
|
|
|
|
func TestRoundBankAnomaly(t *testing.T) {
|
|
a := New(25, -1)
|
|
b := New(250, -2)
|
|
|
|
if !a.Equal(b) {
|
|
t.Errorf("Expected %s to equal %s", a, b)
|
|
}
|
|
|
|
expected := New(2, 0)
|
|
|
|
aRounded := a.RoundBank(0)
|
|
if !aRounded.Equal(expected) {
|
|
t.Errorf("Expected bank rounding %s to equal %s, but it was %s", a, expected, aRounded)
|
|
}
|
|
|
|
bRounded := b.RoundBank(0)
|
|
if !bRounded.Equal(expected) {
|
|
t.Errorf("Expected bank rounding %s to equal %s, but it was %s", b, expected, bRounded)
|
|
}
|
|
}
|
|
|
|
// Trig tests
|
|
|
|
// For Atan
|
|
func TestAtan(t *testing.T) {
|
|
inps := []string{
|
|
"-2.91919191919191919",
|
|
"-1.0",
|
|
"-0.25",
|
|
"0.0",
|
|
"0.33",
|
|
"1.0",
|
|
"5.0",
|
|
"10",
|
|
"11000020.2407442310156021090304691671842603586882014729198302312846062338790031898128063403419218957424",
|
|
}
|
|
sols := []string{
|
|
"-1.24076438822058001027437062753106",
|
|
"-0.78539816339744833061616997868383",
|
|
"-0.24497866312686415",
|
|
"0.0",
|
|
"0.318747560420644443",
|
|
"0.78539816339744833061616997868383",
|
|
"1.37340076694501580123233995736766",
|
|
"1.47112767430373453123233995736766",
|
|
"1.57079623588597296123259450235374",
|
|
}
|
|
for i, inp := range inps {
|
|
d, err := NewFromString(inp)
|
|
if err != nil {
|
|
t.FailNow()
|
|
}
|
|
s, err := NewFromString(sols[i])
|
|
if err != nil {
|
|
t.FailNow()
|
|
}
|
|
a := d.Atan()
|
|
if !a.Equal(s) {
|
|
t.Errorf("expected %s, got %s", s, a)
|
|
}
|
|
}
|
|
}
|
|
|
|
// For Sin
|
|
func TestSin(t *testing.T) {
|
|
inps := []string{
|
|
"-2.91919191919191919",
|
|
"-1.0",
|
|
"-0.25",
|
|
"0.0",
|
|
"0.33",
|
|
"1.0",
|
|
"5.0",
|
|
"10",
|
|
"11000020.2407442310156021090304691671842603586882014729198302312846062338790031898128063403419218957424",
|
|
}
|
|
sols := []string{"-0.22057186252002995641471297726318877448242875710373383657841216606788849153474483300147427943530288911869356126149550184271061369789963810497434594683859566879253561990821788142048867910104964466745284318577343435957806286762494529983369776697504436326725441516925396488258485248699247367113416543705253919473126183478178486954138205996912770183192357029798618739277146694040778731661407420114923656224752540889120768",
|
|
"-0.841470984807896544828551915928318375739843472469519282898610111931110319333748010828751784005573402229699531838022117989945539661588502120624574802425114599802714611508860519655182175315926637327774878594985045816542706701485174683683726979309922117859910272413672784175028365607893544855897795184024100973080880074046886009375162838756876336134083638363801171409953672944184918309063800980214873465660723218405962257950683415203634506166523593278",
|
|
"-0.2474039592545229296662577977006816864013671875",
|
|
"0",
|
|
"0.3240430283948683457891331120415701894104386268737728",
|
|
"0.841470984807896544828551915928318375739843472469519282898610111931110319333748010828751784005573402229699531838022117989945539661588502120624574802425114599802714611508860519655182175315926637327774878594985045816542706701485174683683726979309922117859910272413672784175028365607893544855897795184024100973080880074046886009375162838756876336134083638363801171409953672944184918309063800980214873465660723218405962257950683415203634506166523593278",
|
|
"-0.958924274663138409032065951037351417114444405831206421994322505831797734568720303321152847999323782235893449831846516332891972309733806145798957570823292783131379570446989311599459252931842975162373777189193072018951049969744350662993214861042908755303566670204873618202680865638534865944483058650517380292320436016362659617294570185140789829574277032406195741535138712427510938542219940873171248862329526140744770994303733112530324791184417282382",
|
|
"-0.54402111088937016772477554483765124109312606762621462357463994520238396180161585438877562935656067241573063207614488370477645194661241525080677431257416988398683714890165970942834453391033857378247849486306346743023618509617104937236345831462093934032592562972419977883837745736210439651143668255744843041350221801750331646628192115694352540293150183983357476391787825596543270240461102629075832777618592034309799936",
|
|
"-0.564291758480422881634770440632390475980828840253516895637281099241819037882007239070203007530085741820184955492382572029153491807930868879341091067301689987699567034024159005627332722089169680203292567574310010066799858914647295684974242359142300929248173166551428537696685165964880390889406578530338963341989826231514301546476672476399906348023294571001061677668735117509440368611093448917120819545826797975989350435900286332895885871219875665471968941335407351099209738417818747252638912592184093301853338763294381446907254104878969784040526201729163408095795934201105630182851806342356035203279670146684553491616847294749721014579109870396804713831114709372638323643327823671187472335866664108658093206409882794958673673978956925250261545083579947618620746006004554405785185537391110314728988164693223775249484198058394348289545771967707968288542718255197272633789792059019367104377340604030147471453833808674013259696102003732963091159662478879760121731138091114134586544668859915547568540172541576138084166990547345181184322550297604278946942918844039406876827936831612756344331500301118652183156052728447906384772901595431751550607818380262138322673253023464533931883787069611052589166000316238423939491520880451263927981787175602294299295744",
|
|
}
|
|
for i, inp := range inps {
|
|
d, err := NewFromString(inp)
|
|
if err != nil {
|
|
t.FailNow()
|
|
}
|
|
s, err := NewFromString(sols[i])
|
|
if err != nil {
|
|
t.FailNow()
|
|
}
|
|
a := d.Sin()
|
|
if !a.Equal(s) {
|
|
t.Errorf("expected %s, got %s", s, a)
|
|
}
|
|
}
|
|
}
|
|
|
|
// For Cos
|
|
func TestCos(t *testing.T) {
|
|
inps := []string{
|
|
"-2.91919191919191919",
|
|
"-1.0",
|
|
"-0.25",
|
|
"0.0",
|
|
"0.33",
|
|
"1.0",
|
|
"5.0",
|
|
"10",
|
|
"11000020.2407442310156021090304691671842603586882014729198302312846062338790031898128063403419218957424",
|
|
}
|
|
sols := []string{
|
|
"-0.975370726167463467746508538219884948528729295145689640359666742268127382748782064668565276308334226452812521220478854320025773591423493734486361306323829818426063430805234608660356853863442937297855742231573288105774823103008774355455799906250461848079705023428527473474556899228935370709945979509634251305018978306493011197513482210179171510947538040406781879762352211326273272515279567525396877609653501706919545667682725671944948392322552266752",
|
|
"0.54030230586813965874561515067176071767603141150991567490927772778673118786033739102174242337864109186439207498973007363884202112942385976796862442063752663646870430360736682397798633852405003167527051283327366631405990604840629657123985368031838052877290142895506386796217551784101265975360960112885444847880134909594560331781699767647860744559228420471946006511861233129745921297270844542687374552066388998112901504",
|
|
"0.968912421710644784099084544806854121387004852294921875",
|
|
"1",
|
|
"0.9460423435283869715490383692051286742343482760977712222",
|
|
"0.54030230586813965874561515067176071767603141150991567490927772778673118786033739102174242337864109186439207498973007363884202112942385976796862442063752663646870430360736682397798633852405003167527051283327366631405990604840629657123985368031838052877290142895506386796217551784101265975360960112885444847880134909594560331781699767647860744559228420471946006511861233129745921297270844542687374552066388998112901504",
|
|
"0.28366218546322646623291670213892426815646045792775066552057877370468842342090306560693620285882975471913545189522117672866861003904575909174769890684057564495184019705963607555427518763375472432216131070235796577209064861003009894615394882021220247535890708789312783718414424224334988974848162884228012265684775651099758365989567444515619764427493598258393280941942356912304265535918025036942025858493361644535438208",
|
|
"-0.839071529076452222947082170022504835457755803801719612447629165523199043803440231769716865070163209041973184176293170330332317060558438085478980463542480791358920580076809381102480339018809694514100495572097422057215638383077242523713704127605770444906854175870243452753002238589530499630034663296166308443155999957196346563161387705205277189957388653461251461388391745795979375660087266037741360406956289962327970672363315696841378765492754546688",
|
|
"-0.82557544253149396284458404188071504476091346830440347376462206521981928020803354950315062147200396866217255527509254080721982393941347365824137698491042935894213870423296625749297033966815252917361266452901192457318047750698424190124169875103436588397415032138037063155981648677895645409699825582226442363080800781881653440538927704569142007751338851079530521979429507520541625303794665680584709171813053216867014700596866196844144286737568957809383224972108999354839705480223052622003994027222120126949093911643497423100187973906980635670000034664323357488815820848035808846624518774608931622703631130673844138378087837990739103263093532314835289302930152150130664948083902949999427848344301686172490282395687167681679607401220592559832932068966455384902377056623736013617949634746332323529256184776892339963173795176200590119077305668901887229709592836744082027738666294887303249770621722032438202753270710379312736193201366287952361100525126056993039858894987153270630277483613793395809214871734783742285495171911648254647287555645360520115341268930844095156502348405343740866836850201634640011708462641462047870611041595707018966032206807675586825362640000739202116391403514629284000986232673698892843586989003952425039512325844566790376383098534975022847888104706525937115931692008959513984157709954859352131323440787667052399474107219968",
|
|
}
|
|
for i, inp := range inps {
|
|
d, err := NewFromString(inp)
|
|
if err != nil {
|
|
t.FailNow()
|
|
}
|
|
s, err := NewFromString(sols[i])
|
|
if err != nil {
|
|
t.FailNow()
|
|
}
|
|
a := d.Cos()
|
|
if !a.Equal(s) {
|
|
t.Errorf("expected %s, got %s", s, a)
|
|
}
|
|
}
|
|
}
|
|
|
|
// For Tan
|
|
func TestTan(t *testing.T) {
|
|
inps := []string{
|
|
"-2.91919191919191919",
|
|
"-1.0",
|
|
"-0.25",
|
|
"0.0",
|
|
"0.33",
|
|
"1.0",
|
|
"5.0",
|
|
"10",
|
|
"11000020.2407442310156021090304691671842603586882014729198302312846062338790031898128063403419218957424",
|
|
}
|
|
sols := []string{
|
|
"0.2261415650505790298980791606748881031998682652",
|
|
"-1.5574077246549025",
|
|
"-0.255341921221036275",
|
|
"0.0",
|
|
"0.342524867530038963",
|
|
"1.5574077246549025",
|
|
"-3.3805150062465829",
|
|
"0.6483608274590872485524085572681343280321117494",
|
|
"0.68351325561491170753499935023939368502774607234006019034769919811202010905597996164029250820702097041244539696",
|
|
}
|
|
for i, inp := range inps {
|
|
d, err := NewFromString(inp)
|
|
if err != nil {
|
|
t.FailNow()
|
|
}
|
|
s, err := NewFromString(sols[i])
|
|
if err != nil {
|
|
t.FailNow()
|
|
}
|
|
a := d.Tan()
|
|
if !a.Equal(s) {
|
|
t.Errorf("expected %s, got %s", s, a)
|
|
}
|
|
}
|
|
}
|
|
|
|
func ExampleNewFromFloat32() {
|
|
fmt.Println(NewFromFloat32(123.123123123123).String())
|
|
fmt.Println(NewFromFloat32(.123123123123123).String())
|
|
fmt.Println(NewFromFloat32(-1e13).String())
|
|
// OUTPUT:
|
|
//123.12312
|
|
//0.123123124
|
|
//-10000000000000
|
|
}
|
|
|
|
func ExampleNewFromFloat() {
|
|
fmt.Println(NewFromFloat(123.123123123123).String())
|
|
fmt.Println(NewFromFloat(.123123123123123).String())
|
|
fmt.Println(NewFromFloat(-1e13).String())
|
|
// OUTPUT:
|
|
//123.123123123123
|
|
//0.123123123123123
|
|
//-10000000000000
|
|
}
|