github.com/richardwilkes/toolbox@v1.121.0/xmath/fixed/f128/f128_test.go (about)

     1  // Copyright (c) 2016-2024 by Richard A. Wilkes. All rights reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the Mozilla Public
     4  // License, version 2.0. If a copy of the MPL was not distributed with
     5  // this file, You can obtain one at http://mozilla.org/MPL/2.0/.
     6  //
     7  // This Source Code Form is "Incompatible With Secondary Licenses", as
     8  // defined by the Mozilla Public License, version 2.0.
     9  
    10  package f128_test
    11  
    12  import (
    13  	"encoding/json"
    14  	"strings"
    15  	"testing"
    16  
    17  	"github.com/richardwilkes/toolbox/check"
    18  	"github.com/richardwilkes/toolbox/xmath/fixed"
    19  	"github.com/richardwilkes/toolbox/xmath/fixed/f128"
    20  	"gopkg.in/yaml.v3"
    21  )
    22  
    23  func TestConversion(t *testing.T) {
    24  	testConversion[fixed.D1](t)
    25  	testConversion[fixed.D2](t)
    26  	testConversion[fixed.D3](t)
    27  	testConversion[fixed.D4](t)
    28  	testConversion[fixed.D5](t)
    29  	testConversion[fixed.D6](t)
    30  }
    31  
    32  //nolint:goconst // Not helpful
    33  func testConversion[T fixed.Dx](t *testing.T) {
    34  	check.Equal(t, "0.1", f128.From[T, float64](0.1).String())
    35  	check.Equal(t, "0.2", f128.From[T, float64](0.2).String())
    36  	check.Equal(t, "0.3", f128.FromStringForced[T]("0.3").String())
    37  	check.Equal(t, "-0.1", f128.From[T, float64](-0.1).String())
    38  	check.Equal(t, "-0.2", f128.From[T, float64](-0.2).String())
    39  	check.Equal(t, "-0.3", f128.FromStringForced[T]("-0.3").String())
    40  	threeFill := strings.Repeat("3", f128.MaxDecimalDigits[T]())
    41  	check.Equal(t, "0."+threeFill, f128.FromStringForced[T]("0.33333333").String())
    42  	check.Equal(t, "-0."+threeFill, f128.FromStringForced[T]("-0.33333333").String())
    43  	sixFill := strings.Repeat("6", f128.MaxDecimalDigits[T]())
    44  	check.Equal(t, "0."+sixFill, f128.FromStringForced[T]("0.66666666").String())
    45  	check.Equal(t, "-0."+sixFill, f128.FromStringForced[T]("-0.66666666").String())
    46  	check.Equal(t, "1", f128.From[T, float64](1.0000004).String())
    47  	check.Equal(t, "1", f128.From[T, float64](1.00000049).String())
    48  	check.Equal(t, "1", f128.From[T, float64](1.0000005).String())
    49  	check.Equal(t, "1", f128.From[T, float64](1.0000009).String())
    50  	check.Equal(t, "-1", f128.From[T, float64](-1.0000004).String())
    51  	check.Equal(t, "-1", f128.From[T, float64](-1.00000049).String())
    52  	check.Equal(t, "-1", f128.From[T, float64](-1.0000005).String())
    53  	check.Equal(t, "-1", f128.From[T, float64](-1.0000009).String())
    54  	zeroFill := strings.Repeat("0", f128.MaxDecimalDigits[T]()-1)
    55  	check.Equal(t, "0."+zeroFill+"4", f128.FromStringForced[T]("0."+zeroFill+"405").String())
    56  	check.Equal(t, "-0."+zeroFill+"4", f128.FromStringForced[T]("-0."+zeroFill+"405").String())
    57  
    58  	v, err := f128.FromString[T]("33.0")
    59  	check.NoError(t, err)
    60  	check.Equal(t, v, f128.From[T, int](33))
    61  
    62  	v, err = f128.FromString[T]("33.00000000000000000000")
    63  	check.NoError(t, err)
    64  	check.Equal(t, v, f128.From[T, int](33))
    65  }
    66  
    67  func TestAddSub(t *testing.T) {
    68  	testAddSub[fixed.D1](t)
    69  	testAddSub[fixed.D2](t)
    70  	testAddSub[fixed.D3](t)
    71  	testAddSub[fixed.D4](t)
    72  	testAddSub[fixed.D5](t)
    73  	testAddSub[fixed.D6](t)
    74  }
    75  
    76  func testAddSub[T fixed.Dx](t *testing.T) {
    77  	oneThird := f128.FromStringForced[T]("0.333333")
    78  	negTwoThirds := f128.FromStringForced[T]("-0.666666")
    79  	one := f128.From[T, int](1)
    80  	oneAndTwoThirds := f128.FromStringForced[T]("1.666666")
    81  	nineThousandSix := f128.From[T, int](9006)
    82  	two := f128.From[T, int](2)
    83  	check.Equal(t, "0."+strings.Repeat("9", f128.MaxDecimalDigits[T]()), oneThird.Add(oneThird).Add(oneThird).String())
    84  	check.Equal(t, "0."+strings.Repeat("6", f128.MaxDecimalDigits[T]()-1)+"7", one.Sub(oneThird).String())
    85  	check.Equal(t, "-1."+strings.Repeat("6", f128.MaxDecimalDigits[T]()), negTwoThirds.Sub(one).String())
    86  	check.Equal(t, "0", negTwoThirds.Sub(one).Add(oneAndTwoThirds).String())
    87  	check.Equal(t, f128.From[T, int](10240), f128.From[T, int](1234).Add(nineThousandSix))
    88  	check.Equal(t, "10240", f128.From[T, int](1234).Add(nineThousandSix).String())
    89  	check.Equal(t, "-1.5", f128.From[T, float64](0.5).Sub(two).String())
    90  	ninetyPointZeroSix := f128.FromStringForced[T]("90.06")
    91  	twelvePointThirtyFour := f128.FromStringForced[T]("12.34")
    92  	var answer string
    93  	if f128.MaxDecimalDigits[T]() > 1 {
    94  		answer = "102.4"
    95  	} else {
    96  		answer = "102.3"
    97  	}
    98  	check.Equal(t, f128.FromStringForced[T](answer), twelvePointThirtyFour.Add(ninetyPointZeroSix))
    99  	check.Equal(t, answer, twelvePointThirtyFour.Add(ninetyPointZeroSix).String())
   100  }
   101  
   102  func TestMulDiv(t *testing.T) {
   103  	testMulDiv[fixed.D1](t)
   104  	testMulDiv[fixed.D2](t)
   105  	testMulDiv[fixed.D3](t)
   106  	testMulDiv[fixed.D4](t)
   107  	testMulDiv[fixed.D5](t)
   108  	testMulDiv[fixed.D6](t)
   109  }
   110  
   111  func testMulDiv[T fixed.Dx](t *testing.T) {
   112  	pointThree := f128.FromStringForced[T]("0.3")
   113  	negativePointThree := f128.FromStringForced[T]("-0.3")
   114  	threeFill := strings.Repeat("3", f128.MaxDecimalDigits[T]())
   115  	check.Equal(t, "0."+threeFill, f128.From[T, int](1).Div(f128.From[T, int](3)).String())
   116  	check.Equal(t, "-0."+threeFill, f128.From[T, int](1).Div(f128.From[T, int](-3)).String())
   117  	check.Equal(t, "0.1", pointThree.Div(f128.From[T, int](3)).String())
   118  	check.Equal(t, "0.9", pointThree.Mul(f128.From[T, int](3)).String())
   119  	check.Equal(t, "-0.9", negativePointThree.Mul(f128.From[T, int](3)).String())
   120  }
   121  
   122  func TestMod(t *testing.T) {
   123  	testMod[fixed.D1](t)
   124  	testMod[fixed.D2](t)
   125  	testMod[fixed.D3](t)
   126  	testMod[fixed.D4](t)
   127  	testMod[fixed.D5](t)
   128  	testMod[fixed.D6](t)
   129  }
   130  
   131  func testMod[T fixed.Dx](t *testing.T) {
   132  	check.Equal(t, f128.From[T, int](1), f128.From[T, int](3).Mod(f128.From[T, int](2)))
   133  	check.Equal(t, f128.FromStringForced[T]("0.3"), f128.FromStringForced[T]("9.3").Mod(f128.From[T, int](3)))
   134  	check.Equal(t, f128.FromStringForced[T]("0.1"), f128.FromStringForced[T]("3.1").Mod(f128.FromStringForced[T]("0.2")))
   135  }
   136  
   137  func TestTrunc(t *testing.T) {
   138  	testTrunc[fixed.D1](t)
   139  	testTrunc[fixed.D2](t)
   140  	testTrunc[fixed.D3](t)
   141  	testTrunc[fixed.D4](t)
   142  	testTrunc[fixed.D5](t)
   143  	testTrunc[fixed.D6](t)
   144  }
   145  
   146  func testTrunc[T fixed.Dx](t *testing.T) {
   147  	check.Equal(t, f128.From[T, int](0), f128.FromStringForced[T]("0.3333").Trunc())
   148  	check.Equal(t, f128.From[T, int](2), f128.FromStringForced[T]("2.6789").Trunc())
   149  	check.Equal(t, f128.From[T, int](3), f128.From[T, int](3).Trunc())
   150  	check.Equal(t, f128.From[T, int](0), f128.FromStringForced[T]("-0.3333").Trunc())
   151  	check.Equal(t, f128.From[T, int](-2), f128.FromStringForced[T]("-2.6789").Trunc())
   152  	check.Equal(t, f128.From[T, int](-3), f128.From[T, int](-3).Trunc())
   153  }
   154  
   155  func TestCeil(t *testing.T) {
   156  	testCeil[fixed.D1](t)
   157  	testCeil[fixed.D2](t)
   158  	testCeil[fixed.D3](t)
   159  	testCeil[fixed.D4](t)
   160  	testCeil[fixed.D5](t)
   161  	testCeil[fixed.D6](t)
   162  }
   163  
   164  func testCeil[T fixed.Dx](t *testing.T) {
   165  	check.Equal(t, f128.From[T, int](1), f128.FromStringForced[T]("0.3333").Ceil())
   166  	check.Equal(t, f128.From[T, int](3), f128.FromStringForced[T]("2.6789").Ceil())
   167  	check.Equal(t, f128.From[T, int](3), f128.From[T, int](3).Ceil())
   168  	check.Equal(t, f128.From[T, int](0), f128.FromStringForced[T]("-0.3333").Ceil())
   169  	check.Equal(t, f128.From[T, int](-2), f128.FromStringForced[T]("-2.6789").Ceil())
   170  	check.Equal(t, f128.From[T, int](-3), f128.From[T, int](-3).Ceil())
   171  }
   172  
   173  func TestRound(t *testing.T) {
   174  	testRound[fixed.D1](t)
   175  	testRound[fixed.D2](t)
   176  	testRound[fixed.D3](t)
   177  	testRound[fixed.D4](t)
   178  	testRound[fixed.D5](t)
   179  	testRound[fixed.D6](t)
   180  }
   181  
   182  func testRound[T fixed.Dx](t *testing.T) {
   183  	check.Equal(t, f128.From[T, int](0), f128.FromStringForced[T]("0.3333").Round())
   184  	check.Equal(t, f128.From[T, int](3), f128.FromStringForced[T]("2.6789").Round())
   185  	check.Equal(t, f128.From[T, int](3), f128.From[T, int](3).Round())
   186  	check.Equal(t, f128.From[T, int](0), f128.FromStringForced[T]("-0.3333").Round())
   187  	check.Equal(t, f128.From[T, int](-3), f128.FromStringForced[T]("-2.6789").Round())
   188  	check.Equal(t, f128.From[T, int](-3), f128.From[T, int](-3).Round())
   189  }
   190  
   191  func TestAbs(t *testing.T) {
   192  	testAbs[fixed.D1](t)
   193  	testAbs[fixed.D2](t)
   194  	testAbs[fixed.D3](t)
   195  	testAbs[fixed.D4](t)
   196  	testAbs[fixed.D5](t)
   197  	testAbs[fixed.D6](t)
   198  }
   199  
   200  func testAbs[T fixed.Dx](t *testing.T) {
   201  	check.Equal(t, f128.FromStringForced[T]("0.3333"), f128.FromStringForced[T]("0.3333").Abs())
   202  	check.Equal(t, f128.FromStringForced[T]("2.6789"), f128.FromStringForced[T]("2.6789").Abs())
   203  	check.Equal(t, f128.From[T, int](3), f128.From[T, int](3).Abs())
   204  	check.Equal(t, f128.FromStringForced[T]("0.3333"), f128.FromStringForced[T]("-0.3333").Abs())
   205  	check.Equal(t, f128.FromStringForced[T]("2.6789"), f128.FromStringForced[T]("-2.6789").Abs())
   206  	check.Equal(t, f128.From[T, int](3), f128.From[T, int](-3).Abs())
   207  }
   208  
   209  func TestNeg(t *testing.T) {
   210  	testNeg[fixed.D1](t)
   211  	testNeg[fixed.D2](t)
   212  	testNeg[fixed.D3](t)
   213  	testNeg[fixed.D4](t)
   214  	testNeg[fixed.D5](t)
   215  	testNeg[fixed.D6](t)
   216  }
   217  
   218  func testNeg[T fixed.Dx](t *testing.T) {
   219  	check.Equal(t, f128.FromStringForced[T]("-0.3333"), f128.FromStringForced[T]("0.3333").Neg())
   220  	check.Equal(t, f128.FromStringForced[T]("-2.6789"), f128.FromStringForced[T]("2.6789").Neg())
   221  	check.Equal(t, f128.From[T](-3), f128.From[T](3).Neg())
   222  	check.Equal(t, f128.FromStringForced[T]("0.3333"), f128.FromStringForced[T]("-0.3333").Neg())
   223  	check.Equal(t, f128.FromStringForced[T]("2.6789"), f128.FromStringForced[T]("-2.6789").Neg())
   224  	check.Equal(t, f128.From[T](3), f128.From[T](-3).Neg())
   225  }
   226  
   227  func TestCmp(t *testing.T) {
   228  	testCmp[fixed.D1](t)
   229  	testCmp[fixed.D2](t)
   230  	testCmp[fixed.D3](t)
   231  	testCmp[fixed.D4](t)
   232  	testCmp[fixed.D5](t)
   233  	testCmp[fixed.D6](t)
   234  }
   235  
   236  func testCmp[T fixed.Dx](t *testing.T) {
   237  	check.Equal(t, 1, f128.FromStringForced[T]("0.3333").Cmp(f128.From[T](-3)))
   238  	check.Equal(t, -1, f128.FromStringForced[T]("2.6789").Cmp(f128.From[T](3)))
   239  	check.Equal(t, 0, f128.From[T](3).Cmp(f128.From[T](3)))
   240  }
   241  
   242  func TestEqual(t *testing.T) {
   243  	testEqual[fixed.D1](t)
   244  	testEqual[fixed.D2](t)
   245  	testEqual[fixed.D3](t)
   246  	testEqual[fixed.D4](t)
   247  	testEqual[fixed.D5](t)
   248  	testEqual[fixed.D6](t)
   249  }
   250  
   251  func testEqual[T fixed.Dx](t *testing.T) {
   252  	check.Equal(t, false, f128.FromStringForced[T]("0.3333").Equal(f128.From[T](-3)))
   253  	check.Equal(t, false, f128.FromStringForced[T]("2.6789").Equal(f128.From[T](3)))
   254  	check.Equal(t, true, f128.From[T](3).Equal(f128.From[T](3)))
   255  }
   256  
   257  func TestGreaterThan(t *testing.T) {
   258  	testGreaterThan[fixed.D1](t)
   259  	testGreaterThan[fixed.D2](t)
   260  	testGreaterThan[fixed.D3](t)
   261  	testGreaterThan[fixed.D4](t)
   262  	testGreaterThan[fixed.D5](t)
   263  	testGreaterThan[fixed.D6](t)
   264  }
   265  
   266  func testGreaterThan[T fixed.Dx](t *testing.T) {
   267  	check.Equal(t, true, f128.FromStringForced[T]("0.3333").GreaterThan(f128.From[T](-3)))
   268  	check.Equal(t, false, f128.FromStringForced[T]("2.6789").GreaterThan(f128.From[T](3)))
   269  	check.Equal(t, false, f128.From[T](3).GreaterThan(f128.From[T](3)))
   270  	check.Equal(t, true, f128.From[T](4).GreaterThan(f128.From[T](3)))
   271  	check.Equal(t, true, f128.FromStringForced[T]("2.6789").GreaterThan(f128.From[T](-1)))
   272  }
   273  
   274  func TestGreaterThanOrEqual(t *testing.T) {
   275  	testGreaterThanOrEqual[fixed.D1](t)
   276  	testGreaterThanOrEqual[fixed.D2](t)
   277  	testGreaterThanOrEqual[fixed.D3](t)
   278  	testGreaterThanOrEqual[fixed.D4](t)
   279  	testGreaterThanOrEqual[fixed.D5](t)
   280  	testGreaterThanOrEqual[fixed.D6](t)
   281  }
   282  
   283  func testGreaterThanOrEqual[T fixed.Dx](t *testing.T) {
   284  	check.Equal(t, true, f128.FromStringForced[T]("0.3333").GreaterThanOrEqual(f128.From[T](-3)))
   285  	check.Equal(t, false, f128.FromStringForced[T]("2.6789").GreaterThanOrEqual(f128.From[T](3)))
   286  	check.Equal(t, true, f128.From[T](3).GreaterThanOrEqual(f128.From[T](3)))
   287  	check.Equal(t, true, f128.From[T](4).GreaterThanOrEqual(f128.From[T](3)))
   288  	check.Equal(t, true, f128.FromStringForced[T]("2.6789").GreaterThanOrEqual(f128.From[T](-1)))
   289  }
   290  
   291  func TestLessThan(t *testing.T) {
   292  	testLessThan[fixed.D1](t)
   293  	testLessThan[fixed.D2](t)
   294  	testLessThan[fixed.D3](t)
   295  	testLessThan[fixed.D4](t)
   296  	testLessThan[fixed.D5](t)
   297  	testLessThan[fixed.D6](t)
   298  }
   299  
   300  func testLessThan[T fixed.Dx](t *testing.T) {
   301  	check.Equal(t, false, f128.FromStringForced[T]("0.3333").LessThan(f128.From[T](-3)))
   302  	check.Equal(t, true, f128.FromStringForced[T]("2.6789").LessThan(f128.From[T](3)))
   303  	check.Equal(t, false, f128.From[T](3).LessThan(f128.From[T](3)))
   304  	check.Equal(t, false, f128.From[T](4).LessThan(f128.From[T](3)))
   305  	check.Equal(t, false, f128.FromStringForced[T]("2.6789").LessThan(f128.From[T](-1)))
   306  }
   307  
   308  func TestLessThanOrEqual(t *testing.T) {
   309  	testLessThanOrEqual[fixed.D1](t)
   310  	testLessThanOrEqual[fixed.D2](t)
   311  	testLessThanOrEqual[fixed.D3](t)
   312  	testLessThanOrEqual[fixed.D4](t)
   313  	testLessThanOrEqual[fixed.D5](t)
   314  	testLessThanOrEqual[fixed.D6](t)
   315  }
   316  
   317  func testLessThanOrEqual[T fixed.Dx](t *testing.T) {
   318  	check.Equal(t, false, f128.FromStringForced[T]("0.3333").LessThanOrEqual(f128.From[T](-3)))
   319  	check.Equal(t, true, f128.FromStringForced[T]("2.6789").LessThanOrEqual(f128.From[T](3)))
   320  	check.Equal(t, true, f128.From[T](3).LessThanOrEqual(f128.From[T](3)))
   321  	check.Equal(t, false, f128.From[T](4).LessThanOrEqual(f128.From[T](3)))
   322  	check.Equal(t, false, f128.FromStringForced[T]("2.6789").LessThanOrEqual(f128.From[T](-1)))
   323  }
   324  
   325  func TestComma(t *testing.T) {
   326  	check.Equal(t, "0.12", f128.FromStringForced[fixed.D2]("0.12").Comma())
   327  	check.Equal(t, "1,234,567,890.12", f128.FromStringForced[fixed.D2]("1234567890.12").Comma())
   328  	check.Equal(t, "91,234,567,890.12", f128.FromStringForced[fixed.D2]("91234567890.12").Comma())
   329  	check.Equal(t, "891,234,567,890.12", f128.FromStringForced[fixed.D2]("891234567890.12").Comma())
   330  }
   331  
   332  func TestJSON(t *testing.T) {
   333  	testJSON[fixed.D1](t)
   334  	testJSON[fixed.D2](t)
   335  	testJSON[fixed.D3](t)
   336  	testJSON[fixed.D4](t)
   337  	testJSON[fixed.D5](t)
   338  	testJSON[fixed.D6](t)
   339  }
   340  
   341  func testJSON[T fixed.Dx](t *testing.T) {
   342  	for i := -25000; i < 25001; i += 13 {
   343  		testJSONActual(t, f128.From[T](i))
   344  	}
   345  	testJSONActual(t, f128.From[T, int64](1844674407371259000))
   346  }
   347  
   348  type embedded[T fixed.Dx] struct {
   349  	Field f128.Int[T]
   350  }
   351  
   352  func testJSONActual[T fixed.Dx](t *testing.T, v f128.Int[T]) {
   353  	t.Helper()
   354  	e1 := embedded[T]{Field: v}
   355  	data, err := json.Marshal(&e1)
   356  	check.NoError(t, err)
   357  	var e2 embedded[T]
   358  	err = json.Unmarshal(data, &e2)
   359  	check.NoError(t, err)
   360  	check.Equal(t, e1, e2)
   361  }
   362  
   363  func TestYAML(t *testing.T) {
   364  	testYAML[fixed.D1](t)
   365  	testYAML[fixed.D2](t)
   366  	testYAML[fixed.D3](t)
   367  	testYAML[fixed.D4](t)
   368  	testYAML[fixed.D5](t)
   369  	testYAML[fixed.D6](t)
   370  }
   371  
   372  func testYAML[T fixed.Dx](t *testing.T) {
   373  	for i := -25000; i < 25001; i += 13 {
   374  		testYAMLActual(t, f128.From[T](i))
   375  	}
   376  	testYAMLActual(t, f128.From[T, int64](1844674407371259000))
   377  }
   378  
   379  func testYAMLActual[T fixed.Dx](t *testing.T, v f128.Int[T]) {
   380  	t.Helper()
   381  	e1 := embedded[T]{Field: v}
   382  	data, err := yaml.Marshal(&e1)
   383  	check.NoError(t, err)
   384  	var e2 embedded[T]
   385  	err = yaml.Unmarshal(data, &e2)
   386  	check.NoError(t, err)
   387  	check.Equal(t, e1, e2)
   388  }