github.com/markusbkk/elvish@v0.0.0-20231204143114-91dc52438621/pkg/eval/vals/string.go (about) 1 package vals 2 3 import ( 4 "math" 5 "strconv" 6 "strings" 7 ) 8 9 // Stringer wraps the String method. 10 type Stringer interface { 11 // Stringer converts the receiver to a string. 12 String() string 13 } 14 15 // ToString converts a Value to string. It is implemented for the builtin 16 // float64 and string types, and type satisfying the Stringer interface. It 17 // falls back to Repr(v). 18 func ToString(v interface{}) string { 19 switch v := v.(type) { 20 case int: 21 return strconv.Itoa(v) 22 case float64: 23 return formatFloat64(v) 24 // Other number types handled by "case Stringer" 25 case string: 26 return v 27 case Stringer: 28 return v.String() 29 default: 30 return ReprPlain(v) 31 } 32 } 33 34 func formatFloat64(f float64) string { 35 // Go's 'g' format is not quite ideal for printing floating point numbers; 36 // it uses scientific notation too aggressively, and relatively small 37 // numbers like 1234567 are printed with scientific notations, something we 38 // don't really want. 39 // 40 // So we use a different algorithm for determining when to use scientific 41 // notation. The algorithm is reverse-engineered from Racket's; it may not 42 // be a perfect clone but hopefully good enough. 43 // 44 // See also b.elv.sh/811 for more context. 45 s := strconv.FormatFloat(f, 'f', -1, 64) 46 noPoint := !strings.ContainsRune(s, '.') 47 if (noPoint && len(s) > 14 && s[len(s)-1] == '0') || 48 strings.HasPrefix(s, "0.0000") { 49 return strconv.FormatFloat(f, 'e', -1, 64) 50 } else if noPoint && !math.IsNaN(f) && !math.IsInf(f, 0) { 51 return s + ".0" 52 } 53 return s 54 }