github.com/richardwilkes/toolbox@v1.121.0/xmath/fixed/f64/f64_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 f64_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/f64"
    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", f64.From[T, float64](0.1).String())
    35  	check.Equal(t, "0.2", f64.From[T, float64](0.2).String())
    36  	check.Equal(t, "0.3", f64.FromStringForced[T]("0.3").String())
    37  	check.Equal(t, "-0.1", f64.From[T, float64](-0.1).String())
    38  	check.Equal(t, "-0.2", f64.From[T, float64](-0.2).String())
    39  	check.Equal(t, "-0.3", f64.FromStringForced[T]("-0.3").String())
    40  	threeFill := strings.Repeat("3", f64.MaxDecimalDigits[T]())
    41  	check.Equal(t, "0."+threeFill, f64.FromStringForced[T]("0.33333333").String())
    42  	check.Equal(t, "-0."+threeFill, f64.FromStringForced[T]("-0.33333333").String())
    43  	sixFill := strings.Repeat("6", f64.MaxDecimalDigits[T]())
    44  	check.Equal(t, "0."+sixFill, f64.FromStringForced[T]("0.66666666").String())
    45  	check.Equal(t, "-0."+sixFill, f64.FromStringForced[T]("-0.66666666").String())
    46  	check.Equal(t, "1", f64.From[T, float64](1.0000004).String())
    47  	check.Equal(t, "1", f64.From[T, float64](1.00000049).String())
    48  	check.Equal(t, "1", f64.From[T, float64](1.0000005).String())
    49  	check.Equal(t, "1", f64.From[T, float64](1.0000009).String())
    50  	check.Equal(t, "-1", f64.From[T, float64](-1.0000004).String())
    51  	check.Equal(t, "-1", f64.From[T, float64](-1.00000049).String())
    52  	check.Equal(t, "-1", f64.From[T, float64](-1.0000005).String())
    53  	check.Equal(t, "-1", f64.From[T, float64](-1.0000009).String())
    54  	zeroFill := strings.Repeat("0", f64.MaxDecimalDigits[T]()-1)
    55  	check.Equal(t, "0."+zeroFill+"4", f64.FromStringForced[T]("0."+zeroFill+"405").String())
    56  	check.Equal(t, "-0."+zeroFill+"4", f64.FromStringForced[T]("-0."+zeroFill+"405").String())
    57  
    58  	v, err := f64.FromString[T]("33.0")
    59  	check.NoError(t, err)
    60  	check.Equal(t, v, f64.From[T, int](33))
    61  
    62  	v, err = f64.FromString[T]("33.00000000000000000000")
    63  	check.NoError(t, err)
    64  	check.Equal(t, v, f64.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 := f64.FromStringForced[T]("0.333333")
    78  	negTwoThirds := f64.FromStringForced[T]("-0.666666")
    79  	one := f64.From[T, int](1)
    80  	oneAndTwoThirds := f64.FromStringForced[T]("1.666666")
    81  	nineThousandSix := f64.From[T, int](9006)
    82  	two := f64.From[T, int](2)
    83  	check.Equal(t, "0."+strings.Repeat("9", f64.MaxDecimalDigits[T]()), (oneThird + oneThird + oneThird).String())
    84  	check.Equal(t, "0."+strings.Repeat("6", f64.MaxDecimalDigits[T]()-1)+"7", (one - oneThird).String())
    85  	check.Equal(t, "-1."+strings.Repeat("6", f64.MaxDecimalDigits[T]()), (negTwoThirds - one).String())
    86  	check.Equal(t, "0", (negTwoThirds - one + oneAndTwoThirds).String())
    87  	check.Equal(t, f64.From[T, int](10240), f64.From[T, int](1234)+nineThousandSix)
    88  	check.Equal(t, "10240", (f64.From[T, int](1234) + nineThousandSix).String())
    89  	check.Equal(t, "-1.5", (f64.From[T, float64](0.5) - two).String())
    90  	ninetyPointZeroSix := f64.FromStringForced[T]("90.06")
    91  	twelvePointThirtyFour := f64.FromStringForced[T]("12.34")
    92  	var answer string
    93  	if f64.MaxDecimalDigits[T]() > 1 {
    94  		answer = "102.4"
    95  	} else {
    96  		answer = "102.3"
    97  	}
    98  	check.Equal(t, f64.FromStringForced[T](answer), twelvePointThirtyFour+ninetyPointZeroSix)
    99  	check.Equal(t, answer, (twelvePointThirtyFour + 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 := f64.FromStringForced[T]("0.3")
   113  	negativePointThree := f64.FromStringForced[T]("-0.3")
   114  	threeFill := strings.Repeat("3", f64.MaxDecimalDigits[T]())
   115  	check.Equal(t, "0."+threeFill, f64.From[T, int](1).Div(f64.From[T, int](3)).String())
   116  	check.Equal(t, "-0."+threeFill, f64.From[T, int](1).Div(f64.From[T, int](-3)).String())
   117  	check.Equal(t, "0.1", pointThree.Div(f64.From[T, int](3)).String())
   118  	check.Equal(t, "0.9", pointThree.Mul(f64.From[T, int](3)).String())
   119  	check.Equal(t, "-0.9", negativePointThree.Mul(f64.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, f64.From[T, int](1), f64.From[T, int](3).Mod(f64.From[T, int](2)))
   133  	check.Equal(t, f64.FromStringForced[T]("0.3"), f64.FromStringForced[T]("9.3").Mod(f64.From[T, int](3)))
   134  	check.Equal(t, f64.FromStringForced[T]("0.1"), f64.FromStringForced[T]("3.1").Mod(f64.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, f64.From[T, int](0), f64.FromStringForced[T]("0.3333").Trunc())
   148  	check.Equal(t, f64.From[T, int](2), f64.FromStringForced[T]("2.6789").Trunc())
   149  	check.Equal(t, f64.From[T, int](3), f64.From[T, int](3).Trunc())
   150  	check.Equal(t, f64.From[T, int](0), f64.FromStringForced[T]("-0.3333").Trunc())
   151  	check.Equal(t, f64.From[T, int](-2), f64.FromStringForced[T]("-2.6789").Trunc())
   152  	check.Equal(t, f64.From[T, int](-3), f64.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, f64.From[T, int](1), f64.FromStringForced[T]("0.3333").Ceil())
   166  	check.Equal(t, f64.From[T, int](3), f64.FromStringForced[T]("2.6789").Ceil())
   167  	check.Equal(t, f64.From[T, int](3), f64.From[T, int](3).Ceil())
   168  	check.Equal(t, f64.From[T, int](0), f64.FromStringForced[T]("-0.3333").Ceil())
   169  	check.Equal(t, f64.From[T, int](-2), f64.FromStringForced[T]("-2.6789").Ceil())
   170  	check.Equal(t, f64.From[T, int](-3), f64.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, f64.From[T, int](0), f64.FromStringForced[T]("0.3333").Round())
   184  	check.Equal(t, f64.From[T, int](3), f64.FromStringForced[T]("2.6789").Round())
   185  	check.Equal(t, f64.From[T, int](3), f64.From[T, int](3).Round())
   186  	check.Equal(t, f64.From[T, int](0), f64.FromStringForced[T]("-0.3333").Round())
   187  	check.Equal(t, f64.From[T, int](-3), f64.FromStringForced[T]("-2.6789").Round())
   188  	check.Equal(t, f64.From[T, int](-3), f64.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, f64.FromStringForced[T]("0.3333"), f64.FromStringForced[T]("0.3333").Abs())
   202  	check.Equal(t, f64.FromStringForced[T]("2.6789"), f64.FromStringForced[T]("2.6789").Abs())
   203  	check.Equal(t, f64.From[T, int](3), f64.From[T, int](3).Abs())
   204  	check.Equal(t, f64.FromStringForced[T]("0.3333"), f64.FromStringForced[T]("-0.3333").Abs())
   205  	check.Equal(t, f64.FromStringForced[T]("2.6789"), f64.FromStringForced[T]("-2.6789").Abs())
   206  	check.Equal(t, f64.From[T, int](3), f64.From[T, int](-3).Abs())
   207  }
   208  
   209  func TestComma(t *testing.T) {
   210  	check.Equal(t, "0.12", f64.FromStringForced[fixed.D2]("0.12").Comma())
   211  	check.Equal(t, "1,234,567,890.12", f64.FromStringForced[fixed.D2]("1234567890.12").Comma())
   212  	check.Equal(t, "91,234,567,890.12", f64.FromStringForced[fixed.D2]("91234567890.12").Comma())
   213  	check.Equal(t, "891,234,567,890.12", f64.FromStringForced[fixed.D2]("891234567890.12").Comma())
   214  }
   215  
   216  func TestJSON(t *testing.T) {
   217  	testJSON[fixed.D1](t)
   218  	testJSON[fixed.D2](t)
   219  	testJSON[fixed.D3](t)
   220  	testJSON[fixed.D4](t)
   221  	testJSON[fixed.D5](t)
   222  	testJSON[fixed.D6](t)
   223  }
   224  
   225  func testJSON[T fixed.Dx](t *testing.T) {
   226  	for i := -25000; i < 25001; i += 13 {
   227  		testJSONActual(t, f64.From[T](i))
   228  	}
   229  	testJSONActual(t, f64.From[T, int64](1844674407371259000))
   230  }
   231  
   232  type embedded[T fixed.Dx] struct {
   233  	Field f64.Int[T]
   234  }
   235  
   236  func testJSONActual[T fixed.Dx](t *testing.T, v f64.Int[T]) {
   237  	t.Helper()
   238  	e1 := embedded[T]{Field: v}
   239  	data, err := json.Marshal(&e1)
   240  	check.NoError(t, err)
   241  	var e2 embedded[T]
   242  	err = json.Unmarshal(data, &e2)
   243  	check.NoError(t, err)
   244  	check.Equal(t, e1, e2)
   245  }
   246  
   247  func TestYAML(t *testing.T) {
   248  	testYAML[fixed.D1](t)
   249  	testYAML[fixed.D2](t)
   250  	testYAML[fixed.D3](t)
   251  	testYAML[fixed.D4](t)
   252  	testYAML[fixed.D5](t)
   253  	testYAML[fixed.D6](t)
   254  }
   255  
   256  func testYAML[T fixed.Dx](t *testing.T) {
   257  	for i := -25000; i < 25001; i += 13 {
   258  		testYAMLActual(t, f64.From[T](i))
   259  	}
   260  	testYAMLActual(t, f64.From[T, int64](1844674407371259000))
   261  }
   262  
   263  func testYAMLActual[T fixed.Dx](t *testing.T, v f64.Int[T]) {
   264  	t.Helper()
   265  	e1 := embedded[T]{Field: v}
   266  	data, err := yaml.Marshal(&e1)
   267  	check.NoError(t, err)
   268  	var e2 embedded[T]
   269  	err = yaml.Unmarshal(data, &e2)
   270  	check.NoError(t, err)
   271  	check.Equal(t, e1, e2)
   272  }