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 }