github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/util/codec/decimal.go (about) 1 // Copyright 2015 PingCAP, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package codec 15 16 import ( 17 "bytes" 18 "math/big" 19 20 "github.com/insionng/yougam/libraries/juju/errors" 21 "github.com/insionng/yougam/libraries/pingcap/tidb/mysql" 22 ) 23 24 const ( 25 negativeSign int64 = 8 26 zeroSign int64 = 16 27 positiveSign int64 = 24 28 ) 29 30 func codecSign(value int64) int64 { 31 if value < 0 { 32 return negativeSign 33 } 34 35 return positiveSign 36 } 37 38 // EncodeDecimal encodes a decimal d into a byte slice which can be sorted lexicographically later. 39 // EncodeDecimal guarantees that the encoded value is in ascending order for comparison. 40 // Decimal encoding: 41 // Byte -> value sign 42 // EncodeInt -> exp value 43 // EncodeBytes -> abs value bytes 44 func EncodeDecimal(b []byte, d mysql.Decimal) []byte { 45 if d.Equals(mysql.ZeroDecimal) { 46 return append(b, byte(zeroSign)) 47 } 48 49 v := d.BigIntValue() 50 valSign := codecSign(int64(v.Sign())) 51 52 absVal := new(big.Int) 53 absVal.Abs(v) 54 55 value := []byte(absVal.String()) 56 57 // Trim right side "0", like "12.34000" -> "12.34" or "0.1234000" -> "0.1234". 58 if d.Exponent() != 0 { 59 value = bytes.TrimRight(value, "0") 60 } 61 62 // Get exp and value, format is "value":"exp". 63 // like "12.34" -> "0.1234":"2". 64 // like "-0.01234" -> "-0.1234":"-1". 65 exp := int64(0) 66 div := big.NewInt(10) 67 for ; ; exp++ { 68 if absVal.Sign() == 0 { 69 break 70 } 71 absVal = absVal.Div(absVal, div) 72 } 73 74 expVal := exp + int64(d.Exponent()) 75 if valSign == negativeSign { 76 expVal = -expVal 77 } 78 79 b = append(b, byte(valSign)) 80 b = EncodeInt(b, expVal) 81 if valSign == negativeSign { 82 b = EncodeBytesDesc(b, value) 83 } else { 84 b = EncodeBytes(b, value) 85 } 86 return b 87 } 88 89 // DecodeDecimal decodes bytes to decimal. 90 // DecodeFloat decodes a float from a byte slice 91 // Decimal decoding: 92 // Byte -> value sign 93 // Byte -> exp sign 94 // DecodeInt -> exp value 95 // DecodeBytes -> abs value bytes 96 func DecodeDecimal(b []byte) ([]byte, mysql.Decimal, error) { 97 var ( 98 r = b 99 d mysql.Decimal 100 err error 101 ) 102 103 // Decode value sign. 104 valSign := int64(r[0]) 105 r = r[1:] 106 if valSign == zeroSign { 107 d, err = mysql.ParseDecimal("0") 108 return r, d, errors.Trace(err) 109 } 110 111 // Decode exp value. 112 expVal := int64(0) 113 r, expVal, err = DecodeInt(r) 114 if err != nil { 115 return r, d, errors.Trace(err) 116 } 117 118 // Decode abs value bytes. 119 value := []byte{} 120 if valSign == negativeSign { 121 expVal = -expVal 122 r, value, err = DecodeBytesDesc(r) 123 } else { 124 r, value, err = DecodeBytes(r) 125 } 126 if err != nil { 127 return r, d, errors.Trace(err) 128 } 129 130 // Generate decimal string value. 131 var decimalStr []byte 132 if valSign == negativeSign { 133 decimalStr = append(decimalStr, '-') 134 } 135 136 if expVal <= 0 { 137 // Like decimal "0.1234" or "0.01234". 138 decimalStr = append(decimalStr, '0') 139 decimalStr = append(decimalStr, '.') 140 decimalStr = append(decimalStr, bytes.Repeat([]byte{'0'}, -int(expVal))...) 141 decimalStr = append(decimalStr, value...) 142 } else { 143 // Like decimal "12.34". 144 decimalStr = append(decimalStr, value[:expVal]...) 145 decimalStr = append(decimalStr, '.') 146 decimalStr = append(decimalStr, value[expVal:]...) 147 } 148 149 d, err = mysql.ParseDecimal(string(decimalStr)) 150 return r, d, errors.Trace(err) 151 }