github.com/cockroachdb/apd/v3@v3.2.0/decimal_test.go (about) 1 // Copyright 2016 The Cockroach Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 // implied. See the License for the specific language governing 13 // permissions and limitations under the License. 14 15 package apd 16 17 import ( 18 "encoding/json" 19 "fmt" 20 "math" 21 "math/bits" 22 "testing" 23 "unsafe" 24 ) 25 26 var ( 27 testCtx = &BaseContext 28 ) 29 30 func (d *Decimal) GoString() string { 31 return fmt.Sprintf(`{Coeff: %s, Exponent: %d, Negative: %v, Form: %s}`, d.Coeff.String(), d.Exponent, d.Negative, d.Form) 32 } 33 34 // testExponentError skips t if err was caused by an exponent being outside 35 // of the package's supported exponent range. Since the exponent is so large, 36 // we don't support those tests yet (i.e., it's an expected failure, so we 37 // skip it). 38 func testExponentError(t *testing.T, err error) { 39 if err == nil { 40 return 41 } 42 if err.Error() == errExponentOutOfRangeStr { 43 t.Skip(err) 44 } 45 } 46 47 func newDecimal(t *testing.T, c *Context, s string) *Decimal { 48 d, _, err := c.NewFromString(s) 49 testExponentError(t, err) 50 if err != nil { 51 t.Fatalf("%s: %+v", s, err) 52 } 53 return d 54 } 55 56 func TestNewWithBigInt(t *testing.T) { 57 tests := []string{ 58 "0", 59 "1", 60 "-1", 61 } 62 for _, tc := range tests { 63 t.Run(tc, func(t *testing.T) { 64 expect, _, err := new(Decimal).SetString(tc) 65 if err != nil { 66 t.Fatal(err) 67 } 68 b, ok := new(BigInt).SetString(tc, 10) 69 if !ok { 70 t.Fatal("bad bigint") 71 } 72 d := NewWithBigInt(b, 0) 73 if d.Coeff.Sign() < 0 { 74 t.Fatal("unexpected negative coeff") 75 } 76 // Verify that changing b doesn't change d. 77 b.Set(NewBigInt(1234)) 78 if d.CmpTotal(expect) != 0 { 79 t.Fatalf("expected %s, got %s", expect, d) 80 } 81 }) 82 } 83 } 84 85 func TestUpscale(t *testing.T) { 86 tests := []struct { 87 x, y *Decimal 88 a, b *BigInt 89 s int32 90 }{ 91 {x: New(1, 0), y: New(100, -1), a: NewBigInt(10), b: NewBigInt(100), s: -1}, 92 {x: New(1, 0), y: New(10, -1), a: NewBigInt(10), b: NewBigInt(10), s: -1}, 93 {x: New(1, 0), y: New(10, 0), a: NewBigInt(1), b: NewBigInt(10), s: 0}, 94 {x: New(1, 1), y: New(1, 0), a: NewBigInt(10), b: NewBigInt(1), s: 0}, 95 {x: New(10, -2), y: New(1, -1), a: NewBigInt(10), b: NewBigInt(10), s: -2}, 96 {x: New(1, -2), y: New(100, 1), a: NewBigInt(1), b: NewBigInt(100000), s: -2}, 97 } 98 for _, tc := range tests { 99 t.Run(fmt.Sprintf("%s, %s", tc.x, tc.y), func(t *testing.T) { 100 a, b, s, err := upscale(tc.x, tc.y, new(BigInt)) 101 if err != nil { 102 t.Fatal(err) 103 } 104 if a.Cmp(tc.a) != 0 { 105 t.Errorf("a: expected %s, got %s", tc.a, a) 106 } 107 if b.Cmp(tc.b) != 0 { 108 t.Errorf("b: expected %s, got %s", tc.b, b) 109 } 110 if s != tc.s { 111 t.Errorf("s: expected %d, got %d", tc.s, s) 112 } 113 }) 114 } 115 } 116 117 func TestAdd(t *testing.T) { 118 tests := []struct { 119 x, y string 120 r string 121 }{ 122 {x: "1", y: "10", r: "11"}, 123 {x: "1", y: "1e1", r: "11"}, 124 {x: "1e1", y: "1", r: "11"}, 125 {x: ".1e1", y: "100e-1", r: "11.0"}, 126 } 127 for _, tc := range tests { 128 t.Run(fmt.Sprintf("%s, %s", tc.x, tc.y), func(t *testing.T) { 129 x := newDecimal(t, testCtx, tc.x) 130 y := newDecimal(t, testCtx, tc.y) 131 d := new(Decimal) 132 _, err := testCtx.Add(d, x, y) 133 if err != nil { 134 t.Fatal(err) 135 } 136 s := d.String() 137 if s != tc.r { 138 t.Fatalf("expected: %s, got: %s", tc.r, s) 139 } 140 }) 141 } 142 } 143 144 func TestCmp(t *testing.T) { 145 tests := []struct { 146 x, y string 147 c int 148 }{ 149 {x: "1", y: "10", c: -1}, 150 {x: "1", y: "1e1", c: -1}, 151 {x: "1e1", y: "1", c: 1}, 152 {x: ".1e1", y: "100e-1", c: -1}, 153 154 {x: ".1e1", y: "100e-2", c: 0}, 155 {x: "1", y: ".1e1", c: 0}, 156 {x: "1", y: "1", c: 0}, 157 } 158 for _, tc := range tests { 159 t.Run(fmt.Sprintf("%s, %s", tc.x, tc.y), func(t *testing.T) { 160 x := newDecimal(t, testCtx, tc.x) 161 y := newDecimal(t, testCtx, tc.y) 162 c := x.Cmp(y) 163 if c != tc.c { 164 t.Fatalf("expected: %d, got: %d", tc.c, c) 165 } 166 }) 167 } 168 } 169 170 func TestModf(t *testing.T) { 171 tests := []struct { 172 x string 173 i string 174 f string 175 }{ 176 {x: "1", i: "1", f: "0"}, 177 {x: "1.0", i: "1", f: "0.0"}, 178 {x: "1.0e1", i: "10", f: "0"}, 179 {x: "1.0e2", i: "1.0E+2", f: "0"}, 180 {x: "1.0e-1", i: "0", f: "0.10"}, 181 {x: "1.0e-2", i: "0", f: "0.010"}, 182 {x: "1.1", i: "1", f: "0.1"}, 183 {x: "1234.56", i: "1234", f: "0.56"}, 184 {x: "1234.56e2", i: "123456", f: "0"}, 185 {x: "1234.56e4", i: "1.23456E+7", f: "0"}, 186 {x: "1234.56e-2", i: "12", f: "0.3456"}, 187 {x: "1234.56e-4", i: "0", f: "0.123456"}, 188 {x: "1234.56e-6", i: "0", f: "0.00123456"}, 189 {x: "123456e-8", i: "0", f: "0.00123456"}, 190 {x: ".123456e8", i: "1.23456E+7", f: "0"}, 191 192 {x: "-1", i: "-1", f: "-0"}, 193 {x: "-1.0", i: "-1", f: "-0.0"}, 194 {x: "-1.0e1", i: "-10", f: "-0"}, 195 {x: "-1.0e2", i: "-1.0E+2", f: "-0"}, 196 {x: "-1.0e-1", i: "-0", f: "-0.10"}, 197 {x: "-1.0e-2", i: "-0", f: "-0.010"}, 198 {x: "-1.1", i: "-1", f: "-0.1"}, 199 {x: "-1234.56", i: "-1234", f: "-0.56"}, 200 {x: "-1234.56e2", i: "-123456", f: "-0"}, 201 {x: "-1234.56e4", i: "-1.23456E+7", f: "-0"}, 202 {x: "-1234.56e-2", i: "-12", f: "-0.3456"}, 203 {x: "-1234.56e-4", i: "-0", f: "-0.123456"}, 204 {x: "-1234.56e-6", i: "-0", f: "-0.00123456"}, 205 {x: "-123456e-8", i: "-0", f: "-0.00123456"}, 206 {x: "-.123456e8", i: "-1.23456E+7", f: "-0"}, 207 } 208 for _, tc := range tests { 209 t.Run(tc.x, func(t *testing.T) { 210 x := newDecimal(t, testCtx, tc.x) 211 integ, frac := new(Decimal), new(Decimal) 212 x.Modf(integ, frac) 213 if tc.i != integ.String() { 214 t.Fatalf("integ: expected: %s, got: %s", tc.i, integ) 215 } 216 if tc.f != frac.String() { 217 t.Fatalf("frac: expected: %s, got: %s", tc.f, frac) 218 } 219 a := new(Decimal) 220 if _, err := testCtx.Add(a, integ, frac); err != nil { 221 t.Fatal(err) 222 } 223 if a.Cmp(x) != 0 { 224 t.Fatalf("%s != %s", a, x) 225 } 226 if integ.Exponent < 0 { 227 t.Fatal(integ.Exponent) 228 } 229 if frac.Exponent > 0 { 230 t.Fatal(frac.Exponent) 231 } 232 233 integ2, frac2 := new(Decimal), new(Decimal) 234 x.Modf(integ2, nil) 235 x.Modf(nil, frac2) 236 if integ.CmpTotal(integ2) != 0 { 237 t.Fatalf("got %s, expected %s", integ2, integ) 238 } 239 if frac.CmpTotal(frac2) != 0 { 240 t.Fatalf("got %s, expected %s", frac2, frac) 241 } 242 }) 243 } 244 245 // Ensure we don't panic on both nil. 246 a := new(Decimal) 247 a.Modf(nil, nil) 248 } 249 250 func TestInt64(t *testing.T) { 251 tests := []struct { 252 x string 253 i int64 254 err bool 255 }{ 256 {x: "0.12e1", err: true}, 257 {x: "0.1e1", i: 1}, 258 {x: "10", i: 10}, 259 {x: "12.3e3", i: 12300}, 260 {x: "1e-1", err: true}, 261 {x: "1e2", i: 100}, 262 {x: "1", i: 1}, 263 {x: "NaN", err: true}, 264 {x: "Inf", err: true}, 265 {x: "9223372036854775807", i: 9223372036854775807}, 266 {x: "-9223372036854775808", i: -9223372036854775808}, 267 {x: "9223372036854775808", err: true}, 268 } 269 for _, tc := range tests { 270 t.Run(tc.x, func(t *testing.T) { 271 x := newDecimal(t, testCtx, tc.x) 272 i, err := x.Int64() 273 hasErr := err != nil 274 if tc.err != hasErr { 275 t.Fatalf("expected error: %v, got error: %v", tc.err, err) 276 } 277 if hasErr { 278 return 279 } 280 if i != tc.i { 281 t.Fatalf("expected: %v, got %v", tc.i, i) 282 } 283 }) 284 } 285 } 286 287 func TestQuoErr(t *testing.T) { 288 tests := []struct { 289 x, y string 290 p uint32 291 err string 292 }{ 293 {x: "1", y: "1", p: 0, err: errZeroPrecisionStr}, 294 {x: "1", y: "0", p: 1, err: "division by zero"}, 295 } 296 for _, tc := range tests { 297 c := testCtx.WithPrecision(tc.p) 298 x := newDecimal(t, testCtx, tc.x) 299 y := newDecimal(t, testCtx, tc.y) 300 d := new(Decimal) 301 _, err := c.Quo(d, x, y) 302 if err == nil { 303 t.Fatal("expected error") 304 } 305 if err.Error() != tc.err { 306 t.Fatalf("expected %s, got %s", tc.err, err) 307 } 308 } 309 } 310 311 func TestConditionString(t *testing.T) { 312 tests := map[Condition]string{ 313 Overflow: "overflow", 314 Overflow | Underflow: "overflow, underflow", 315 Subnormal | Inexact: "inexact, subnormal", 316 } 317 for c, s := range tests { 318 t.Run(s, func(t *testing.T) { 319 cs := c.String() 320 if cs != s { 321 t.Errorf("expected %s; got %s", s, cs) 322 } 323 }) 324 } 325 } 326 327 func TestFloat64(t *testing.T) { 328 tests := []float64{ 329 0, 330 1, 331 -1, 332 math.MaxFloat32, 333 math.SmallestNonzeroFloat32, 334 math.MaxFloat64, 335 math.SmallestNonzeroFloat64, 336 } 337 338 for _, tc := range tests { 339 t.Run(fmt.Sprint(tc), func(t *testing.T) { 340 d := new(Decimal) 341 d.SetFloat64(tc) 342 f, err := d.Float64() 343 if err != nil { 344 t.Fatal(err) 345 } 346 if tc != f { 347 t.Fatalf("expected %v, got %v", tc, f) 348 } 349 }) 350 } 351 } 352 353 func TestCeil(t *testing.T) { 354 tests := map[float64]int64{ 355 0: 0, 356 -0.1: 0, 357 0.1: 1, 358 -0.9: 0, 359 0.9: 1, 360 -1: -1, 361 1: 1, 362 -1.1: -1, 363 1.1: 2, 364 } 365 366 for f, r := range tests { 367 t.Run(fmt.Sprint(f), func(t *testing.T) { 368 d, err := new(Decimal).SetFloat64(f) 369 if err != nil { 370 t.Fatal(err) 371 } 372 _, err = testCtx.Ceil(d, d) 373 if err != nil { 374 t.Fatal(err) 375 } 376 i, err := d.Int64() 377 if err != nil { 378 t.Fatal(err) 379 } 380 if i != r { 381 t.Fatalf("got %v, expected %v", i, r) 382 } 383 }) 384 } 385 } 386 387 func TestFloor(t *testing.T) { 388 tests := map[float64]int64{ 389 0: 0, 390 -0.1: -1, 391 0.1: 0, 392 -0.9: -1, 393 0.9: 0, 394 -1: -1, 395 1: 1, 396 -1.1: -2, 397 1.1: 1, 398 } 399 400 for f, r := range tests { 401 t.Run(fmt.Sprint(f), func(t *testing.T) { 402 d, err := new(Decimal).SetFloat64(f) 403 if err != nil { 404 t.Fatal(err) 405 } 406 _, err = testCtx.Floor(d, d) 407 if err != nil { 408 t.Fatal(err) 409 } 410 i, err := d.Int64() 411 if err != nil { 412 t.Fatal(err) 413 } 414 if i != r { 415 t.Fatalf("got %v, expected %v", i, r) 416 } 417 }) 418 } 419 } 420 421 func TestFormat(t *testing.T) { 422 tests := map[string]struct { 423 e, E, f, g, G string 424 }{ 425 "NaN": {}, 426 "Infinity": {}, 427 "-Infinity": {}, 428 "sNaN": {}, 429 "0": { 430 e: "0e+0", 431 E: "0E+0", 432 }, 433 "-0": { 434 e: "-0e+0", 435 E: "-0E+0", 436 }, 437 "0.0": { 438 e: "0e-1", 439 E: "0E-1", 440 }, 441 "-0.0": { 442 e: "-0e-1", 443 E: "-0E-1", 444 }, 445 "0E+2": { 446 e: "0e+2", 447 f: "000", 448 g: "0e+2", 449 }, 450 "0E-9": { 451 e: "0e-9", 452 f: "0.000000000", 453 g: "0.000000000", 454 G: "0.000000000", 455 }, 456 } 457 verbs := []string{"%e", "%E", "%f", "%g", "%G"} 458 459 for input, tc := range tests { 460 t.Run(input, func(t *testing.T) { 461 d, _, err := NewFromString(input) 462 if err != nil { 463 t.Fatal(err) 464 } 465 for i, s := range []string{tc.e, tc.E, tc.f, tc.g, tc.G} { 466 if s == "" { 467 s = input 468 } 469 v := verbs[i] 470 t.Run(v, func(t *testing.T) { 471 out := fmt.Sprintf(v, d) 472 if out != s { 473 t.Fatalf("expected %s, got %s", s, out) 474 } 475 }) 476 } 477 }) 478 } 479 } 480 481 func TestFormatFlags(t *testing.T) { 482 const stdD = "1.23E+56" 483 tests := []struct { 484 d string 485 fmt string 486 out string 487 }{ 488 { 489 d: stdD, 490 fmt: "%3G", 491 out: "1.23E+56", 492 }, 493 { 494 d: stdD, 495 fmt: "%010G", 496 out: "001.23E+56", 497 }, 498 { 499 d: stdD, 500 fmt: "%10G", 501 out: " 1.23E+56", 502 }, 503 { 504 d: stdD, 505 fmt: "%+G", 506 out: "+1.23E+56", 507 }, 508 { 509 d: stdD, 510 fmt: "% G", 511 out: " 1.23E+56", 512 }, 513 { 514 d: stdD, 515 fmt: "%-10G", 516 out: "1.23E+56 ", 517 }, 518 { 519 d: stdD, 520 fmt: "%-010G", 521 out: "1.23E+56 ", 522 }, 523 { 524 d: "nan", 525 fmt: "%-10G", 526 out: "NaN ", 527 }, 528 { 529 d: "nan", 530 fmt: "%10G", 531 out: " NaN", 532 }, 533 { 534 d: "nan", 535 fmt: "%010G", 536 out: " NaN", 537 }, 538 { 539 d: "inf", 540 fmt: "%-10G", 541 out: "Infinity ", 542 }, 543 { 544 d: "inf", 545 fmt: "%10G", 546 out: " Infinity", 547 }, 548 { 549 d: "inf", 550 fmt: "%010G", 551 out: " Infinity", 552 }, 553 { 554 d: "-inf", 555 fmt: "%-10G", 556 out: "-Infinity ", 557 }, 558 { 559 d: "-inf", 560 fmt: "%10G", 561 out: " -Infinity", 562 }, 563 { 564 d: "-inf", 565 fmt: "%010G", 566 out: " -Infinity", 567 }, 568 { 569 d: "0", 570 fmt: "%d", 571 out: "%!d(*apd.Decimal=0)", 572 }, 573 } 574 for _, tc := range tests { 575 t.Run(fmt.Sprintf("%s: %s", tc.d, tc.fmt), func(t *testing.T) { 576 d := newDecimal(t, &BaseContext, tc.d) 577 s := fmt.Sprintf(tc.fmt, d) 578 if s != tc.out { 579 t.Fatalf("expected %q, got %q", tc.out, s) 580 } 581 }) 582 } 583 } 584 585 func TestContextSetStringt(t *testing.T) { 586 tests := []struct { 587 s string 588 c *Context 589 expect string 590 }{ 591 { 592 s: "1.234", 593 c: &BaseContext, 594 expect: "1.234", 595 }, 596 { 597 s: "1.234", 598 c: BaseContext.WithPrecision(2), 599 expect: "1.2", 600 }, 601 } 602 for i, tc := range tests { 603 t.Run(fmt.Sprintf("%d: %s", i, tc.s), func(t *testing.T) { 604 d := new(Decimal) 605 if _, _, err := tc.c.SetString(d, tc.s); err != nil { 606 t.Fatal(err) 607 } 608 got := d.String() 609 if got != tc.expect { 610 t.Fatalf("expected: %s, got: %s", tc.expect, got) 611 } 612 }) 613 } 614 } 615 616 func TestQuantize(t *testing.T) { 617 tests := []struct { 618 s string 619 e int32 620 expect string 621 }{ 622 { 623 s: "1.00", 624 e: -1, 625 expect: "1.0", 626 }, 627 { 628 s: "2.0", 629 e: -1, 630 expect: "2.0", 631 }, 632 { 633 s: "3", 634 e: -1, 635 expect: "3.0", 636 }, 637 { 638 s: "9.9999", 639 e: -2, 640 expect: "10.00", 641 }, 642 } 643 c := BaseContext.WithPrecision(10) 644 for _, tc := range tests { 645 t.Run(fmt.Sprintf("%s: %d", tc.s, tc.e), func(t *testing.T) { 646 d, _, err := NewFromString(tc.s) 647 if err != nil { 648 t.Fatal(err) 649 } 650 if _, err := c.Quantize(d, d, tc.e); err != nil { 651 t.Fatal(err) 652 } 653 s := d.String() 654 if s != tc.expect { 655 t.Fatalf("expected: %s, got: %s", tc.expect, s) 656 } 657 }) 658 } 659 } 660 661 func TestCmpOrder(t *testing.T) { 662 tests := []struct { 663 s string 664 order int 665 }{ 666 {s: "-NaN", order: -4}, 667 {s: "-sNaN", order: -3}, 668 {s: "-Infinity", order: -2}, 669 {s: "-127", order: -1}, 670 {s: "-1.00", order: -1}, 671 {s: "-1", order: -1}, 672 {s: "-0.000", order: -1}, 673 {s: "-0", order: -1}, 674 {s: "0", order: 1}, 675 {s: "1.2300", order: 1}, 676 {s: "1.23", order: 1}, 677 {s: "1E+9", order: 1}, 678 {s: "Infinity", order: 2}, 679 {s: "sNaN", order: 3}, 680 {s: "NaN", order: 4}, 681 } 682 683 for _, tc := range tests { 684 t.Run(tc.s, func(t *testing.T) { 685 d, _, err := NewFromString(tc.s) 686 if err != nil { 687 t.Fatal(err) 688 } 689 o := d.cmpOrder() 690 if o != tc.order { 691 t.Fatalf("got %d, expected %d", o, tc.order) 692 } 693 }) 694 } 695 } 696 697 func TestIsZero(t *testing.T) { 698 tests := []struct { 699 s string 700 zero bool 701 }{ 702 {s: "-NaN", zero: false}, 703 {s: "-sNaN", zero: false}, 704 {s: "-Infinity", zero: false}, 705 {s: "-127", zero: false}, 706 {s: "-1.00", zero: false}, 707 {s: "-1", zero: false}, 708 {s: "-0.000", zero: true}, 709 {s: "-0", zero: true}, 710 {s: "0", zero: true}, 711 {s: "1.2300", zero: false}, 712 {s: "1.23", zero: false}, 713 {s: "1E+9", zero: false}, 714 {s: "Infinity", zero: false}, 715 {s: "sNaN", zero: false}, 716 {s: "NaN", zero: false}, 717 } 718 719 for _, tc := range tests { 720 t.Run(tc.s, func(t *testing.T) { 721 d, _, err := NewFromString(tc.s) 722 if err != nil { 723 t.Fatal(err) 724 } 725 z := d.IsZero() 726 if z != tc.zero { 727 t.Fatalf("got %v, expected %v", z, tc.zero) 728 } 729 }) 730 } 731 } 732 733 func TestNeg(t *testing.T) { 734 tests := map[string]string{ 735 "0": "0", 736 "-0": "0", 737 "-0.000": "0.000", 738 "-00.000100": "0.000100", 739 } 740 741 for tc, expect := range tests { 742 t.Run(tc, func(t *testing.T) { 743 d, _, err := NewFromString(tc) 744 if err != nil { 745 t.Fatal(err) 746 } 747 var z Decimal 748 z.Neg(d) 749 s := z.String() 750 if s != expect { 751 t.Fatalf("expected %s, got %s", expect, s) 752 } 753 }) 754 } 755 } 756 757 func TestReduce(t *testing.T) { 758 tests := map[string]int{ 759 "-0": 0, 760 "0": 0, 761 "0.0": 0, 762 "00": 0, 763 "0.00": 0, 764 "-01000": 3, 765 "01000": 3, 766 "-1": 0, 767 "1": 0, 768 "-10.000E4": 4, 769 "10.000E4": 4, 770 "-10.00": 3, 771 "10.00": 3, 772 "-10": 1, 773 "10": 1, 774 "-143200000000000000000000000000000000000000000000000000000000": 56, 775 "143200000000000000000000000000000000000000000000000000000000": 56, 776 "Inf": 0, 777 "NaN": 0, 778 } 779 780 for s, n := range tests { 781 t.Run(s, func(t *testing.T) { 782 d, _, err := NewFromString(s) 783 if err != nil { 784 t.Fatal(err) 785 } 786 _, got := d.Reduce(d) 787 if n != got { 788 t.Fatalf("got %v, expected %v", got, n) 789 } 790 }) 791 } 792 } 793 794 // TestSizeof is meant to catch changes that unexpectedly increase 795 // the size of the BigInt, Decimal, and Context structs. 796 func TestSizeof(t *testing.T) { 797 // map[uint_size][type]sizeof 798 exp := map[int]map[string]uintptr{ 799 32: { 800 "BigInt": 20, 801 "Decimal": 28, 802 "Context": 24, 803 }, 804 64: { 805 "BigInt": 24, 806 "Decimal": 32, 807 "Context": 32, 808 }, 809 }[bits.UintSize] 810 811 var b BigInt 812 if s := unsafe.Sizeof(b); s != exp["BigInt"] { 813 t.Errorf("sizeof(BigInt) changed: %d", s) 814 } 815 var d Decimal 816 if s := unsafe.Sizeof(d); s != exp["Decimal"] { 817 t.Errorf("sizeof(Decimal) changed: %d", s) 818 } 819 var c Context 820 if s := unsafe.Sizeof(c); s != exp["Context"] { 821 t.Errorf("sizeof(Context) changed: %d", s) 822 } 823 } 824 825 // TestSize tests the Size method on BigInt and Decimal. Unlike Sizeof, which 826 // returns the shallow size of the structs, the Size method reports the total 827 // memory footprint of each struct and all referenced objects. 828 func TestSize(t *testing.T) { 829 // map[uint_size][is_inline][type]size 830 exp := map[int]map[bool]map[string]uintptr{ 831 32: { 832 true: { 833 "BigInt": 20, 834 "Decimal": 28, 835 }, 836 false: { 837 "BigInt": 72, 838 "Decimal": 80, 839 }, 840 }, 841 64: { 842 true: { 843 "BigInt": 24, 844 "Decimal": 32, 845 }, 846 false: { 847 "BigInt": 112, 848 "Decimal": 120, 849 }, 850 }, 851 }[bits.UintSize] 852 853 var d Decimal 854 if e, s := exp[true]["Decimal"], d.Size(); e != s { 855 t.Errorf("(*Decimal).Size() != %d: %d", e, s) 856 } 857 if e, s := exp[true]["BigInt"], d.Coeff.Size(); e != s { 858 t.Errorf("(*BigInt).Size() != %d: %d", e, s) 859 } 860 // Set to an inlinable value. 861 d.SetInt64(1234) 862 if e, s := exp[true]["Decimal"], d.Size(); e != s { 863 t.Errorf("(*Decimal).Size() != %d: %d", e, s) 864 } 865 if e, s := exp[true]["BigInt"], d.Coeff.Size(); e != s { 866 t.Errorf("(*BigInt).Size() != %d: %d", e, s) 867 } 868 // Set to a non-inlinable value. 869 if _, _, err := d.SetString("123456789123456789123456789.123456789123456789"); err != nil { 870 t.Fatal(err) 871 } 872 if d.Coeff.isInline() { 873 // Sanity-check, in case inlineWords changes. 874 t.Fatal("BigInt inlined large value. Did inlineWords change?") 875 } 876 if e, s := exp[false]["Decimal"], d.Size(); e != s { 877 t.Errorf("(*Decimal).Size() != %d: %d", e, s) 878 } 879 if e, s := exp[false]["BigInt"], d.Coeff.Size(); e != s { 880 t.Errorf("(*BigInt).Size() != %d: %d", e, s) 881 } 882 } 883 884 func TestJSONEncoding(t *testing.T) { 885 var encodingTests = []string{ 886 "0", 887 "1", 888 "2", 889 "10", 890 "1000", 891 "1234567890", 892 "298472983472983471903246121093472394872319615612417471234712061", 893 "0.0", 894 "NaN", 895 "Inf", 896 "123.456", 897 "1E1", 898 "1E-1", 899 "1.2E3", 900 } 901 902 for _, test := range encodingTests { 903 for _, sign := range []string{"", "+", "-"} { 904 x := sign + test 905 var tx Decimal 906 tx.SetString(x) 907 b, err := json.Marshal(&tx) 908 if err != nil { 909 t.Errorf("marshaling of %s failed: %s", &tx, err) 910 continue 911 } 912 var rx Decimal 913 if err := json.Unmarshal(b, &rx); err != nil { 914 t.Errorf("unmarshaling of %s failed: %s", &tx, err) 915 continue 916 } 917 if rx.CmpTotal(&tx) != 0 { 918 t.Errorf("JSON encoding of %s failed: got %s want %s", &tx, &rx, &tx) 919 } 920 } 921 } 922 }