godex: improved printing of numeric constants
TODO: Add testing code (another CL). LGTM=adonovan R=golang-codereviews, adonovan CC=golang-codereviews https://golang.org/cl/88090044
This commit is contained in:
parent
066bab1496
commit
a7ddb1e509
|
@ -7,8 +7,11 @@ package main
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"go/token"
|
||||||
"io"
|
"io"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"code.google.com/p/go.tools/go/exact"
|
||||||
"code.google.com/p/go.tools/go/types"
|
"code.google.com/p/go.tools/go/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -202,17 +205,122 @@ func (p *printer) printDecl(keyword string, n int, printGroup func()) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// absInt returns the absolute value of v as a *big.Int.
|
||||||
|
// v must be a numeric value.
|
||||||
|
func absInt(v exact.Value) *big.Int {
|
||||||
|
// compute big-endian representation of v
|
||||||
|
b := exact.Bytes(v) // little-endian
|
||||||
|
for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {
|
||||||
|
b[i], b[j] = b[j], b[i]
|
||||||
|
}
|
||||||
|
return new(big.Int).SetBytes(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
one = big.NewRat(1, 1)
|
||||||
|
ten = big.NewRat(10, 1)
|
||||||
|
)
|
||||||
|
|
||||||
|
// floatString returns the string representation for a
|
||||||
|
// numeric value v in normalized floating-point format.
|
||||||
|
func floatString(v exact.Value) string {
|
||||||
|
if exact.Sign(v) == 0 {
|
||||||
|
return "0.0"
|
||||||
|
}
|
||||||
|
// x != 0
|
||||||
|
|
||||||
|
// convert |v| into a big.Rat x
|
||||||
|
x := new(big.Rat).SetFrac(absInt(exact.Num(v)), absInt(exact.Denom(v)))
|
||||||
|
|
||||||
|
// normalize x and determine exponent e
|
||||||
|
// (This is not very efficient, but also not speed-critical.)
|
||||||
|
var e int
|
||||||
|
for x.Cmp(ten) >= 0 {
|
||||||
|
x.Quo(x, ten)
|
||||||
|
e++
|
||||||
|
}
|
||||||
|
for x.Cmp(one) < 0 {
|
||||||
|
x.Mul(x, ten)
|
||||||
|
e--
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(gri) Values such as 1/2 are easier to read in form 0.5
|
||||||
|
// rather than 5.0e-1. Similarly, 1.0e1 is easier to read as
|
||||||
|
// 10.0. Fine-tune best exponent range for readability.
|
||||||
|
|
||||||
|
s := x.FloatString(100) // good-enough precision
|
||||||
|
|
||||||
|
// trim trailing 0's
|
||||||
|
i := len(s)
|
||||||
|
for i > 0 && s[i-1] == '0' {
|
||||||
|
i--
|
||||||
|
}
|
||||||
|
s = s[:i]
|
||||||
|
|
||||||
|
// add a 0 if the number ends in decimal point
|
||||||
|
if len(s) > 0 && s[len(s)-1] == '.' {
|
||||||
|
s += "0"
|
||||||
|
}
|
||||||
|
|
||||||
|
// add exponent and sign
|
||||||
|
if e != 0 {
|
||||||
|
s += fmt.Sprintf("e%+d", e)
|
||||||
|
}
|
||||||
|
if exact.Sign(v) < 0 {
|
||||||
|
s = "-" + s
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(gri) If v is a "small" fraction (i.e., numerator and denominator
|
||||||
|
// are just a small number of decimal digits), add the exact fraction as
|
||||||
|
// a comment. For instance: 3.3333...e-1 /* = 1/3 */
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// valString returns the string representation for the value v.
|
||||||
|
// Setting floatFmt forces an integer value to be formatted in
|
||||||
|
// normalized floating-point format.
|
||||||
|
// TODO(gri) Move this code into package exact.
|
||||||
|
func valString(v exact.Value, floatFmt bool) string {
|
||||||
|
switch v.Kind() {
|
||||||
|
case exact.Int:
|
||||||
|
if floatFmt {
|
||||||
|
return floatString(v)
|
||||||
|
}
|
||||||
|
case exact.Float:
|
||||||
|
return floatString(v)
|
||||||
|
case exact.Complex:
|
||||||
|
re := exact.Real(v)
|
||||||
|
im := exact.Imag(v)
|
||||||
|
var s string
|
||||||
|
if exact.Sign(re) != 0 {
|
||||||
|
s = floatString(re)
|
||||||
|
if exact.Sign(im) >= 0 {
|
||||||
|
s += " + "
|
||||||
|
} else {
|
||||||
|
s += " - "
|
||||||
|
im = exact.UnaryOp(token.SUB, im, 0) // negate im
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// im != 0, otherwise v would be exact.Int or exact.Float
|
||||||
|
return s + floatString(im) + "i"
|
||||||
|
}
|
||||||
|
return v.String()
|
||||||
|
}
|
||||||
|
|
||||||
func (p *printer) printObj(obj types.Object) {
|
func (p *printer) printObj(obj types.Object) {
|
||||||
p.printf("%s", obj.Name())
|
p.print(obj.Name())
|
||||||
// don't write untyped types (for constants)
|
// don't write untyped types (for constants)
|
||||||
if typ := obj.Type(); typed(typ) {
|
typ, basic := obj.Type().Underlying().(*types.Basic)
|
||||||
|
if basic && typ.Info()&types.IsUntyped == 0 {
|
||||||
p.print(" ")
|
p.print(" ")
|
||||||
p.writeType(p.pkg, typ)
|
p.writeType(p.pkg, typ)
|
||||||
}
|
}
|
||||||
// write constant value
|
// write constant value
|
||||||
// TODO(gri) use floating-point notation for exact floating-point numbers (fractions)
|
|
||||||
if obj, ok := obj.(*types.Const); ok {
|
if obj, ok := obj.(*types.Const); ok {
|
||||||
p.printf(" = %s", obj.Val())
|
floatFmt := basic && typ.Info()&(types.IsFloat|types.IsComplex) != 0
|
||||||
|
p.print(" = ")
|
||||||
|
p.print(valString(obj.Val(), floatFmt))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,13 +336,6 @@ func (p *printer) printFunc(recvType types.Type, obj *types.Func) {
|
||||||
p.writeSignature(p.pkg, sig)
|
p.writeSignature(p.pkg, sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
func typed(typ types.Type) bool {
|
|
||||||
if t, ok := typ.Underlying().(*types.Basic); ok {
|
|
||||||
return t.Info()&types.IsUntyped == 0
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// combinedMethodSet returns the method set for a named type T
|
// combinedMethodSet returns the method set for a named type T
|
||||||
// merged with all the methods of *T that have different names than
|
// merged with all the methods of *T that have different names than
|
||||||
// the methods of T.
|
// the methods of T.
|
||||||
|
|
Loading…
Reference in New Issue