Robert Griesemer | f39cca9 | 2016-02-02 10:14:36 -0800 | [diff] [blame] | 1 | // Copyright 2015 The Go Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style |
| 3 | // license that can be found in the LICENSE file. |
| 4 | |
| 5 | // This file implements encoding/decoding of Floats. |
| 6 | |
| 7 | package big |
| 8 | |
Josh Bleecher Snyder | 5693bee | 2016-07-07 15:29:48 -0700 | [diff] [blame] | 9 | import ( |
| 10 | "encoding/binary" |
| 11 | "fmt" |
| 12 | ) |
| 13 | |
| 14 | // Gob codec version. Permits backward-compatible changes to the encoding. |
| 15 | const floatGobVersion byte = 1 |
| 16 | |
| 17 | // GobEncode implements the gob.GobEncoder interface. |
| 18 | // The Float value and all its attributes (precision, |
| 19 | // rounding mode, accuracy) are marshalled. |
| 20 | func (x *Float) GobEncode() ([]byte, error) { |
| 21 | if x == nil { |
| 22 | return nil, nil |
| 23 | } |
| 24 | |
| 25 | // determine max. space (bytes) required for encoding |
| 26 | sz := 1 + 1 + 4 // version + mode|acc|form|neg (3+2+2+1bit) + prec |
| 27 | n := 0 // number of mantissa words |
| 28 | if x.form == finite { |
| 29 | // add space for mantissa and exponent |
| 30 | n = int((x.prec + (_W - 1)) / _W) // required mantissa length in words for given precision |
| 31 | // actual mantissa slice could be shorter (trailing 0's) or longer (unused bits): |
| 32 | // - if shorter, only encode the words present |
| 33 | // - if longer, cut off unused words when encoding in bytes |
| 34 | // (in practice, this should never happen since rounding |
| 35 | // takes care of it, but be safe and do it always) |
| 36 | if len(x.mant) < n { |
| 37 | n = len(x.mant) |
| 38 | } |
| 39 | // len(x.mant) >= n |
| 40 | sz += 4 + n*_S // exp + mant |
| 41 | } |
| 42 | buf := make([]byte, sz) |
| 43 | |
| 44 | buf[0] = floatGobVersion |
| 45 | b := byte(x.mode&7)<<5 | byte((x.acc+1)&3)<<3 | byte(x.form&3)<<1 |
| 46 | if x.neg { |
| 47 | b |= 1 |
| 48 | } |
| 49 | buf[1] = b |
| 50 | binary.BigEndian.PutUint32(buf[2:], x.prec) |
| 51 | |
| 52 | if x.form == finite { |
| 53 | binary.BigEndian.PutUint32(buf[6:], uint32(x.exp)) |
| 54 | x.mant[len(x.mant)-n:].bytes(buf[10:]) // cut off unused trailing words |
| 55 | } |
| 56 | |
| 57 | return buf, nil |
| 58 | } |
| 59 | |
| 60 | // GobDecode implements the gob.GobDecoder interface. |
| 61 | // The result is rounded per the precision and rounding mode of |
| 62 | // z unless z's precision is 0, in which case z is set exactly |
| 63 | // to the decoded value. |
| 64 | func (z *Float) GobDecode(buf []byte) error { |
| 65 | if len(buf) == 0 { |
| 66 | // Other side sent a nil or default value. |
| 67 | *z = Float{} |
| 68 | return nil |
| 69 | } |
| 70 | |
| 71 | if buf[0] != floatGobVersion { |
| 72 | return fmt.Errorf("Float.GobDecode: encoding version %d not supported", buf[0]) |
| 73 | } |
| 74 | |
| 75 | oldPrec := z.prec |
| 76 | oldMode := z.mode |
| 77 | |
| 78 | b := buf[1] |
| 79 | z.mode = RoundingMode((b >> 5) & 7) |
| 80 | z.acc = Accuracy((b>>3)&3) - 1 |
| 81 | z.form = form((b >> 1) & 3) |
| 82 | z.neg = b&1 != 0 |
| 83 | z.prec = binary.BigEndian.Uint32(buf[2:]) |
| 84 | |
| 85 | if z.form == finite { |
| 86 | z.exp = int32(binary.BigEndian.Uint32(buf[6:])) |
| 87 | z.mant = z.mant.setBytes(buf[10:]) |
| 88 | } |
| 89 | |
| 90 | if oldPrec != 0 { |
| 91 | z.mode = oldMode |
| 92 | z.SetPrec(uint(oldPrec)) |
| 93 | } |
| 94 | |
| 95 | return nil |
| 96 | } |
Robert Griesemer | f39cca9 | 2016-02-02 10:14:36 -0800 | [diff] [blame] | 97 | |
| 98 | // MarshalText implements the encoding.TextMarshaler interface. |
| 99 | // Only the Float value is marshaled (in full precision), other |
| 100 | // attributes such as precision or accuracy are ignored. |
| 101 | func (x *Float) MarshalText() (text []byte, err error) { |
| 102 | if x == nil { |
| 103 | return []byte("<nil>"), nil |
| 104 | } |
| 105 | var buf []byte |
| 106 | return x.Append(buf, 'g', -1), nil |
| 107 | } |
| 108 | |
| 109 | // UnmarshalText implements the encoding.TextUnmarshaler interface. |
| 110 | // The result is rounded per the precision and rounding mode of z. |
| 111 | // If z's precision is 0, it is changed to 64 before rounding takes |
| 112 | // effect. |
| 113 | func (z *Float) UnmarshalText(text []byte) error { |
| 114 | // TODO(gri): get rid of the []byte/string conversion |
| 115 | _, _, err := z.Parse(string(text), 0) |
| 116 | if err != nil { |
| 117 | err = fmt.Errorf("math/big: cannot unmarshal %q into a *big.Float (%v)", text, err) |
| 118 | } |
| 119 | return err |
| 120 | } |