func TestDecGobEncoding(t *testing.T) { var medium bytes.Buffer enc := gob.NewEncoder(&medium) dec := gob.NewDecoder(&medium) for i, test := range decGobEncodingTests { for j := 0; j < 2; j++ { for k := inf.Scale(-5); k <= 5; k++ { medium.Reset() // empty buffer for each test case (in case of failures) stest := test if j != 0 { // negative numbers stest = "-" + test } var tx inf.Dec tx.SetString(stest) tx.SetScale(k) // test with positive, negative, and zero scale if err := enc.Encode(&tx); err != nil { t.Errorf("#%d%c: encoding failed: %s", i, 'a'+j, err) } var rx inf.Dec if err := dec.Decode(&rx); err != nil { t.Errorf("#%d%c: decoding failed: %s", i, 'a'+j, err) } if rx.Cmp(&tx) != 0 { t.Errorf("#%d%c: transmission failed: got %s want %s", i, 'a'+j, &rx, &tx) } } } } }
// ParseQuantity turns str into a Quantity, or returns an error. func ParseQuantity(str string) (*Quantity, error) { parts := splitRE.FindStringSubmatch(strings.TrimSpace(str)) // regexp returns are entire match, followed by an entry for each () section. if len(parts) != 3 { return nil, ErrFormatWrong } amount := new(inf.Dec) if _, ok := amount.SetString(parts[1]); !ok { return nil, ErrNumeric } base, exponent, format, ok := quantitySuffixer.interpret(suffix(parts[2])) if !ok { return nil, ErrSuffix } // So that no one but us has to think about suffixes, remove it. if base == 10 { amount.SetScale(amount.Scale() + inf.Scale(-exponent)) } else if base == 2 { // numericSuffix = 2 ** exponent numericSuffix := big.NewInt(1).Lsh(bigOne, uint(exponent)) ub := amount.UnscaledBig() amount.SetUnscaledBig(ub.Mul(ub, numericSuffix)) } // Cap at min/max bounds. sign := amount.Sign() if sign == -1 { amount.Neg(amount) } // This rounds non-zero values up to the minimum representable // value, under the theory that if you want some resources, you // should get some resources, even if you asked for way too small // of an amount. // Arguably, this should be inf.RoundHalfUp (normal rounding), but // that would have the side effect of rounding values < .5m to zero. if v, ok := amount.Unscaled(); v != int64(0) || !ok { amount.Round(amount, 3, inf.RoundUp) } // The max is just a simple cap. if amount.Cmp(maxAllowed) > 0 { amount.Set(maxAllowed) } if format == BinarySI && amount.Cmp(decOne) < 0 && amount.Cmp(decZero) > 0 { // This avoids rounding and hopefully confusion, too. format = DecimalSI } if sign == -1 { amount.Neg(amount) } return &Quantity{amount, format}, nil }
func dec(i int64, exponent int) *inf.Dec { // See the below test-- scale is the negative of an exponent. return inf.NewDec(i, inf.Scale(-exponent)) }
q.Amount.SetScale(0) q.Amount.SetUnscaled(c.Int63()) return } // Be sure to test cases like 1Mi q.Amount.SetScale(0) q.Amount.SetUnscaled(c.Int63n(1024) << uint(10*c.Intn(5))) return } if c.RandBool() { q.Format = DecimalSI } else { q.Format = DecimalExponent } if c.RandBool() { q.Amount.SetScale(inf.Scale(c.Intn(4))) q.Amount.SetUnscaled(c.Int63()) return } // Be sure to test cases like 1M q.Amount.SetScale(inf.Scale(3 - c.Intn(15))) q.Amount.SetUnscaled(c.Int63n(1000)) }, ) func TestJSON(t *testing.T) { for i := 0; i < 500; i++ { q := &Quantity{} fuzzer.Fuzz(q) b, err := json.Marshal(q) if err != nil {