github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/mysql/decimal_test.go (about) 1 // The MIT License (MIT) 2 3 // Copyright (c) 2015 Spring, Inc. 4 5 // Permission is hereby granted, free of charge, to any person obtaining a copy 6 // of this software and associated documentation files (the "Software"), to deal 7 // in the Software without restriction, including without limitation the rights 8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 // copies of the Software, and to permit persons to whom the Software is 10 // furnished to do so, subject to the following conditions: 11 12 // The above copyright notice and this permission notice shall be included in 13 // all copies or substantial portions of the Software. 14 15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 // THE SOFTWARE. 22 23 // - Based on https://yougam/libraries/oguzbilgic/fpd, which has the following license: 24 // """ 25 // The MIT License (MIT) 26 27 // Copyright (c) 2013 Oguz Bilgic 28 29 // Permission is hereby granted, free of charge, to any person obtaining a copy of 30 // this software and associated documentation files (the "Software"), to deal in 31 // the Software without restriction, including without limitation the rights to 32 // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 33 // the Software, and to permit persons to whom the Software is furnished to do so, 34 // subject to the following conditions: 35 36 // The above copyright notice and this permission notice shall be included in all 37 // copies or substantial portions of the Software. 38 39 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 40 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 41 // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 42 // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 43 // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 44 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 45 // """ 46 47 // Copyright 2015 PingCAP, Inc. 48 // 49 // Licensed under the Apache License, Version 2.0 (the "License"); 50 // you may not use this file except in compliance with the License. 51 // You may obtain a copy of the License at 52 // 53 // http://www.apache.org/licenses/LICENSE-2.0 54 // 55 // Unless required by applicable law or agreed to in writing, software 56 // distributed under the License is distributed on an "AS IS" BASIS, 57 // See the License for the specific language governing permissions and 58 // limitations under the License. 59 60 package mysql 61 62 import ( 63 "encoding/json" 64 "encoding/xml" 65 "math" 66 "strings" 67 "testing" 68 ) 69 70 var testTable = map[float64]string{ 71 3.141592653589793: "3.141592653589793", 72 3: "3", 73 1234567890123456: "1234567890123456", 74 1234567890123456000: "1234567890123456000", 75 1234.567890123456: "1234.567890123456", 76 .1234567890123456: "0.1234567890123456", 77 0: "0", 78 .1111111111111110: "0.111111111111111", 79 .1111111111111111: "0.1111111111111111", 80 .1111111111111119: "0.1111111111111119", 81 .000000000000000001: "0.000000000000000001", 82 .000000000000000002: "0.000000000000000002", 83 .000000000000000003: "0.000000000000000003", 84 .000000000000000005: "0.000000000000000005", 85 .000000000000000008: "0.000000000000000008", 86 .1000000000000001: "0.1000000000000001", 87 .1000000000000002: "0.1000000000000002", 88 .1000000000000003: "0.1000000000000003", 89 .1000000000000005: "0.1000000000000005", 90 .1000000000000008: "0.1000000000000008", 91 } 92 93 func init() { 94 // add negatives 95 for f, s := range testTable { 96 if f > 0 { 97 testTable[-f] = "-" + s 98 } 99 } 100 } 101 102 func TestNewFromFloat(t *testing.T) { 103 for f, s := range testTable { 104 d := NewDecimalFromFloat(f) 105 if d.String() != s { 106 t.Errorf("expected %s, got %s (%s, %d)", 107 s, d.String(), 108 d.value.String(), d.exp) 109 } 110 } 111 112 shouldPanicOn := []float64{ 113 math.NaN(), 114 math.Inf(1), 115 math.Inf(-1), 116 } 117 118 for _, n := range shouldPanicOn { 119 var d Decimal 120 if !didPanic(func() { d = NewDecimalFromFloat(n) }) { 121 t.Fatalf("Expected panic when creating a Decimal from %v, got %v instead", n, d.String()) 122 } 123 } 124 } 125 126 func TestNewFromString(t *testing.T) { 127 for _, s := range testTable { 128 d, err := ParseDecimal(s) 129 if err != nil { 130 t.Errorf("error while parsing %s", s) 131 } else if d.String() != s { 132 t.Errorf("expected %s, got %s (%s, %d)", 133 s, d.String(), 134 d.value.String(), d.exp) 135 } 136 } 137 } 138 139 func TestNewFromStringErrs(t *testing.T) { 140 tests := []string{ 141 "", 142 "qwert", 143 "-", 144 ".", 145 "-.", 146 ".-", 147 "234-.56", 148 "234-56", 149 "2-", 150 "..", 151 "2..", 152 "..2", 153 ".5.2", 154 "8..2", 155 "8.1.", 156 } 157 158 for _, s := range tests { 159 _, err := ParseDecimal(s) 160 161 if err == nil { 162 t.Errorf("error expected when parsing %s", s) 163 } 164 } 165 } 166 167 func TestNewFromFloatWithExponent(t *testing.T) { 168 type Inp struct { 169 float float64 170 exp int32 171 } 172 tests := map[Inp]string{ 173 Inp{123.4, -3}: "123.400", 174 Inp{123.4, -1}: "123.4", 175 Inp{123.412345, 1}: "120", 176 Inp{123.412345, 0}: "123", 177 Inp{123.412345, -5}: "123.41235", 178 Inp{123.412345, -6}: "123.412345", 179 Inp{123.412345, -7}: "123.4123450", 180 } 181 182 // add negatives 183 for p, s := range tests { 184 if p.float > 0 { 185 tests[Inp{-p.float, p.exp}] = "-" + s 186 } 187 } 188 189 for input, s := range tests { 190 d := NewDecimalFromFloatWithExponent(input.float, input.exp) 191 if d.String() != s { 192 t.Errorf("expected %s, got %s (%s, %d)", 193 s, d.String(), 194 d.value.String(), d.exp) 195 } 196 } 197 198 shouldPanicOn := []float64{ 199 math.NaN(), 200 math.Inf(1), 201 math.Inf(-1), 202 } 203 204 for _, n := range shouldPanicOn { 205 var d Decimal 206 if !didPanic(func() { d = NewDecimalFromFloatWithExponent(n, 0) }) { 207 t.Fatalf("Expected panic when creating a Decimal from %v, got %v instead", n, d.String()) 208 } 209 } 210 } 211 212 func TestJSON(t *testing.T) { 213 for _, s := range testTable { 214 var doc struct { 215 Amount Decimal `json:"amount"` 216 } 217 docStr := `{"amount":"` + s + `"}` 218 err := json.Unmarshal([]byte(docStr), &doc) 219 if err != nil { 220 t.Errorf("error unmarshaling %s: %v", docStr, err) 221 } else if doc.Amount.String() != s { 222 t.Errorf("expected %s, got %s (%s, %d)", 223 s, doc.Amount.String(), 224 doc.Amount.value.String(), doc.Amount.exp) 225 } 226 227 out, err := json.Marshal(&doc) 228 if err != nil { 229 t.Errorf("error marshaling %+v: %v", doc, err) 230 } else if string(out) != docStr { 231 t.Errorf("expected %s, got %s", docStr, string(out)) 232 } 233 } 234 } 235 236 func TestBadJSON(t *testing.T) { 237 for _, testCase := range []string{ 238 "]o_o[", 239 "{", 240 `{"amount":""`, 241 `{"amount":""}`, 242 `{"amount":"nope"}`, 243 `0.333`, 244 } { 245 var doc struct { 246 Amount Decimal `json:"amount"` 247 } 248 err := json.Unmarshal([]byte(testCase), &doc) 249 if err == nil { 250 t.Errorf("expected error, got %+v", doc) 251 } 252 } 253 } 254 255 func TestXML(t *testing.T) { 256 for _, s := range testTable { 257 var doc struct { 258 XMLName xml.Name `xml:"account"` 259 Amount Decimal `xml:"amount"` 260 } 261 docStr := `<account><amount>` + s + `</amount></account>` 262 err := xml.Unmarshal([]byte(docStr), &doc) 263 if err != nil { 264 t.Errorf("error unmarshaling %s: %v", docStr, err) 265 } else if doc.Amount.String() != s { 266 t.Errorf("expected %s, got %s (%s, %d)", 267 s, doc.Amount.String(), 268 doc.Amount.value.String(), doc.Amount.exp) 269 } 270 271 out, err := xml.Marshal(&doc) 272 if err != nil { 273 t.Errorf("error marshaling %+v: %v", doc, err) 274 } else if string(out) != docStr { 275 t.Errorf("expected %s, got %s", docStr, string(out)) 276 } 277 } 278 } 279 280 func TestBadXML(t *testing.T) { 281 for _, testCase := range []string{ 282 "o_o", 283 "<abc", 284 "<account><amount>7", 285 `<html><body></body></html>`, 286 `<account><amount></amount></account>`, 287 `<account><amount>nope</amount></account>`, 288 `0.333`, 289 } { 290 var doc struct { 291 XMLName xml.Name `xml:"account"` 292 Amount Decimal `xml:"amount"` 293 } 294 err := xml.Unmarshal([]byte(testCase), &doc) 295 if err == nil { 296 t.Errorf("expected error, got %+v", doc) 297 } 298 } 299 } 300 301 func TestDecimal_rescale(t *testing.T) { 302 type Inp struct { 303 int int64 304 exp int32 305 rescale int32 306 } 307 tests := map[Inp]string{ 308 Inp{1234, -3, -5}: "1.234", 309 Inp{1234, -3, 0}: "1.000", 310 Inp{1234, 3, 0}: "1234000", 311 Inp{1234, -4, -4}: "0.1234", 312 } 313 314 // add negatives 315 for p, s := range tests { 316 if p.int > 0 { 317 tests[Inp{-p.int, p.exp, p.rescale}] = "-" + s 318 } 319 } 320 321 for input, s := range tests { 322 d := NewDecimalFromInt(input.int, input.exp).rescale(input.rescale) 323 324 if d.String() != s { 325 t.Errorf("expected %s, got %s (%s, %d)", 326 s, d.String(), 327 d.value.String(), d.exp) 328 } 329 330 // test StringScaled 331 s2 := NewDecimalFromInt(input.int, input.exp).StringScaled(input.rescale) 332 if s2 != s { 333 t.Errorf("expected %s, got %s", s, s2) 334 } 335 } 336 } 337 338 func TestDecimal_Floor(t *testing.T) { 339 type testData struct { 340 input string 341 expected string 342 } 343 tests := []testData{ 344 {"1.999", "1"}, 345 {"1", "1"}, 346 {"1.01", "1"}, 347 {"0", "0"}, 348 {"0.9", "0"}, 349 {"0.1", "0"}, 350 {"-0.9", "-1"}, 351 {"-0.1", "-1"}, 352 {"-1.00", "-1"}, 353 {"-1.01", "-2"}, 354 {"-1.999", "-2"}, 355 } 356 for _, test := range tests { 357 d, _ := ParseDecimal(test.input) 358 expected, _ := ParseDecimal(test.expected) 359 got := d.Floor() 360 if !got.Equals(expected) { 361 t.Errorf("Floor(%s): got %s, expected %s", d, got, expected) 362 } 363 } 364 } 365 366 func TestDecimal_Ceil(t *testing.T) { 367 type testData struct { 368 input string 369 expected string 370 } 371 tests := []testData{ 372 {"1.999", "2"}, 373 {"1", "1"}, 374 {"1.01", "2"}, 375 {"0", "0"}, 376 {"0.9", "1"}, 377 {"0.1", "1"}, 378 {"-0.9", "0"}, 379 {"-0.1", "0"}, 380 {"-1.00", "-1"}, 381 {"-1.01", "-1"}, 382 {"-1.999", "-1"}, 383 } 384 for _, test := range tests { 385 d, _ := ParseDecimal(test.input) 386 expected, _ := ParseDecimal(test.expected) 387 got := d.Ceil() 388 if !got.Equals(expected) { 389 t.Errorf("Ceil(%s): got %s, expected %s", d, got, expected) 390 } 391 } 392 } 393 394 func TestDecimal_RoundAndStringFixed(t *testing.T) { 395 type testData struct { 396 input string 397 places int32 398 expected string 399 expectedFixed string 400 } 401 tests := []testData{ 402 {"1.454", 0, "1", ""}, 403 {"1.454", 1, "1.5", ""}, 404 {"1.454", 2, "1.45", ""}, 405 {"1.454", 3, "1.454", ""}, 406 {"1.454", 4, "1.454", "1.4540"}, 407 {"1.454", 5, "1.454", "1.45400"}, 408 {"1.554", 0, "2", ""}, 409 {"1.554", 1, "1.6", ""}, 410 {"1.554", 2, "1.55", ""}, 411 {"0.554", 0, "1", ""}, 412 {"0.454", 0, "0", ""}, 413 {"0.454", 5, "0.454", "0.45400"}, 414 {"0", 0, "0", ""}, 415 {"0", 1, "0", "0.0"}, 416 {"0", 2, "0", "0.00"}, 417 {"0", -1, "0", ""}, 418 {"5", 2, "5", "5.00"}, 419 {"5", 1, "5", "5.0"}, 420 {"5", 0, "5", ""}, 421 {"500", 2, "500", "500.00"}, 422 {"545", -1, "550", ""}, 423 {"545", -2, "500", ""}, 424 {"545", -3, "1000", ""}, 425 {"545", -4, "0", ""}, 426 {"499", -3, "0", ""}, 427 {"499", -4, "0", ""}, 428 } 429 430 // add negative number tests 431 for _, test := range tests { 432 expected := test.expected 433 if expected != "0" { 434 expected = "-" + expected 435 } 436 expectedStr := test.expectedFixed 437 if strings.ContainsAny(expectedStr, "123456789") && expectedStr != "" { 438 expectedStr = "-" + expectedStr 439 } 440 tests = append(tests, 441 testData{"-" + test.input, test.places, expected, expectedStr}) 442 } 443 444 for _, test := range tests { 445 d, err := ParseDecimal(test.input) 446 if err != nil { 447 panic(err) 448 } 449 450 // test Round 451 expected, err := ParseDecimal(test.expected) 452 if err != nil { 453 panic(err) 454 } 455 got := d.Round(test.places) 456 if !got.Equals(expected) { 457 t.Errorf("Rounding %s to %d places, got %s, expected %s", 458 d, test.places, got, expected) 459 } 460 // test StringFixed 461 if test.expectedFixed == "" { 462 test.expectedFixed = test.expected 463 } 464 gotStr := d.StringFixed(test.places) 465 if gotStr != test.expectedFixed { 466 t.Errorf("(%s).StringFixed(%d): got %s, expected %s", 467 d, test.places, gotStr, test.expectedFixed) 468 } 469 } 470 } 471 472 func TestDecimal_Uninitialized(t *testing.T) { 473 a := Decimal{} 474 b := Decimal{} 475 476 decs := []Decimal{ 477 a, 478 a.rescale(10), 479 a.Abs(), 480 a.Add(b), 481 a.Sub(b), 482 a.Mul(b), 483 a.Floor(), 484 a.Ceil(), 485 } 486 487 for _, d := range decs { 488 if d.String() != "0" { 489 t.Errorf("expected 0, got %s", d.String()) 490 } 491 if d.StringFixed(3) != "0.000" { 492 t.Errorf("expected 0, got %s", d.StringFixed(3)) 493 } 494 if d.StringScaled(-2) != "0" { 495 t.Errorf("expected 0, got %s", d.StringScaled(-2)) 496 } 497 } 498 499 if a.Cmp(b) != 0 { 500 t.Errorf("a != b") 501 } 502 if a.Exponent() != 0 { 503 t.Errorf("a.Exponent() != 0") 504 } 505 if a.IntPart() != 0 { 506 t.Errorf("a.IntPar() != 0") 507 } 508 f, _ := a.Float64() 509 if f != 0 { 510 t.Errorf("a.Float64() != 0") 511 } 512 if a.Rat().RatString() != "0" { 513 t.Errorf("a.Rat() != 0, got %s", a.Rat().RatString()) 514 } 515 } 516 517 func TestDecimal_Add(t *testing.T) { 518 type Inp struct { 519 a string 520 b string 521 } 522 523 inputs := map[Inp]string{ 524 Inp{"2", "3"}: "5", 525 Inp{"2454495034", "3451204593"}: "5905699627", 526 Inp{"24544.95034", ".3451204593"}: "24545.2954604593", 527 Inp{".1", ".1"}: "0.2", 528 Inp{".1", "-.1"}: "0.0", 529 Inp{"0", "1.001"}: "1.001", 530 } 531 532 for inp, res := range inputs { 533 a, err := ParseDecimal(inp.a) 534 if err != nil { 535 t.FailNow() 536 } 537 b, err := ParseDecimal(inp.b) 538 if err != nil { 539 t.FailNow() 540 } 541 c := a.Add(b) 542 if c.String() != res { 543 t.Errorf("expected %s, got %s", res, c.String()) 544 } 545 } 546 } 547 548 func TestDecimal_Sub(t *testing.T) { 549 type Inp struct { 550 a string 551 b string 552 } 553 554 inputs := map[Inp]string{ 555 Inp{"2", "3"}: "-1", 556 Inp{"12", "3"}: "9", 557 Inp{"-2", "9"}: "-11", 558 Inp{"2454495034", "3451204593"}: "-996709559", 559 Inp{"24544.95034", ".3451204593"}: "24544.6052195407", 560 Inp{".1", "-.1"}: "0.2", 561 Inp{".1", ".1"}: "0.0", 562 Inp{"0", "1.001"}: "-1.001", 563 Inp{"1.001", "0"}: "1.001", 564 Inp{"2.3", ".3"}: "2.0", 565 } 566 567 for inp, res := range inputs { 568 a, err := ParseDecimal(inp.a) 569 if err != nil { 570 t.FailNow() 571 } 572 b, err := ParseDecimal(inp.b) 573 if err != nil { 574 t.FailNow() 575 } 576 c := a.Sub(b) 577 if c.String() != res { 578 t.Errorf("expected %s, got %s", res, c.String()) 579 } 580 } 581 } 582 583 func TestDecimal_Mul(t *testing.T) { 584 type Inp struct { 585 a string 586 b string 587 } 588 589 inputs := map[Inp]string{ 590 Inp{"2", "3"}: "6", 591 Inp{"2454495034", "3451204593"}: "8470964534836491162", 592 Inp{"24544.95034", ".3451204593"}: "8470.964534836491162", 593 Inp{".1", ".1"}: "0.01", 594 Inp{"0", "1.001"}: "0.000", 595 } 596 597 for inp, res := range inputs { 598 a, err := ParseDecimal(inp.a) 599 if err != nil { 600 t.FailNow() 601 } 602 b, err := ParseDecimal(inp.b) 603 if err != nil { 604 t.FailNow() 605 } 606 c := a.Mul(b) 607 if c.String() != res { 608 t.Errorf("expected %s, got %s", res, c.String()) 609 } 610 } 611 612 // positive scale 613 c := NewDecimalFromInt(1234, 5).Mul(NewDecimalFromInt(45, -1)) 614 if c.String() != "555300000.0" { 615 t.Errorf("Expected %s, got %s", "555300000.0", c.String()) 616 } 617 } 618 619 func TestDecimal_Div(t *testing.T) { 620 type Inp struct { 621 a string 622 b string 623 } 624 625 inputs := map[Inp]string{ 626 Inp{"6", "3"}: "2.0000", 627 Inp{"10", "2"}: "5.0000", 628 Inp{"2.2", "1.1"}: "2.00000", 629 Inp{"-2.2", "-1.1"}: "2.00000", 630 Inp{"12.88", "5.6"}: "2.300000", 631 Inp{"1023427554493", "43432632"}: "23563.5629", // rounded 632 Inp{"1", "434324545566634"}: "0.0000", 633 Inp{"1", "3"}: "0.3333", 634 Inp{"2", "3"}: "0.6667", // rounded 635 Inp{"10000", "3"}: "3333.3333", 636 Inp{"10234274355545544493", "-3"}: "-3411424785181848164.3333", 637 Inp{"-4612301402398.4753343454", "23.5"}: "-196268144782.91384401469787", 638 } 639 640 for inp, expected := range inputs { 641 num, err := ParseDecimal(inp.a) 642 if err != nil { 643 t.FailNow() 644 } 645 denom, err := ParseDecimal(inp.b) 646 if err != nil { 647 t.FailNow() 648 } 649 got := num.Div(denom) 650 if got.String() != expected { 651 t.Errorf("expected %s when dividing %v by %v, got %v", 652 expected, num, denom, got) 653 } 654 } 655 656 type Inp2 struct { 657 n int64 658 exp int32 659 n2 int64 660 exp2 int32 661 } 662 663 // test code path where exp > 0 664 inputs2 := map[Inp2]string{ 665 Inp2{124, 10, 3, 1}: "41333333333.3333", 666 Inp2{124, 10, 3, 0}: "413333333333.3333", 667 Inp2{124, 10, 6, 1}: "20666666666.6667", 668 Inp2{124, 10, 6, 0}: "206666666666.6667", 669 Inp2{10, 10, 10, 1}: "1000000000.0000", 670 } 671 672 for inp, expectedAbs := range inputs2 { 673 for i := -1; i <= 1; i += 2 { 674 for j := -1; j <= 1; j += 2 { 675 n := inp.n * int64(i) 676 n2 := inp.n2 * int64(j) 677 num := NewDecimalFromInt(n, inp.exp) 678 denom := NewDecimalFromInt(n2, inp.exp2) 679 expected := expectedAbs 680 if i != j { 681 expected = "-" + expectedAbs 682 } 683 got := num.Div(denom) 684 if got.String() != expected { 685 t.Errorf("expected %s when dividing %v by %v, got %v", 686 expected, num, denom, got) 687 } 688 } 689 } 690 } 691 } 692 693 func TestDecimal_Overflow(t *testing.T) { 694 if !didPanic(func() { NewDecimalFromInt(1, math.MinInt32).Mul(NewDecimalFromInt(1, math.MinInt32)) }) { 695 t.Fatalf("should have gotten an overflow panic") 696 } 697 if !didPanic(func() { NewDecimalFromInt(1, math.MaxInt32).Mul(NewDecimalFromInt(1, math.MaxInt32)) }) { 698 t.Fatalf("should have gotten an overflow panic") 699 } 700 } 701 702 func TestIntPart(t *testing.T) { 703 for _, testCase := range []struct { 704 Dec string 705 IntPart int64 706 }{ 707 {"0.01", 0}, 708 {"12.1", 12}, 709 {"9999.999", 9999}, 710 {"-32768.01234", -32768}, 711 } { 712 d, err := ParseDecimal(testCase.Dec) 713 if err != nil { 714 t.Fatal(err) 715 } 716 if d.IntPart() != testCase.IntPart { 717 t.Errorf("expect %d, got %d", testCase.IntPart, d.IntPart()) 718 } 719 } 720 } 721 722 // old tests after this line 723 724 func TestDecimal_Scale(t *testing.T) { 725 a := NewDecimalFromInt(1234, -3) 726 if a.Exponent() != -3 { 727 t.Errorf("error") 728 } 729 } 730 731 func TestDecimal_Abs1(t *testing.T) { 732 a := NewDecimalFromInt(-1234, -4) 733 b := NewDecimalFromInt(1234, -4) 734 735 c := a.Abs() 736 if c.Cmp(b) != 0 { 737 t.Errorf("error") 738 } 739 } 740 741 func TestDecimal_Abs2(t *testing.T) { 742 a := NewDecimalFromInt(-1234, -4) 743 b := NewDecimalFromInt(1234, -4) 744 745 c := b.Abs() 746 if c.Cmp(a) == 0 { 747 t.Errorf("error") 748 } 749 } 750 751 func TestDecimal_Equal(t *testing.T) { 752 a := NewDecimalFromInt(1234, 3) 753 b := NewDecimalFromInt(1234, 3) 754 755 if !a.Equals(b) { 756 t.Errorf("%q should equal %q", a, b) 757 } 758 } 759 760 func TestDecimal_ScalesNotEqual(t *testing.T) { 761 a := NewDecimalFromInt(1234, 2) 762 b := NewDecimalFromInt(1234, 3) 763 if a.Equals(b) { 764 t.Errorf("%q should not equal %q", a, b) 765 } 766 } 767 768 func TestDecimal_Cmp1(t *testing.T) { 769 a := NewDecimalFromInt(123, 3) 770 b := NewDecimalFromInt(-1234, 2) 771 772 if a.Cmp(b) != 1 { 773 t.Errorf("Error") 774 } 775 } 776 777 func TestDecimal_Cmp2(t *testing.T) { 778 a := NewDecimalFromInt(123, 3) 779 b := NewDecimalFromInt(1234, 2) 780 781 if a.Cmp(b) != -1 { 782 t.Errorf("Error") 783 } 784 } 785 786 func TestDecimal_Cmp3(t *testing.T) { 787 a := NewDecimalFromInt(2, 0) 788 b := NewDecimalFromInt(3, 0) 789 if n := a.Div(b).Mul(b).Cmp(a); n != -1 { 790 t.Errorf("Error %d", n) 791 } 792 } 793 794 func TestDecimalConvert(t *testing.T) { 795 a, err := ConvertToDecimal(1235.3) 796 if err != nil || a.String() != "1235.3" { 797 t.Error("Error") 798 } 799 a, err = ConvertToDecimal(1235) 800 if err != nil || a.String() != "1235" { 801 t.Error("Error") 802 } 803 a, err = ConvertToDecimal("-234.23") 804 if err != nil || a.String() != "-234.23" { 805 t.Error("Error") 806 } 807 a, err = ConvertToDecimal(NewDecimalFromFloat(23.45)) 808 if err != nil || a.String() != "23.45" { 809 t.Error("Error") 810 } 811 } 812 813 func TestDecimalComplex(t *testing.T) { 814 result := NewDecimalFromFloat(3).Div(NewDecimalFromFloat(5)).Mul(NewDecimalFromFloat(15)).String() 815 if result != "9.0000" { 816 t.Errorf("got %s", result) 817 } 818 result = NewDecimalFromFloat(3.553).Mul(NewDecimalFromFloat(2.34)).Div(NewDecimalFromFloat(24.8)).String() 819 if result != "0.335242742" { 820 t.Errorf("got %s", result) 821 } 822 result = NewDecimalFromFloat(4.67).Div(NewDecimalFromFloat(32.099)).Div(NewDecimalFromFloat(9.32)).String() 823 if result != "0.0156102359" { 824 t.Errorf("got %s", result) 825 } 826 a, _ := ParseDecimal("23.342983749572929843520960646") 827 b, _ := ParseDecimal("23.234122323452345412351235351") 828 result = a.Mul(b).String() 829 if result != "542.353739831937742603572001528416" { 830 t.Errorf("got %s", result) 831 } 832 } 833 834 func TestDecimalRoundTruncateFragDigits(t *testing.T) { 835 a, err := ConvertToDecimal("12.3456") 836 if err != nil || a.fracDigits != 4 { 837 t.Error("Error") 838 } 839 a = a.Truncate(3) 840 if a.fracDigits != 3 { 841 t.Error("Error") 842 } 843 if a.String() != "12.345" { 844 t.Error("Error") 845 } 846 a = a.Round(2) 847 if a.fracDigits != 2 { 848 t.Error("Error") 849 } 850 if a.String() != "12.35" { 851 t.Error("Error") 852 } 853 } 854 855 func didPanic(f func()) bool { 856 ret := false 857 func() { 858 859 defer func() { 860 if message := recover(); message != nil { 861 ret = true 862 } 863 }() 864 865 // call the target function 866 f() 867 868 }() 869 870 return ret 871 } 872 873 func TestDecimalScientificNotation(t *testing.T) { 874 tbl := []struct { 875 Input string 876 Expected float64 877 }{ 878 {"314e-2", 3.14}, 879 {"1e2", 100}, 880 {"2E-1", 0.2}, 881 {"2E0", 2}, 882 {"2.2E-1", 0.22}, 883 } 884 885 for _, c := range tbl { 886 n, err := ParseDecimal(c.Input) 887 if err != nil { 888 t.Error(err) 889 } 890 891 f, _ := n.Float64() 892 if f != c.Expected { 893 t.Errorf("%f != %f", f, c.Expected) 894 } 895 } 896 897 tblErr := []string{ 898 "12ee", 899 "ae10", 900 "12e1a", 901 "12e1.2", 902 "e1", 903 } 904 905 for _, c := range tblErr { 906 _, err := ParseDecimal(c) 907 if err == nil { 908 t.Errorf("%s must be invalid decimal", c) 909 } 910 } 911 }