1 package big
2 3 import (
4 "math"
5 "testing"
6 )
7 8 // The decimal expansion of 1.7976931348623157e308: 17 significant digits then
9 // 292 zeros, a 1027-bit (17-word) numerator. It is the value the relay audit
10 // recorded as broken under stage4 in both directions - big.Int could not print
11 // a multi-word decimal, and big.Rat's decimal parse died on the next String().
12 const maxFloat64Decimal = "179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
13 14 func pow10(k int32) (s []byte) {
15 s = []byte{:k + 1}
16 s[0] = '1'
17 for i := int32(1); i <= k; i++ {
18 s[i] = '0'
19 }
20 return
21 }
22 23 // TestIntDecimalRoundTrip walks the base-10 leaf and the recursive
24 // divisors/convertWords path (values above leafSize words) and the power-of-two
25 // branch, in both directions.
26 func TestIntDecimalRoundTrip(t *testing.T) {
27 cases := []string{
28 "0",
29 "1",
30 "123456789012345678901234567890",
31 "18446744073709551615",
32 "18446744073709551616",
33 maxFloat64Decimal,
34 }
35 for i := int32(0); i < len(cases); i++ {
36 want := []byte(cases[i])
37 var x Int
38 if _, ok := x.SetString(want, 10); !ok {
39 t.Fatalf("SetString(%s, 10) failed", want)
40 }
41 if got := x.String(); got != want {
42 t.Fatalf("SetString(%s).String() = %s", want, got)
43 }
44 hex := x.Text(16)
45 var y Int
46 if _, ok := y.SetString(hex, 16); !ok {
47 t.Fatalf("SetString(%s, 16) failed", hex)
48 }
49 if got := y.String(); got != want {
50 t.Fatalf("hex round trip of %s = %s", want, got)
51 }
52 }
53 }
54 55 // TestIntExpReusesAccumulator squares 10^k through expNN for k past 20, where
56 // the accumulator leaves one word. basicMul accumulates into z and relies on
57 // its opening clear(z[0:len(x)+len(y)]); when stage4 dropped clear, every
58 // reused accumulator kept its previous product's digits and Exp(10,20) came
59 // out as 10^20+10^5.
60 func TestIntExpReusesAccumulator(t *testing.T) {
61 for k := int32(1); k <= 70; k++ {
62 var want Int
63 want.SetString(pow10(k), 10)
64 var got Int
65 got.Exp(NewInt(10), NewInt(int64(k)), nil)
66 if got.Cmp(&want) != 0 {
67 t.Fatalf("Exp(10, %d) = %s, want %s", k, got.String(), want.String())
68 }
69 }
70 }
71 72 // TestRatLongDecimal is the audit's Rat repro: SetString of a long decimal
73 // exponent succeeds, and the next String() formats a 309-digit numerator.
74 func TestRatLongDecimal(t *testing.T) {
75 var r Rat
76 if _, ok := r.SetString([]byte("1.7976931348623157e308")); !ok {
77 t.Fatal("SetString(1.7976931348623157e308) failed")
78 }
79 if !r.IsInt() {
80 t.Fatal("1.7976931348623157e308 must be an integer")
81 }
82 if got := r.RatString(); got != []byte(maxFloat64Decimal) {
83 t.Fatalf("RatString() = %s", got)
84 }
85 var back Rat
86 if _, ok := back.SetString(r.String()); !ok {
87 t.Fatal("re-parsing Rat.String() failed")
88 }
89 if back.Cmp(&r) != 0 {
90 t.Fatal("Rat.String() did not round trip")
91 }
92 }
93 94 // TestRatSmallestSubnormal is the exact value of math.SmallestNonzeroFloat64:
95 // 1/2^1074, whose decimal denominator needs a divisor above the 17-word table
96 // (a second-level recursive conversion). The divisor table used to be cached in
97 // a package global while the nats it holds are arena allocations, so the
98 // metadata outlived the words: the next conversion divided by a divisor whose
99 // top word had been reused, `reciprocalWord(0)` was reached, and bits.Div32
100 // divided by zero.
101 func TestRatSmallestSubnormal(t *testing.T) {
102 one := NewInt(1)
103 den := NewInt(2)
104 den.Exp(den, NewInt(1074), nil)
105 var r Rat
106 r.SetFrac(one, den)
107 got := string(r.RatString())
108 if len(got) != 326 {
109 t.Fatalf("RatString(1/2^1074) is %d bytes, want 326", len(got))
110 }
111 if got[0] != '1' || got[1] != '/' {
112 t.Fatalf("RatString(1/2^1074) = %s", got)
113 }
114 if f, _ := r.Float64(); f != math.SmallestNonzeroFloat64 {
115 t.Fatalf("1/2^1074 = %g, want SmallestNonzeroFloat64", f)
116 }
117 }
118