github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/util/encoding/decimal_test.go (about) 1 // Copyright 2016 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package encoding 12 13 import ( 14 "bytes" 15 "fmt" 16 "math" 17 "math/rand" 18 "testing" 19 20 "github.com/cockroachdb/apd" 21 "github.com/cockroachdb/cockroach/pkg/util/randutil" 22 ) 23 24 func TestDecimalMandE(t *testing.T) { 25 testCases := []struct { 26 Value string 27 E int 28 M []byte 29 }{ 30 {"1.0", 1, []byte{0x02}}, 31 {"10.0", 1, []byte{0x14}}, 32 {"99.0", 1, []byte{0xc6}}, 33 {"99.01", 1, []byte{0xc7, 0x02}}, 34 {"99.0001", 1, []byte{0xc7, 0x01, 0x02}}, 35 {"100.0", 2, []byte{0x02}}, 36 {"100.01", 2, []byte{0x03, 0x01, 0x02}}, 37 {"100.1", 2, []byte{0x03, 0x01, 0x14}}, 38 {"1234", 2, []byte{0x19, 0x44}}, 39 {"9999", 2, []byte{0xc7, 0xc6}}, 40 {"9999.000001", 2, []byte{0xc7, 0xc7, 0x01, 0x01, 0x02}}, 41 {"9999.000009", 2, []byte{0xc7, 0xc7, 0x01, 0x01, 0x12}}, 42 {"9999.00001", 2, []byte{0xc7, 0xc7, 0x01, 0x01, 0x14}}, 43 {"9999.00009", 2, []byte{0xc7, 0xc7, 0x01, 0x01, 0xb4}}, 44 {"9999.000099", 2, []byte{0xc7, 0xc7, 0x01, 0x01, 0xc6}}, 45 {"9999.0001", 2, []byte{0xc7, 0xc7, 0x01, 0x02}}, 46 {"9999.001", 2, []byte{0xc7, 0xc7, 0x01, 0x14}}, 47 {"9999.01", 2, []byte{0xc7, 0xc7, 0x02}}, 48 {"9999.1", 2, []byte{0xc7, 0xc7, 0x14}}, 49 {"10000", 3, []byte{0x02}}, 50 {"10001", 3, []byte{0x03, 0x01, 0x02}}, 51 {"12345", 3, []byte{0x03, 0x2f, 0x5a}}, 52 {"123450", 3, []byte{0x19, 0x45, 0x64}}, 53 {"1234.5", 2, []byte{0x19, 0x45, 0x64}}, 54 {"12.345", 1, []byte{0x19, 0x45, 0x64}}, 55 {"0.123", 0, []byte{0x19, 0x3c}}, 56 {"0.0123", 0, []byte{0x03, 0x2e}}, 57 {"0.00123", -1, []byte{0x19, 0x3c}}, 58 {"1e-307", -153, []byte{0x14}}, 59 {"1e308", 155, []byte{0x2}}, 60 {"9223372036854775807", 10, []byte{0x13, 0x2d, 0x43, 0x91, 0x07, 0x89, 0x6d, 0x9b, 0x75, 0x0e}}, 61 } 62 for _, c := range testCases { 63 d := new(apd.Decimal) 64 if _, _, err := d.SetString(c.Value); err != nil { 65 t.Fatalf("could not parse decimal from string %q", c.Value) 66 } 67 68 if e, m := decimalEandM(d, nil); e != c.E || !bytes.Equal(m, c.M) { 69 t.Errorf("unexpected mismatch in E/M for %v. expected E=%v | M=[% x], got E=%v | M=[% x]", 70 c.Value, c.E, c.M, e, m) 71 } 72 } 73 } 74 75 func mustDecimal(s string) *apd.Decimal { 76 d, _, err := new(apd.Decimal).SetString(s) 77 if err != nil { 78 panic(fmt.Sprintf("could not set string %q on decimal", s)) 79 } 80 return d 81 } 82 83 func randBuf(rng *rand.Rand, maxLen int) []byte { 84 buf := make([]byte, rng.Intn(maxLen+1)) 85 _, _ = rng.Read(buf) 86 return buf 87 } 88 89 func encodeDecimalWithDir(dir Direction, buf []byte, d *apd.Decimal) []byte { 90 if dir == Ascending { 91 return EncodeDecimalAscending(buf, d) 92 } 93 return EncodeDecimalDescending(buf, d) 94 } 95 96 func decodeDecimalWithDir( 97 t *testing.T, dir Direction, buf []byte, tmp []byte, 98 ) ([]byte, apd.Decimal) { 99 var err error 100 var resBuf []byte 101 var res apd.Decimal 102 if dir == Ascending { 103 resBuf, res, err = DecodeDecimalAscending(buf, tmp) 104 } else { 105 resBuf, res, err = DecodeDecimalDescending(buf, tmp) 106 } 107 if err != nil { 108 t.Fatal(err) 109 } 110 return resBuf, res 111 } 112 113 func mustDecimalFloat64(f float64) *apd.Decimal { 114 d, err := new(apd.Decimal).SetFloat64(f) 115 if err != nil { 116 panic(err) 117 } 118 return d 119 } 120 121 func mustDecimalString(s string) *apd.Decimal { 122 d, _, err := apd.NewFromString(s) 123 if err != nil { 124 panic(err) 125 } 126 return d 127 } 128 129 func TestEncodeDecimal(t *testing.T) { 130 testCases := []struct { 131 Value *apd.Decimal 132 Encoding []byte 133 }{ 134 {&apd.Decimal{Form: apd.NaN}, []byte{0x18}}, 135 {&apd.Decimal{Form: apd.Infinite, Negative: true}, []byte{0x19}}, 136 {apd.New(-99122, 99999), []byte{0x1a, 0x86, 0x3c, 0xad, 0x38, 0xe6, 0xd7, 0x00}}, 137 // Three duplicates to make sure -13*10^1000 <= -130*10^999 <= -13*10^1000 138 {apd.New(-13, 1000), []byte{0x1a, 0x86, 0xfe, 0x0a, 0xe5, 0x00}}, 139 {apd.New(-130, 999), []byte{0x1a, 0x86, 0xfe, 0x0a, 0xe5, 0x00}}, 140 {apd.New(-13, 1000), []byte{0x1a, 0x86, 0xfe, 0x0a, 0xe5, 0x00}}, 141 {mustDecimalFloat64(-math.MaxFloat64), []byte{0x1a, 0x87, 0x64, 0xfc, 0x60, 0x66, 0x44, 0xe4, 0x9e, 0x82, 0xc0, 0x8d, 0x00}}, 142 {apd.New(-130, 100), []byte{0x1a, 0x87, 0xcb, 0xfc, 0xc3, 0x00}}, 143 {apd.New(-13, 0), []byte{0x24, 0xe5, 0x00}}, 144 {apd.New(-11, 0), []byte{0x24, 0xe9, 0x00}}, 145 {mustDecimal("-10.123456789"), []byte{0x24, 0xea, 0xe6, 0xba, 0x8e, 0x62, 0x4b, 0x00}}, 146 {mustDecimal("-10"), []byte{0x24, 0xeb, 0x00}}, 147 {mustDecimal("-9.123456789"), []byte{0x24, 0xec, 0xe6, 0xba, 0x8e, 0x62, 0x4b, 0x00}}, 148 {mustDecimal("-9"), []byte{0x24, 0xed, 0x00}}, 149 {mustDecimal("-1.1"), []byte{0x24, 0xfc, 0xeb, 0x00}}, 150 {apd.New(-1, 0), []byte{0x24, 0xfd, 0x00}}, 151 {apd.New(-8, -1), []byte{0x25, 0x5f, 0x00}}, 152 {apd.New(-1, -1), []byte{0x25, 0xeb, 0x00}}, 153 {mustDecimal("-.09"), []byte{0x25, 0xed, 0x00}}, 154 {mustDecimal("-.054321"), []byte{0x25, 0xf4, 0xa8, 0xd5, 0x00}}, 155 {mustDecimal("-.012"), []byte{0x25, 0xfc, 0xd7, 0x00}}, 156 {apd.New(-11, -4), []byte{0x26, 0x89, 0xe9, 0x00}}, 157 {apd.New(-11, -6), []byte{0x26, 0x8a, 0xe9, 0x00}}, 158 {mustDecimalFloat64(-math.SmallestNonzeroFloat64), []byte{0x26, 0xf6, 0xa1, 0xf5, 0x00}}, 159 {apd.New(-11, -66666), []byte{0x26, 0xf7, 0x82, 0x34, 0xe9, 0x00}}, 160 {mustDecimal("-0"), []byte{0x27}}, 161 {apd.New(0, 0), []byte{0x27}}, 162 {mustDecimalFloat64(math.SmallestNonzeroFloat64), []byte{0x28, 0x87, 0x5e, 0x0a, 0x00}}, 163 {apd.New(11, -6), []byte{0x28, 0x87, 0xfd, 0x16, 0x00}}, 164 {apd.New(11, -4), []byte{0x28, 0x87, 0xfe, 0x16, 0x00}}, 165 {apd.New(1, -1), []byte{0x29, 0x14, 0x00}}, 166 {apd.New(8, -1), []byte{0x29, 0xa0, 0x00}}, 167 {apd.New(1, 0), []byte{0x2a, 0x02, 0x00}}, 168 {mustDecimal("1.1"), []byte{0x2a, 0x03, 0x14, 0x00}}, 169 {apd.New(11, 0), []byte{0x2a, 0x16, 0x00}}, 170 {apd.New(13, 0), []byte{0x2a, 0x1a, 0x00}}, 171 {mustDecimalFloat64(math.MaxFloat64), []byte{0x34, 0xf6, 0x9b, 0x03, 0x9f, 0x99, 0xbb, 0x1b, 0x61, 0x7d, 0x3f, 0x72, 0x00}}, 172 // Four duplicates to make sure 13*10^1000 <= 130*10^999 <= 1300*10^998 <= 13*10^1000 173 {apd.New(13, 1000), []byte{0x34, 0xf7, 0x01, 0xf5, 0x1a, 0x00}}, 174 {apd.New(130, 999), []byte{0x34, 0xf7, 0x01, 0xf5, 0x1a, 0x00}}, 175 {apd.New(1300, 998), []byte{0x34, 0xf7, 0x01, 0xf5, 0x1a, 0x00}}, 176 {apd.New(13, 1000), []byte{0x34, 0xf7, 0x01, 0xf5, 0x1a, 0x00}}, 177 {apd.New(99122, 99999), []byte{0x34, 0xf7, 0xc3, 0x52, 0xc7, 0x19, 0x28, 0x00}}, 178 {apd.New(99122839898321208, 99999), []byte{0x34, 0xf7, 0xc3, 0x58, 0xc7, 0x19, 0x39, 0x4f, 0xb3, 0xa7, 0x2b, 0x29, 0xa0, 0x00}}, 179 {&apd.Decimal{Form: apd.Infinite}, []byte{0x35}}, 180 } 181 182 rng, _ := randutil.NewPseudoRand() 183 184 var lastEncoded []byte 185 for _, dir := range []Direction{Ascending, Descending} { 186 for _, tmp := range [][]byte{nil, make([]byte, 0, 100)} { 187 for i, c := range testCases { 188 t.Run(fmt.Sprintf("%v_%d_%d_%s", dir, cap(tmp), i, c.Value), func(t *testing.T) { 189 enc := encodeDecimalWithDir(dir, nil, c.Value) 190 _, dec := decodeDecimalWithDir(t, dir, enc, tmp) 191 if dir == Ascending && !bytes.Equal(enc, c.Encoding) { 192 t.Errorf("unexpected mismatch for %s. expected [% x], got [% x]", 193 c.Value, c.Encoding, enc) 194 } 195 if i > 0 { 196 if (bytes.Compare(lastEncoded, enc) > 0 && dir == Ascending) || 197 (bytes.Compare(lastEncoded, enc) < 0 && dir == Descending) { 198 t.Errorf("%v: expected [% x] to be less than or equal to [% x]", 199 c.Value, testCases[i-1].Encoding, enc) 200 } 201 } 202 testPeekLength(t, enc) 203 if dec.Cmp(c.Value) != 0 { 204 t.Errorf("%d unexpected mismatch for %v. got %v", i, c.Value, dec) 205 } 206 lastEncoded = enc 207 208 // Test that appending the decimal to an existing buffer works. It 209 // is important to test with various values, slice lengths, and 210 // capacities because the various encoding paths try to use any 211 // spare capacity to avoid allocations. 212 for trials := 0; trials < 5; trials++ { 213 orig := randBuf(rng, 30) 214 origLen := len(orig) 215 216 bufCap := origLen + rng.Intn(30) 217 buf := make([]byte, origLen, bufCap) 218 copy(buf, orig) 219 220 enc := encodeDecimalWithDir(dir, buf, c.Value) 221 // Append some random bytes 222 enc = append(enc, randBuf(rng, 20)...) 223 _, dec := decodeDecimalWithDir(t, dir, enc[origLen:], tmp) 224 225 if dec.Cmp(c.Value) != 0 { 226 t.Errorf("unexpected mismatch for %v. got %v", c.Value, dec) 227 } 228 // Verify the existing values weren't modified. 229 for i := range orig { 230 if enc[i] != orig[i] { 231 t.Errorf("existing byte %d changed after encoding (from %d to %d)", 232 i, orig[i], enc[i]) 233 } 234 } 235 } 236 }) 237 } 238 } 239 } 240 } 241 242 func TestEncodeDecimalRand(t *testing.T) { 243 rng, _ := randutil.NewPseudoRand() 244 // Test both directions. 245 for _, dir := range []Direction{Ascending, Descending} { 246 var prev *apd.Decimal 247 var prevEnc []byte 248 const randomTrials = 100000 249 for i := 0; i < randomTrials; i++ { 250 cur := randDecimal(rng, -20, 20) 251 var tmp, appendTo []byte 252 // Test with and without appending. 253 if rng.Intn(2) == 1 { 254 appendTo = randBuf(rng, 30) 255 appendTo = appendTo[:rng.Intn(len(appendTo)+1)] 256 } 257 // Test with and without tmp buffer. 258 if rng.Intn(2) == 1 { 259 tmp = randBuf(rng, 100) 260 } 261 var enc []byte 262 var res apd.Decimal 263 var err error 264 if dir == Ascending { 265 enc = EncodeDecimalAscending(appendTo, cur) 266 enc = enc[len(appendTo):] 267 _, res, err = DecodeDecimalAscending(enc, tmp) 268 } else { 269 enc = EncodeDecimalDescending(appendTo, cur) 270 enc = enc[len(appendTo):] 271 _, res, err = DecodeDecimalDescending(enc, tmp) 272 } 273 if err != nil { 274 t.Fatal(err) 275 } 276 277 testPeekLength(t, enc) 278 279 // Make sure we decode the same value we encoded. 280 if cur.Cmp(&res) != 0 { 281 t.Fatalf("unexpected mismatch for %v, got %v", cur, res) 282 } 283 284 // Make sure lexicographical sorting is consistent. 285 if prev != nil { 286 bytesCmp := bytes.Compare(prevEnc, enc) 287 cmpType := "same" 288 if dir == Descending { 289 bytesCmp *= -1 290 cmpType = "inverse" 291 } 292 if decCmp := prev.Cmp(cur); decCmp != bytesCmp { 293 t.Fatalf("expected [% x] to compare to [% x] the %s way that %v compares to %v", 294 prevEnc, enc, cmpType, prev, cur) 295 } 296 } 297 prev = cur 298 prevEnc = enc 299 } 300 } 301 } 302 303 func TestNonsortingEncodeDecimal(t *testing.T) { 304 testCases := []struct { 305 Value *apd.Decimal 306 Encoding []byte 307 }{ 308 {&apd.Decimal{Form: apd.NaN}, []byte{0x18}}, 309 {&apd.Decimal{Form: apd.Infinite, Negative: true}, []byte{0x19}}, 310 {apd.New(-99122, 99999), []byte{0x1a, 0xf8, 0x01, 0x86, 0xa4, 0x01, 0x83, 0x32}}, 311 // Three duplicates to make sure -13*10^1000 <= -130*10^999 <= -13*10^1000 312 {apd.New(-13, 1000), []byte{0x1a, 0xf7, 0x03, 0xea, 0x0d}}, 313 {apd.New(-130, 999), []byte{0x1a, 0xf7, 0x03, 0xea, 0x82}}, 314 {apd.New(-13, 1000), []byte{0x1a, 0xf7, 0x03, 0xea, 0x0d}}, 315 {mustDecimalFloat64(-math.MaxFloat64), []byte{0x1a, 0xf7, 0x01, 0x35, 0x3f, 0xdd, 0xec, 0x7f, 0x2f, 0xaf, 0x35}}, 316 {apd.New(-130, 100), []byte{0x1a, 0xef, 0x82}}, 317 {apd.New(-13, 0), []byte{0x1a, 0x8a, 0x0d}}, 318 {apd.New(-11, 0), []byte{0x1a, 0x8a, 0x0b}}, 319 {apd.New(-1, 0), []byte{0x1a, 0x89, 0x01}}, 320 {apd.New(-8, -1), []byte{0x25, 0x08}}, 321 {apd.New(-1, -1), []byte{0x25, 0x01}}, 322 {apd.New(-11, -4), []byte{0x26, 0x8a, 0x0b}}, 323 {apd.New(-11, -6), []byte{0x26, 0x8c, 0x0b}}, 324 {mustDecimalFloat64(-math.SmallestNonzeroFloat64), []byte{0x26, 0xf7, 0x01, 0x43, 0x05}}, 325 {apd.New(-11, -66666), []byte{0x26, 0xf8, 0x01, 0x04, 0x68, 0x0b}}, 326 {mustDecimal("-0"), []byte{0x1a, 0x89}}, 327 {apd.New(0, 0), []byte{0x27}}, 328 {mustDecimalFloat64(math.SmallestNonzeroFloat64), []byte{0x28, 0xf7, 0x01, 0x43, 0x05}}, 329 {apd.New(11, -6), []byte{0x28, 0x8c, 0x0b}}, 330 {apd.New(11, -4), []byte{0x28, 0x8a, 0x0b}}, 331 {apd.New(1, -1), []byte{0x29, 0x01}}, 332 {apd.New(12345, -5), []byte{0x29, 0x30, 0x39}}, 333 {apd.New(8, -1), []byte{0x29, 0x08}}, 334 {apd.New(1, 0), []byte{0x34, 0x89, 0x01}}, 335 {apd.New(11, 0), []byte{0x34, 0x8a, 0x0b}}, 336 {apd.New(13, 0), []byte{0x34, 0x8a, 0x0d}}, 337 // Note that this does not sort correctly! 338 {apd.New(255, 0), []byte{0x34, 0x8b, 0xff}}, 339 {apd.New(256, 0), []byte{0x34, 0x8b, 0x01, 0x00}}, 340 {mustDecimalFloat64(math.MaxFloat64), []byte{0x34, 0xf7, 0x01, 0x35, 0x3f, 0xdd, 0xec, 0x7f, 0x2f, 0xaf, 0x35}}, 341 // Four duplicates to make sure 13*10^1000 <= 130*10^999 <= 1300*10^998 <= 13*10^1000 342 {apd.New(13, 1000), []byte{0x34, 0xf7, 0x03, 0xea, 0x0d}}, 343 {apd.New(130, 999), []byte{0x34, 0xf7, 0x03, 0xea, 0x82}}, 344 {apd.New(1300, 998), []byte{0x34, 0xf7, 0x03, 0xea, 0x05, 0x14}}, 345 {apd.New(13, 1000), []byte{0x34, 0xf7, 0x03, 0xea, 0x0d}}, 346 {apd.New(99122, 99999), []byte{0x34, 0xf8, 0x01, 0x86, 0xa4, 0x01, 0x83, 0x32}}, 347 {apd.New(99122839898321208, 99999), []byte{0x34, 0xf8, 0x01, 0x86, 0xb0, 0x01, 0x60, 0x27, 0xb2, 0x9d, 0x44, 0x71, 0x38}}, 348 {&apd.Decimal{Form: apd.Infinite}, []byte{0x35}}, 349 {mustDecimalString("142378208485490985369999605144727062141206925976498256305323716858805588894693616552055968571135475510700810219028167653516982373238641332965927953273383572708760984694356069974208844865675206339235758647159337463780100273189720943242182911961627806424621091859596571173867825568394327041453823674373002756096"), []byte{0x34, 0xf7, 0x01, 0x35, 0xca, 0xc0, 0xd8, 0x34, 0x68, 0x5d, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, 350 } 351 352 rng, _ := randutil.NewPseudoRand() 353 354 for _, tmp := range [][]byte{nil, make([]byte, 0, 100)} { 355 for i, c := range testCases { 356 t.Run(fmt.Sprintf("%d_%d_%s", cap(tmp), i, c.Value), func(t *testing.T) { 357 enc := EncodeNonsortingDecimal(nil, c.Value) 358 dec, err := DecodeNonsortingDecimal(enc, tmp) 359 if err != nil { 360 t.Fatal(err) 361 } 362 if !bytes.Equal(enc, c.Encoding) { 363 t.Errorf("unexpected mismatch for %s. expected [% x], got [% x]", 364 c.Value, c.Encoding, enc) 365 } 366 if dec.CmpTotal(c.Value) != 0 { 367 t.Errorf("%d unexpected mismatch for %v. got %v", i, c.Value, dec) 368 } 369 // Test that appending the decimal to an existing buffer works. It 370 // is important to test with various values, slice lengths, and 371 // capacities because the various encoding paths try to use any 372 // spare capacity to avoid allocations. 373 for trials := 0; trials < 5; trials++ { 374 orig := randBuf(rng, 30) 375 origLen := len(orig) 376 377 bufCap := origLen + rng.Intn(30) 378 buf := make([]byte, origLen, bufCap) 379 copy(buf, orig) 380 381 enc := EncodeNonsortingDecimal(buf, c.Value) 382 dec, err := DecodeNonsortingDecimal(enc[origLen:], tmp) 383 if err != nil { 384 t.Fatal(err) 385 } 386 387 if dec.CmpTotal(c.Value) != 0 { 388 t.Errorf("unexpected mismatch for %v. got %v", c.Value, dec) 389 } 390 // Verify the existing values weren't modified. 391 for i := range orig { 392 if enc[i] != orig[i] { 393 t.Errorf("existing byte %d changed after encoding (from %d to %d)", 394 i, orig[i], enc[i]) 395 } 396 } 397 } 398 }) 399 } 400 } 401 } 402 403 func TestNonsortingEncodeDecimalRand(t *testing.T) { 404 rng, _ := randutil.NewPseudoRand() 405 const randomTrials = 200000 406 for i := 0; i < randomTrials; i++ { 407 var tmp, appendTo []byte 408 // Test with and without appending. 409 if rng.Intn(2) == 1 { 410 appendTo = randBuf(rng, 30) 411 appendTo = appendTo[:rng.Intn(len(appendTo)+1)] 412 } 413 // Test with and without tmp buffer. 414 if rng.Intn(2) == 1 { 415 tmp = randBuf(rng, 100) 416 } 417 cur := randDecimal(rng, -20, 20) 418 419 enc := EncodeNonsortingDecimal(appendTo, cur) 420 enc = enc[len(appendTo):] 421 res, err := DecodeNonsortingDecimal(enc, tmp) 422 if err != nil { 423 t.Fatal(err) 424 } 425 426 // Make sure we decode the same value we encoded. 427 if cur.Cmp(&res) != 0 { 428 t.Fatalf("unexpected mismatch for %v, got %v", cur, res) 429 } 430 431 // Make sure we would have overestimated the value. 432 if est := UpperBoundNonsortingDecimalSize(cur); est < len(enc) { 433 t.Fatalf("expected estimate of %d for %v to be greater than or equal to the encoded length, found [% x]", est, cur, enc) 434 } 435 } 436 } 437 438 // TestNonsortingEncodeDecimalRoundtrip tests that decimals can round trip 439 // through EncodeNonsortingDecimal and DecodeNonsortingDecimal with an expected 440 // coefficient and exponent. 441 func TestNonsortingEncodeDecimalRoundtrip(t *testing.T) { 442 tests := map[string]string{ 443 "0": "0E+0", 444 "0.0": "0E-1", 445 "0.00": "0E-2", 446 "0e-10": "0E-10", 447 "0.00e-10": "0E-12", 448 "00": "0E+0", 449 "-0": "-0E+0", 450 "-0.0": "-0E-1", 451 "-0.00": "-0E-2", 452 "-0e-10": "-0E-10", 453 "-0.00e-10": "-0E-12", 454 "-00": "-0E+0", 455 } 456 for tc, expect := range tests { 457 t.Run(tc, func(t *testing.T) { 458 d, _, err := apd.NewFromString(tc) 459 if err != nil { 460 t.Fatal(err) 461 } 462 enc := EncodeNonsortingDecimal(nil, d) 463 res, err := DecodeNonsortingDecimal(enc, nil) 464 if err != nil { 465 t.Fatal(err) 466 } 467 s := res.Text('E') 468 if expect != s { 469 t.Fatalf("expected %s, got %s", expect, s) 470 } 471 }) 472 } 473 } 474 475 func TestDecodeMultipleDecimalsIntoNonsortingDecimal(t *testing.T) { 476 tcs := []struct { 477 value []string 478 }{ 479 { 480 []string{"1.0", "5.0", "7.0"}, 481 }, 482 { 483 []string{"1.0", "-1.0", "0.0"}, 484 }, 485 { 486 []string{"1.0", "-1.0", "10.0"}, 487 }, 488 { 489 []string{"nan", "1.0", "-1.0"}, 490 }, 491 { 492 []string{"-1.0", "inf", "5.0"}, 493 }, 494 } 495 496 for _, tc := range tcs { 497 var actual apd.Decimal 498 for _, num := range tc.value { 499 expected, _, err := apd.NewFromString(num) 500 if err != nil { 501 t.Fatal(err) 502 } 503 enc := EncodeNonsortingDecimal(nil, expected) 504 err = DecodeIntoNonsortingDecimal(&actual, enc, nil) 505 if err != nil { 506 t.Fatal(err) 507 } 508 if actual.Cmp(expected) != 0 { 509 t.Errorf("unexpected mismatch for %v, got %v", expected, &actual) 510 } 511 } 512 } 513 } 514 515 func TestUpperBoundNonsortingDecimalUnscaledSize(t *testing.T) { 516 x := make([]byte, 100) 517 d := new(apd.Decimal) 518 for i := 0; i < len(x); i++ { 519 d.Coeff.SetString(string(x[:i]), 10) 520 reference := UpperBoundNonsortingDecimalSize(d) 521 bound := upperBoundNonsortingDecimalUnscaledSize(i) 522 if bound < reference || bound > reference+bigWordSize { 523 t.Errorf("%d: got a bound of %d but expected between %d and %d", i, bound, reference, reference+bigWordSize) 524 } 525 x[i] = '1' 526 } 527 } 528 529 // randDecimal generates a random decimal with exponent in the 530 // range [minExp, maxExp]. 531 func randDecimal(rng *rand.Rand, minExp, maxExp int) *apd.Decimal { 532 exp := randutil.RandIntInRange(rng, minExp, maxExp+1) 533 // Transform random float in [0, 1) to [-1, 1) and multiply by 10^exp. 534 floatVal := (rng.Float64()*2 - 1) * math.Pow10(exp) 535 return mustDecimalFloat64(floatVal) 536 } 537 538 // makeDecimalVals creates decimal values with exponents in 539 // the range [minExp, maxExp]. 540 func makeDecimalVals(minExp, maxExp int) []*apd.Decimal { 541 rng, _ := randutil.NewPseudoRand() 542 vals := make([]*apd.Decimal, 10000) 543 for i := range vals { 544 vals[i] = randDecimal(rng, minExp, maxExp) 545 } 546 return vals 547 } 548 549 func makeEncodedVals(minExp, maxExp int) [][]byte { 550 rng, _ := randutil.NewPseudoRand() 551 vals := make([][]byte, 10000) 552 for i := range vals { 553 vals[i] = EncodeDecimalAscending(nil, randDecimal(rng, minExp, maxExp)) 554 } 555 return vals 556 } 557 558 func BenchmarkEncodeDecimalSmall(b *testing.B) { 559 vals := makeDecimalVals(-40, -1) 560 buf := make([]byte, 0, 100) 561 562 b.ResetTimer() 563 for i := 0; i < b.N; i++ { 564 _ = EncodeDecimalAscending(buf, vals[i%len(vals)]) 565 } 566 } 567 568 func BenchmarkDecodeDecimalSmall(b *testing.B) { 569 vals := makeEncodedVals(-40, -1) 570 buf := make([]byte, 0, 100) 571 572 b.ResetTimer() 573 for i := 0; i < b.N; i++ { 574 _, _, _ = DecodeDecimalAscending(vals[i%len(vals)], buf) 575 } 576 } 577 578 func BenchmarkEncodeDecimalMedium(b *testing.B) { 579 vals := makeDecimalVals(0, 10) 580 buf := make([]byte, 0, 100) 581 582 b.ResetTimer() 583 for i := 0; i < b.N; i++ { 584 _ = EncodeDecimalAscending(buf, vals[i%len(vals)]) 585 } 586 } 587 588 func BenchmarkDecodeDecimalMedium(b *testing.B) { 589 vals := makeEncodedVals(0, 10) 590 buf := make([]byte, 0, 100) 591 592 b.ResetTimer() 593 for i := 0; i < b.N; i++ { 594 _, _, _ = DecodeDecimalAscending(vals[i%len(vals)], buf) 595 } 596 } 597 598 func BenchmarkEncodeDecimalLarge(b *testing.B) { 599 vals := makeDecimalVals(11, 40) 600 buf := make([]byte, 0, 100) 601 602 b.ResetTimer() 603 for i := 0; i < b.N; i++ { 604 _ = EncodeDecimalAscending(buf, vals[i%len(vals)]) 605 } 606 } 607 608 func BenchmarkDecodeDecimalLarge(b *testing.B) { 609 vals := makeEncodedVals(11, 40) 610 buf := make([]byte, 0, 100) 611 612 b.ResetTimer() 613 for i := 0; i < b.N; i++ { 614 _, _, _ = DecodeDecimalAscending(vals[i%len(vals)], buf) 615 } 616 } 617 618 func BenchmarkPeekLengthDecimal(b *testing.B) { 619 vals := makeEncodedVals(-20, 20) 620 621 b.ResetTimer() 622 for i := 0; i < b.N; i++ { 623 _, _ = PeekLength(vals[i%len(vals)]) 624 } 625 } 626 627 func BenchmarkNonsortingEncodeDecimal(b *testing.B) { 628 vals := makeDecimalVals(-20, 20) 629 buf := make([]byte, 0, 100) 630 631 b.ResetTimer() 632 for i := 0; i < b.N; i++ { 633 _ = EncodeNonsortingDecimal(buf, vals[i%len(vals)]) 634 } 635 } 636 637 func BenchmarkNonsortingDecodeDecimal(b *testing.B) { 638 rng, _ := randutil.NewPseudoRand() 639 640 vals := make([][]byte, 10000) 641 for i := range vals { 642 d := randDecimal(rng, -20, 20) 643 vals[i] = EncodeNonsortingDecimal(nil, d) 644 } 645 646 buf := make([]byte, 0, 100) 647 648 b.ResetTimer() 649 for i := 0; i < b.N; i++ { 650 _, _ = DecodeNonsortingDecimal(vals[i%len(vals)], buf) 651 } 652 }