所以我收到一个接口{},但我想以任何方式将其转换为float64,如果不可能则返回错误。
这就是我正在做的事情:
func getFloat(unk interface{}) (float64, error) {
if v_flt, ok := unk.(float64); ok {
return v_flt, nil
} else if v_int, ok := unk.(int); ok {
return float64(v_int), nil
} else if v_int, ok := unk.(int16); ok {
return float64(v_int), nil
} else ... // other integer types
} else if v_str, ok := unk.(string); ok {
v_flt, err := strconv.ParseFloat(v_str, 64)
if err == nil {
return v_flt, nil
}
return math.NaN(), err
} else if unk == nil {
return math.NaN(), errors.New("getFloat: unknown value is nil")
} else {
return math.NaN(), errors.New("getFloat: unknown value is of incompatible type")
}
}
但我觉得我的方式不对,有没有更好的方法呢?
答案 0 :(得分:16)
Dave C使用reflect
得到了一个很好的答案,我将其与下面的按类型代码进行比较。首先,为了更简洁地完成您已经做过的事情,您可以使用type switch:
switch i := unk.(type) {
case float64:
return i, nil
case float32:
return float64(i), nil
case int64:
return float64(i), nil
// ...other cases...
default:
return math.NaN(), errors.New("getFloat: unknown value is of incompatible type")
}
case float64:
就像您的if i, ok := unk.(float64); ok { ... }
。针对该案例的代码,float64
访问i
。尽管没有大括号,但案例就像块一样:i
的类型在每个case
下都不同,并且没有C风格的漏洞。
另外,转换为float64
时请注意 large int64
s (over 253) will be rounded,因此,如果您将float64
视为“通用”数字类型,请考虑其局限性考虑到了。
一个例子是在http://play.golang.org/p/EVmv2ibI_j的游乐场。
Dave C提到,如果您使用reflect
,可以避免写出个别案例;他的答案有代码,甚至处理命名类型和指向合适类型的指针。他还提到了处理字符串和可转换为它们的类型。在做了比较选项的天真测试之后:
reflect
版本传递给int,每秒可获得约300万次转换;除非您转换数百万件物品,否则开销不会明显。 reflect
;常见类型(9m / s)的速度要快一些,但保留了reflect
代码的灵活性。switch
会失去一些灵活性,但可以挤出一些额外的性能,因为escape analysis可以确定其输入无法逃脱。 代码为on the Playground及以下:
package main
/* To actually run the timings, you need to run this from your machine, not the Playground */
import (
"errors"
"fmt"
"math"
"reflect"
"runtime"
"strconv"
"time"
)
var floatType = reflect.TypeOf(float64(0))
var stringType = reflect.TypeOf("")
func getFloat(unk interface{}) (float64, error) {
switch i := unk.(type) {
case float64:
return i, nil
case float32:
return float64(i), nil
case int64:
return float64(i), nil
case int32:
return float64(i), nil
case int:
return float64(i), nil
case uint64:
return float64(i), nil
case uint32:
return float64(i), nil
case uint:
return float64(i), nil
case string:
return strconv.ParseFloat(i, 64)
default:
v := reflect.ValueOf(unk)
v = reflect.Indirect(v)
if v.Type().ConvertibleTo(floatType) {
fv := v.Convert(floatType)
return fv.Float(), nil
} else if v.Type().ConvertibleTo(stringType) {
sv := v.Convert(stringType)
s := sv.String()
return strconv.ParseFloat(s, 64)
} else {
return math.NaN(), fmt.Errorf("Can't convert %v to float64", v.Type())
}
}
}
func getFloatReflectOnly(unk interface{}) (float64, error) {
v := reflect.ValueOf(unk)
v = reflect.Indirect(v)
if !v.Type().ConvertibleTo(floatType) {
return math.NaN(), fmt.Errorf("cannot convert %v to float64", v.Type())
}
fv := v.Convert(floatType)
return fv.Float(), nil
}
var errUnexpectedType = errors.New("Non-numeric type could not be converted to float")
func getFloatSwitchOnly(unk interface{}) (float64, error) {
switch i := unk.(type) {
case float64:
return i, nil
case float32:
return float64(i), nil
case int64:
return float64(i), nil
case int32:
return float64(i), nil
case int:
return float64(i), nil
case uint64:
return float64(i), nil
case uint32:
return float64(i), nil
case uint:
return float64(i), nil
default:
return math.NaN(), errUnexpectedType
}
}
func main() {
var m1, m2 runtime.MemStats
runtime.ReadMemStats(&m1)
start := time.Now()
for i := 0; i < 1e6; i++ {
getFloatReflectOnly(37)
}
fmt.Println("Reflect-only, 1e6 runs:")
fmt.Println("Wall time:", time.Now().Sub(start))
runtime.ReadMemStats(&m2)
fmt.Println("Bytes allocated:", m2.TotalAlloc-m1.TotalAlloc)
runtime.ReadMemStats(&m1)
start = time.Now()
for i := 0; i < 1e6; i++ {
getFloat(37)
}
fmt.Println("\nReflect-and-switch, 1e6 runs:")
fmt.Println("Wall time:", time.Since(start))
runtime.ReadMemStats(&m2)
fmt.Println("Bytes allocated:", m2.TotalAlloc-m1.TotalAlloc)
runtime.ReadMemStats(&m1)
start = time.Now()
for i := 0; i < 1e6; i++ {
getFloatSwitchOnly(37)
}
fmt.Println("\nSwitch only, 1e6 runs:")
fmt.Println("Wall time:", time.Since(start))
runtime.ReadMemStats(&m2)
fmt.Println("Bytes allocated:", m2.TotalAlloc-m1.TotalAlloc)
}
/*
Reflect-only, 1e6 runs:
Wall time: 297.335642ms
Bytes allocated: 16006648
Reflect-and-switch, 1e6 runs:
Wall time: 110.40057ms
Bytes allocated: 8001144
Switch only, 1e6 runs:
Wall time: 15.069157ms
Bytes allocated: 64
*/
答案 1 :(得分:7)
您可以使用反射包:
import "reflect"
var floatType = reflect.TypeOf(float64(0))
func getFloat(unk interface{}) (float64, error) {
v := reflect.ValueOf(unk)
v = reflect.Indirect(v)
if !v.Type().ConvertibleTo(floatType) {
return 0, fmt.Errorf("cannot convert %v to float64", v.Type())
}
fv := v.Convert(floatType)
return fv.Float(), nil
}
在Go Playground中运行:http://play.golang.org/p/FRM21HRq4o