// decimalMandE computes and returns the mantissa M and exponent E for d. // // The mantissa is a base-100 representation of the value. The exponent E // determines where to put the decimal point. // // Each centimal digit of the mantissa is stored in a byte. If the value of the // centimal digit is X (hence X>=0 and X<=99) then the byte value will be 2*X+1 // for every byte of the mantissa, except for the last byte which will be // 2*X+0. The mantissa must be the minimum number of bytes necessary to // represent the value; trailing X==0 digits are omitted. This means that the // mantissa will never contain a byte with the value 0x00. // // If we assume all digits of the mantissa occur to the right of the decimal // point, then the exponent E is the power of one hundred by which one must // multiply the mantissa to recover the original value. func decimalMandE(d decimal.Decimal, tmp []byte) (int, []byte) { bs := d.BigInt().String() if bs[0] == '-' { bs = bs[1:] } // The exponent will be the combination of the decimal's exponent, and the // number of digits in the big.Int. e10 := int(d.Exponent()) + len(bs) // Strip off trailing zeros in big.Int's string representation. for bs[len(bs)-1] == '0' { bs = bs[:len(bs)-1] } // Make sure b is large enough to hold the smallest even number of bytes // greater than or equal to the size of bs + 1. if n := 2 * ((len(bs) + 2) / 2); n <= cap(tmp) { tmp = tmp[:len(bs)+1] } else { tmp = make([]byte, len(bs)+1, n) } tmp[0] = '0' copy(tmp[1:], []byte(bs)) // Convert the power-10 exponent to a power of 100 exponent. var e100 int if e10 >= 0 { e100 = (e10 + 1) / 2 } else { e100 = e10 / 2 } // Strip the leading 0 if the conversion to e100 did not add a multiple of // 10. if e100*2 == e10 { tmp = tmp[1:] } // Ensure that the number of digits is even. if len(tmp)%2 != 0 { tmp = append(tmp, '0') } // Convert the base-10 'b' slice to a base-100 'm' slice. We do this // conversion in place to avoid an allocation. m := tmp[:len(tmp)/2] for i := 0; i < len(tmp); i += 2 { accum := 10*int(tmp[i]-'0') + int(tmp[i+1]-'0') // The bytes are encoded as 2n+1. m[i/2] = byte(2*accum + 1) } // The last byte is encoded as 2n+0. m[len(m)-1]-- return e100, m }
// SetDecimal encodes the specified decimal value into the bytes field of // the receiver, sets the tag and clears the checksum. func (v *Value) SetDecimal(d decimal.Decimal) error { // TODO(nvanbenschoten) Deal with exponent normalization. bb, err := d.BigInt().GobEncode() if err != nil { return fmt.Errorf("failed to Gob encode decimal's big.Int: %v", err) } v.RawBytes = make([]byte, headerSize+binary.MaxVarintLen64+len(bb)) n := binary.PutVarint(v.RawBytes[headerSize:], int64(d.Exponent())) copy(v.RawBytes[headerSize+n:], bb) v.RawBytes = v.RawBytes[:headerSize+n+len(bb)] v.setTag(ValueType_DECIMAL) return nil }
func encodeDecimal(b []byte, d decimal.Decimal, invert bool) []byte { // Handle the simplistic cases first. neg := false switch d.BigInt().Sign() { case -1: neg = true case 0: return append(b, floatZero) } // TODO(nvanbenschoten) Switch to a base-256 mantissa. e, m := decimalMandE(d, b[len(b):]) return encodeMandE(b, neg != invert, e, m) }
// prettyPrintFirstValue returns a string representation of the first decodable // value in the provided byte slice, along with the remaining byte slice // after decoding. func prettyPrintFirstValue(b []byte) ([]byte, string, error) { var err error switch PeekType(b) { case Null: b, _ = DecodeIfNull(b) return b, "NULL", nil case NotNull: b, _ = DecodeIfNotNull(b) return b, "#", nil case Int: var i int64 b, i, err = DecodeVarintAscending(b) if err != nil { return b, "", err } return b, strconv.FormatInt(i, 10), nil case Float: // Handle float specific values separately. switch b[0] { case floatNaN, floatNaNDesc: return b[1:], strconv.FormatFloat(math.NaN(), 'e', -1, 64), nil case floatInfinity: return b[1:], strconv.FormatFloat(math.Inf(1), 'e', -1, 64), nil case floatNegativeInfinity: return b[1:], strconv.FormatFloat(math.Inf(-1), 'e', -1, 64), nil } // Decode both floats and decimals as decimals to avoid // overflow. var d decimal.Decimal b, d, err = DecodeDecimalAscending(b, nil) if err != nil { return b, "", err } return b, d.String(), nil case Bytes: var s string b, s, err = DecodeStringAscending(b, nil) if err != nil { return b, "", err } return b, strconv.Quote(s), nil case BytesDesc: var s string b, s, err = DecodeStringDescending(b, nil) if err != nil { return b, "", err } return b, strconv.Quote(s), nil case Time: var t time.Time b, t, err = DecodeTimeAscending(b) if err != nil { return b, "", err } return b, t.UTC().Format(time.UnixDate), nil case TimeDesc: var t time.Time b, t, err = DecodeTimeDescending(b) if err != nil { return b, "", err } return b, t.UTC().Format(time.UnixDate), nil default: // This shouldn't ever happen, but if it does, return an empty slice. return nil, strconv.Quote(string(b)), nil } }
func TestEncodeDecimal(t *testing.T) { testCases := []struct { Value decimal.Decimal Encoding []byte }{ {decimal.NewFromFloat(-math.MaxFloat64), []byte{0x04, 0x64, 0xfc, 0x60, 0x66, 0x44, 0xe4, 0x9e, 0x82, 0xc0, 0x8d, 0x0}}, {decimal.New(-1, 308), []byte{0x04, 0x64, 0xfd, 0x0}}, // Four duplicates to make sure -1*10^4 <= -10*10^3 <= -100*10^2 <= -1*10^4 {decimal.New(-1, 4), []byte{0x0c, 0xfd, 0x0}}, {decimal.New(-10, 3), []byte{0x0c, 0xfd, 0x0}}, {decimal.New(-100, 2), []byte{0x0c, 0xfd, 0x0}}, {decimal.New(-1, 4), []byte{0x0c, 0xfd, 0x0}}, {decimal.New(-9999, 0), []byte{0x0d, 0x38, 0x39, 0x00}}, {decimal.New(-10, 1), []byte{0x0d, 0xfd, 0x00}}, {decimal.New(-99, 0), []byte{0x0e, 0x39, 0x00}}, {decimal.New(-1, 0), []byte{0x0e, 0xfd, 0x0}}, {decimal.New(-123, -5), []byte{0x10, 0x1, 0xe6, 0xc3, 0x0}}, {decimal.New(-1, -307), []byte{0x10, 0x99, 0xeb, 0x0}}, {decimal.NewFromFloat(-math.SmallestNonzeroFloat64), []byte{0x10, 0xa1, 0xf5, 0x0}}, {decimal.New(0, 0), []byte{0x11}}, {decimal.NewFromFloat(math.SmallestNonzeroFloat64), []byte{0x12, 0x5e, 0xa, 0x0}}, {decimal.New(1, -307), []byte{0x12, 0x66, 0x14, 0x0}}, {decimal.New(123, -5), []byte{0x12, 0xfe, 0x19, 0x3c, 0x0}}, {decimal.New(123, -4), []byte{0x13, 0x03, 0x2e, 0x0}}, {decimal.New(123, -3), []byte{0x13, 0x19, 0x3c, 0x0}}, {decimal.New(1, 0), []byte{0x14, 0x02, 0x0}}, {decimal.New(1, 1), []byte{0x14, 0x14, 0x0}}, {decimal.New(12345, -3), []byte{0x14, 0x19, 0x45, 0x64, 0x0}}, {decimal.New(990, -1), []byte{0x14, 0xc6, 0x0}}, {decimal.New(990001, -4), []byte{0x14, 0xc7, 0x01, 0x02, 0x0}}, {decimal.New(9901, -2), []byte{0x14, 0xc7, 0x02, 0x0}}, {decimal.New(10, 1), []byte{0x15, 0x02, 0x0}}, {decimal.New(10001, -2), []byte{0x15, 0x03, 0x01, 0x02, 0x0}}, {decimal.New(1001, -1), []byte{0x15, 0x03, 0x01, 0x14, 0x0}}, {decimal.New(1234, 0), []byte{0x15, 0x19, 0x44, 0x0}}, {decimal.New(12345, -1), []byte{0x15, 0x19, 0x45, 0x64, 0x0}}, {decimal.New(9999, 0), []byte{0x15, 0xc7, 0xc6, 0x0}}, {decimal.New(9999000001, -6), []byte{0x15, 0xc7, 0xc7, 0x01, 0x01, 0x02, 0x0}}, {decimal.New(9999000009, -6), []byte{0x15, 0xc7, 0xc7, 0x01, 0x01, 0x12, 0x0}}, {decimal.New(9999000010, -6), []byte{0x15, 0xc7, 0xc7, 0x01, 0x01, 0x14, 0x0}}, {decimal.New(9999000090, -6), []byte{0x15, 0xc7, 0xc7, 0x01, 0x01, 0xb4, 0x0}}, {decimal.New(9999000099, -6), []byte{0x15, 0xc7, 0xc7, 0x01, 0x01, 0xc6, 0x0}}, {decimal.New(99990001, -4), []byte{0x15, 0xc7, 0xc7, 0x01, 0x02, 0x0}}, {decimal.New(9999001, -3), []byte{0x15, 0xc7, 0xc7, 0x01, 0x14, 0x0}}, {decimal.New(999901, -2), []byte{0x15, 0xc7, 0xc7, 0x02, 0x0}}, {decimal.New(99991, -1), []byte{0x15, 0xc7, 0xc7, 0x14, 0x0}}, {decimal.New(10000, 0), []byte{0x16, 0x02, 0x0}}, {decimal.New(10001, 0), []byte{0x16, 0x03, 0x01, 0x02, 0x0}}, {decimal.New(12345, 0), []byte{0x16, 0x03, 0x2f, 0x5a, 0x0}}, {decimal.New(123450, 0), []byte{0x16, 0x19, 0x45, 0x64, 0x0}}, {decimal.New(1, 308), []byte{0x1e, 0x9b, 0x2, 0x0}}, {decimal.NewFromFloat(math.MaxFloat64), []byte{0x1e, 0x9b, 0x3, 0x9f, 0x99, 0xbb, 0x1b, 0x61, 0x7d, 0x3f, 0x72, 0x0}}, } var lastEncoded []byte for _, dir := range []Direction{Ascending, Descending} { for i, c := range testCases { var enc []byte var err error var dec decimal.Decimal if dir == Ascending { enc = EncodeDecimalAscending(nil, c.Value) _, dec, err = DecodeDecimalAscending(enc, nil) } else { enc = EncodeDecimalDescending(nil, c.Value) _, dec, err = DecodeDecimalDescending(enc, nil) } if dir == Ascending && !bytes.Equal(enc, c.Encoding) { t.Errorf("unexpected mismatch for %s. expected [% x], got [% x]", c.Value, c.Encoding, enc) } if i > 0 { if (bytes.Compare(lastEncoded, enc) > 0 && dir == Ascending) || (bytes.Compare(lastEncoded, enc) < 0 && dir == Descending) { t.Errorf("%v: expected [% x] to be less than or equal to [% x]", c.Value, testCases[i-1].Encoding, enc) } } if err != nil { t.Error(err) continue } if !dec.Equals(c.Value) { t.Errorf("%d unexpected mismatch for %v. got %v", i, c.Value, dec) } lastEncoded = enc } // Test that appending the decimal to an existing buffer works. var enc []byte var dec decimal.Decimal other := decimal.NewFromFloat(1.23) if dir == Ascending { enc = EncodeDecimalAscending([]byte("hello"), other) _, dec, _ = DecodeDecimalAscending(enc[5:], nil) } else { enc = EncodeDecimalDescending([]byte("hello"), other) _, dec, _ = DecodeDecimalDescending(enc[5:], nil) } if !dec.Equals(other) { t.Errorf("unexpected mismatch for %v. got %v", 1.23, other) } } }