// TODO(austin): Maybe add to bignum in more general form func ratToString(rat *bignum.Rational) string { n, dnat := rat.Value() d := bignum.MakeInt(false, dnat) w, frac := n.QuoRem(d) out := w.String() if frac.IsZero() { return out } r := frac.Abs() r = r.Mul(bignum.Nat(1e6)) dec, tail := r.DivMod(dnat) // Round last digit if tail.Cmp(dnat.Div(bignum.Nat(2))) >= 0 { dec = dec.Add(bignum.Nat(1)) } // Strip zeros ten := bignum.Nat(10) for !dec.IsZero() { dec2, r2 := dec.DivMod(ten) if !r2.IsZero() { break } dec = dec2 } out += "." + dec.String() return out }
// a.convertTo(t) converts the value of the analyzed expression a, // which must be a constant, ideal number, to a new analyzed // expression with a constant value of type t. // // TODO(austin) Rename to resolveIdeal or something? func (a *expr) convertTo(t Type) *expr { if !a.t.isIdeal() { log.Crashf("attempted to convert from %v, expected ideal", a.t) } var rat *bignum.Rational // XXX(Spec) The spec says "It is erroneous". // // It is an error to assign a value with a non-zero fractional // part to an integer, or if the assignment would overflow or // underflow, or in general if the value cannot be represented // by the type of the variable. switch a.t { case IdealFloatType: rat = a.asIdealFloat()() if t.isInteger() && !rat.IsInt() { a.diag("constant %v truncated to integer", ratToString(rat)) return nil } case IdealIntType: i := a.asIdealInt()() rat = bignum.MakeRat(i, bignum.Nat(1)) default: log.Crashf("unexpected ideal type %v", a.t) } // Check bounds if t, ok := t.lit().(BoundedType); ok { if rat.Cmp(t.minVal()) < 0 { a.diag("constant %v underflows %v", ratToString(rat), t) return nil } if rat.Cmp(t.maxVal()) > 0 { a.diag("constant %v overflows %v", ratToString(rat), t) return nil } } // Convert rat to type t. res := a.newExpr(t, a.desc) switch t := t.lit().(type) { case *uintType: n, d := rat.Value() f := n.Quo(bignum.MakeInt(false, d)) v := f.Abs().Value() res.eval = func(*Thread) uint64 { return v } case *intType: n, d := rat.Value() f := n.Quo(bignum.MakeInt(false, d)) v := f.Value() res.eval = func(*Thread) int64 { return v } case *idealIntType: n, d := rat.Value() f := n.Quo(bignum.MakeInt(false, d)) res.eval = func() *bignum.Integer { return f } case *floatType: n, d := rat.Value() v := float64(n.Value()) / float64(d.Value()) res.eval = func(*Thread) float64 { return v } case *idealFloatType: res.eval = func() *bignum.Rational { return rat } default: log.Crashf("cannot convert to type %T", t) } return res }