// 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 }