コード例 #1
0
ファイル: decimal.go プロジェクト: alaypatel07/cockroach
// 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
}
コード例 #2
0
ファイル: data.go プロジェクト: alaypatel07/cockroach
// 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
}
コード例 #3
0
ファイル: decimal.go プロジェクト: alaypatel07/cockroach
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)
}
コード例 #4
0
ファイル: encoding.go プロジェクト: alaypatel07/cockroach
// 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
	}
}
コード例 #5
0
ファイル: decimal_test.go プロジェクト: alaypatel07/cockroach
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)
		}
	}
}