// DecodeDecimal decodes bytes to decimal. // DecodeFloat decodes a float from a byte slice // Decimal decoding: // Byte -> value sign // DecodeInt -> exp value // DecodeBytes -> abs value bytes func DecodeDecimal(b []byte) ([]byte, mysql.Decimal, error) { var ( r = b d mysql.Decimal err error ) // Decode value sign. valSign := int64(r[0]) r = r[1:] if valSign == zeroSign { d, err = mysql.ParseDecimal("0") return r, d, errors.Trace(err) } // Decode exp value. expVal := int64(0) r, expVal, err = DecodeInt(r) if err != nil { return r, d, errors.Trace(err) } // Decode abs value bytes. value := []byte{} if valSign == negativeSign { expVal = -expVal r, value, err = DecodeBytesDesc(r) } else { r, value, err = DecodeBytes(r) } if err != nil { return r, d, errors.Trace(err) } // Set decimal sign and point to value. if valSign == negativeSign { value = append([]byte("-0."), value...) } else { value = append([]byte("0."), value...) } numberDecimal, err := mysql.ParseDecimal(string(value)) if err != nil { return r, d, errors.Trace(err) } expDecimal := mysql.NewDecimalFromInt(1, int32(expVal)) d = numberDecimal.Mul(expDecimal) if expDecimal.Exponent() > 0 { // For int64(3), it will be converted to value=0.3 and exp=1 when doing encode. // Its frac will be changed after we run d = numberDecimal.Mul(expDecimal). // So we try to get frac to the original one. d.SetFracDigits(d.FracDigits() - expDecimal.Exponent()) } return r, d, nil }
func (d *Datum) compareMysqlDecimal(dec mysql.Decimal) (int, error) { switch d.k { case KindMysqlDecimal: return d.GetMysqlDecimal().Cmp(dec), nil case KindString, KindBytes: dDec, err := mysql.ParseDecimal(d.GetString()) return dDec.Cmp(dec), err default: fVal, _ := dec.Float64() return d.compareFloat64(fVal) } }
func (d *Datum) convertToMysqlDecimal(target *FieldType) (Datum, error) { var ret Datum var dec mysql.Decimal switch d.k { case KindInt64: dec = mysql.NewDecimalFromInt(d.GetInt64(), 0) case KindUint64: dec = mysql.NewDecimalFromUint(d.GetUint64(), 0) case KindFloat32, KindFloat64: dec = mysql.NewDecimalFromFloat(d.GetFloat64()) case KindString, KindBytes: var err error dec, err = mysql.ParseDecimal(d.GetString()) if err != nil { return ret, errors.Trace(err) } case KindMysqlDecimal: dec = d.GetMysqlDecimal() case KindMysqlTime: dec = d.GetMysqlTime().ToNumber() case KindMysqlDuration: dec = d.GetMysqlDuration().ToNumber() case KindMysqlBit: dec = mysql.NewDecimalFromFloat(d.GetMysqlBit().ToNumber()) case KindMysqlEnum: dec = mysql.NewDecimalFromFloat(d.GetMysqlEnum().ToNumber()) case KindMysqlHex: dec = mysql.NewDecimalFromFloat(d.GetMysqlHex().ToNumber()) case KindMysqlSet: dec = mysql.NewDecimalFromFloat(d.GetMysqlSet().ToNumber()) default: return invalidConv(d, target.Tp) } if target.Decimal != UnspecifiedLength { dec = dec.Round(int32(target.Decimal)) } ret.SetValue(dec) return ret, nil }
func testFrac(c *C, v mysql.Decimal) { b := EncodeDecimal([]byte{}, v) _, d, err := DecodeDecimal(b) c.Assert(err, IsNil) c.Assert(v.Equals(d), IsTrue) c.Assert(v.FracDigits(), Equals, d.FracDigits()) c.Assert(v.String(), Equals, d.String()) }
// EncodeDecimal encodes a decimal d into a byte slice which can be sorted lexicographically later. // EncodeDecimal guarantees that the encoded value is in ascending order for comparison. // Decimal encoding: // EncodeInt -> value sign // EncodeInt -> exp sign // EncodeInt -> exp value // EncodeBytes -> abs value bytes func EncodeDecimal(b []byte, d mysql.Decimal) []byte { if d.Equals(mysql.ZeroDecimal) { return EncodeInt(b, zeroSign) } v := d.BigIntValue() valSign := codecSign(int64(v.Sign())) absVal := new(big.Int) absVal.Abs(v) value := []byte(absVal.String()) // Trim right side "0", like "12.34000" -> "12.34" or "0.1234000" -> "0.1234". if d.Exponent() != 0 { value = bytes.TrimRight(value, "0") } // Get exp and value, format is "value":"exp". // like "12.34" -> "0.1234":"2". // like "-0.01234" -> "-0.1234":"-1". exp := int64(0) div := big.NewInt(10) for ; ; exp++ { if absVal.Sign() == 0 { break } absVal = absVal.Div(absVal, div) } expVal := exp + int64(d.Exponent()) expSign := codecSign(expVal) // For negtive exp, do bit reverse for exp. // For negtive decimal, do bit reverse for exp and value. expVal = encodeExp(expVal, expSign, valSign) codecValue(value, valSign) r := EncodeInt(b, valSign) r = EncodeInt(r, expSign) r = EncodeInt(r, expVal) r = EncodeBytes(r, value) return r }
// EncodeDecimal encodes a decimal d into a byte slice which can be sorted lexicographically later. // EncodeDecimal guarantees that the encoded value is in ascending order for comparison. // Decimal encoding: // Byte -> value sign // EncodeInt -> exp value // EncodeBytes -> abs value bytes func EncodeDecimal(b []byte, d mysql.Decimal) []byte { if d.Equals(mysql.ZeroDecimal) { return append(b, byte(zeroSign)) } v := d.BigIntValue() valSign := codecSign(int64(v.Sign())) absVal := new(big.Int) absVal.Abs(v) // Get exp and value, format is "value":"exp". // like "12.34" -> "0.1234":"2". // like "-0.01234" -> "-0.1234":"-1". exp := int64(0) div := big.NewInt(10) mod := big.NewInt(0) value := []byte{} for ; ; exp++ { if absVal.Sign() == 0 { break } mod.Mod(absVal, div) absVal.Div(absVal, div) value = append([]byte(strconv.Itoa(int(mod.Int64()))), value...) } value = bytes.TrimRight(value, "0") expVal := exp + int64(d.Exponent()) if valSign == negativeSign { expVal = -expVal } b = append(b, byte(valSign)) b = EncodeInt(b, expVal) if valSign == negativeSign { b = EncodeBytesDesc(b, value) } else { b = EncodeBytes(b, value) } return b }