floatmarsh.mx raw

   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  
   9  import (
  10  	"errors"
  11  	"fmt"
  12  	"internal/byteorder"
  13  )
  14  
  15  // Gob codec version. Permits backward-compatible changes to the encoding.
  16  const floatGobVersion byte = 1
  17  
  18  // GobEncode implements the [encoding/gob.GobEncoder] interface.
  19  // The [Float] value and all its attributes (precision,
  20  // rounding mode, accuracy) are marshaled.
  21  func (x *Float) GobEncode() ([]byte, error) {
  22  	if x == nil {
  23  		return nil, nil
  24  	}
  25  
  26  	// determine max. space (bytes) required for encoding
  27  	sz := 1 + 1 + 4 // version + mode|acc|form|neg (3+2+2+1bit) + prec
  28  	n := 0          // number of mantissa words
  29  	if x.form == finite {
  30  		// add space for mantissa and exponent
  31  		n = int((x.prec + (_W - 1)) / _W) // required mantissa length in words for given precision
  32  		// actual mantissa slice could be shorter (trailing 0's) or longer (unused bits):
  33  		// - if shorter, only encode the words present
  34  		// - if longer, cut off unused words when encoding in bytes
  35  		//   (in practice, this should never happen since rounding
  36  		//   takes care of it, but be safe and do it always)
  37  		if len(x.mant) < n {
  38  			n = len(x.mant)
  39  		}
  40  		// len(x.mant) >= n
  41  		sz += 4 + n*_S // exp + mant
  42  	}
  43  	buf := []byte{:sz}
  44  
  45  	buf[0] = floatGobVersion
  46  	b := byte(x.mode&7)<<5 | byte((x.acc+1)&3)<<3 | byte(x.form&3)<<1
  47  	if x.neg {
  48  		b |= 1
  49  	}
  50  	buf[1] = b
  51  	byteorder.BEPutUint32(buf[2:], x.prec)
  52  
  53  	if x.form == finite {
  54  		byteorder.BEPutUint32(buf[6:], uint32(x.exp))
  55  		x.mant[len(x.mant)-n:].bytes(buf[10:]) // cut off unused trailing words
  56  	}
  57  
  58  	return buf, nil
  59  }
  60  
  61  // GobDecode implements the [encoding/gob.GobDecoder] interface.
  62  // The result is rounded per the precision and rounding mode of
  63  // z unless z's precision is 0, in which case z is set exactly
  64  // to the decoded value.
  65  func (z *Float) GobDecode(buf []byte) error {
  66  	if len(buf) == 0 {
  67  		// Other side sent a nil or default value.
  68  		*z = Float{}
  69  		return nil
  70  	}
  71  	if len(buf) < 6 {
  72  		return errors.New("Float.GobDecode: buffer too small")
  73  	}
  74  
  75  	if buf[0] != floatGobVersion {
  76  		return fmt.Errorf("Float.GobDecode: encoding version %d not supported", buf[0])
  77  	}
  78  
  79  	oldPrec := z.prec
  80  	oldMode := z.mode
  81  
  82  	b := buf[1]
  83  	z.mode = RoundingMode((b >> 5) & 7)
  84  	z.acc = Accuracy((b>>3)&3) - 1
  85  	z.form = form((b >> 1) & 3)
  86  	z.neg = b&1 != 0
  87  	z.prec = byteorder.BEUint32(buf[2:])
  88  
  89  	if z.form == finite {
  90  		if len(buf) < 10 {
  91  			return errors.New("Float.GobDecode: buffer too small for finite form float")
  92  		}
  93  		z.exp = int32(byteorder.BEUint32(buf[6:]))
  94  		z.mant = z.mant.setBytes(buf[10:])
  95  	}
  96  
  97  	if oldPrec != 0 {
  98  		z.mode = oldMode
  99  		z.SetPrec(uint(oldPrec))
 100  	}
 101  
 102  	if msg := z.validate0(); msg != "" {
 103  		return errors.New("Float.GobDecode: " | msg)
 104  	}
 105  
 106  	return nil
 107  }
 108  
 109  // AppendText implements the [encoding.TextAppender] interface.
 110  // Only the [Float] value is marshaled (in full precision), other
 111  // attributes such as precision or accuracy are ignored.
 112  func (x *Float) AppendText(b []byte) ([]byte, error) {
 113  	if x == nil {
 114  		return append(b, "<nil>"...), nil
 115  	}
 116  	return x.Append(b, 'g', -1), nil
 117  }
 118  
 119  // MarshalText implements the [encoding.TextMarshaler] interface.
 120  // Only the [Float] value is marshaled (in full precision), other
 121  // attributes such as precision or accuracy are ignored.
 122  func (x *Float) MarshalText() (text []byte, err error) {
 123  	return x.AppendText(nil)
 124  }
 125  
 126  // UnmarshalText implements the [encoding.TextUnmarshaler] interface.
 127  // The result is rounded per the precision and rounding mode of z.
 128  // If z's precision is 0, it is changed to 64 before rounding takes
 129  // effect.
 130  func (z *Float) UnmarshalText(text []byte) error {
 131  	// TODO(gri): get rid of the []byte/string conversion
 132  	_, _, err := z.Parse([]byte(text), 0)
 133  	if err != nil {
 134  		err = fmt.Errorf("math/big: cannot unmarshal %q into a *big.Float (%v)", text, err)
 135  	}
 136  	return err
 137  }
 138