英文:
Go: How to check precision loss when converting float64 to float32
问题
我有一个场景,我接收到一个float64的值,但必须将其作为float32的值发送到另一个服务。我们知道接收到的值应该总是适合float32的。然而,为了安全起见,我想记录一下我们通过转换为float32而丢失数据的情况。
这段代码块无法编译,因为你不能直接比较float32和float64。
func convert(input float64) (output float32, err error) {
const tolerance = 0.001
output = float32(input)
if output > input+tolerance || output < input-tolerance {
return 0, errors.New("lost too much precision")
}
return output, nil
}
有没有一种简单的方法来检查我是否满足了这个条件?这个检查会频繁发生,所以我想避免进行字符串转换。
英文:
I have a scenario where I receive a float64 value, but must send it down the wire to another service as a float32 value. We know the received value should always fit into a float32. However, to be safe I want to log the case where we are losing data by converting to float32.
This code block does not compile, since you can't compare float32 to float64 directly.
func convert(input float64) (output float32, err error) {
const tolerance = 0.001
output = float32(input)
if output > input+tolerance || output < input-tolerance {
return 0, errors.New("lost too much precision")
}
return output, nil
}
Is there an easy way to check that I am hitting this condition? This check will happen at high frequency, so I want to avoid doing string conversions.
答案1
得分: 4
你可以将float32
的值转换回float64
,只是为了验证。
要检查转换后的值是否表示相同的值,只需将其与原始值(输入)进行比较。只返回一个ok bool
信息(而不是一个error
)也足够/符合惯例:
func convert(input float64) (output float32, ok bool) {
output = float32(input)
ok = float64(output) == input
return
}
(注意:未检查NaN
等边缘情况。)
进行测试:
fmt.Println(convert(1))
fmt.Println(convert(1.5))
fmt.Println(convert(0.123456789))
fmt.Println(convert(math.MaxFloat32))
输出结果(在Go Playground上尝试):
1 true
1.5 true
0.12345679 false
3.4028235e+38 true
请注意,由于float32
的精度小于float64
,因此这通常会导致ok = false
的结果,即使转换后的值可能非常接近输入值。
因此,在实践中,检查转换值的差异会更有用。您提出的解决方案检查了绝对差值,这并不是很有用:例如,1000000.1
和1000000
是非常接近的数字,尽管差值为0.1
。0.0001
和0.00011
的差异要小得多:0.00001
,但与这些数字相比,差异要大得多。
因此,您应该检查相对差异,例如:
func convert(input float64) (output float32, ok bool) {
const maxRelDiff = 1e-8
output = float32(input)
diff := math.Abs(float64(output) - input)
ok = diff <= math.Abs(input)*maxRelDiff
return
}
进行测试:
fmt.Println(convert(1))
fmt.Println(convert(1.5))
fmt.Println(convert(1e20))
fmt.Println(convert(math.Pi))
fmt.Println(convert(0.123456789))
fmt.Println(convert(math.MaxFloat32))
输出结果(在Go Playground上尝试):
1 true
1.5 true
1e+20 false
3.1415927 false
0.12345679 false
3.4028235e+38 true
英文:
You can convert back the float32
value to float64
, just for the validation.
To check if the converted value represents the same value, simply compare it to the original value (the input). It's also enough / idiomatic to just return an ok bool
info (instead of an error
):
func convert(input float64) (output float32, ok bool) {
output = float32(input)
ok = float64(output) == input
return
}
(Note: edge cases like NaN
are not checked.)
Testing it:
fmt.Println(convert(1))
fmt.Println(convert(1.5))
fmt.Println(convert(0.123456789))
fmt.Println(convert(math.MaxFloat32))
Output (try it on the Go Playground):
1 true
1.5 true
0.12345679 false
3.4028235e+38 true
Note that this will often give ok = false
result because the precision of float32
is less than that of float64
, even though the converted value may be very close to the input.
So in practice it would be more useful to check the difference of the converted value. Your proposed solution checks for the absolute difference value which is not so useful: for example 1000000.1
and 1000000
are very close numbers, even though the difference is 0.1
. 0.0001
and 0.00011
have much less difference: 0.00001
, yet the difference compared to the numbers is much bigger.
So you should check the relative difference, for example:
func convert(input float64) (output float32, ok bool) {
const maxRelDiff = 1e-8
output = float32(input)
diff := math.Abs(float64(output) - input)
ok = diff <= math.Abs(input)*maxRelDiff
return
}
Testing it:
fmt.Println(convert(1))
fmt.Println(convert(1.5))
fmt.Println(convert(1e20))
fmt.Println(convert(math.Pi))
fmt.Println(convert(0.123456789))
fmt.Println(convert(math.MaxFloat32))
Output (try it on the Go Playground):
1 true
1.5 true
1e+20 false
3.1415927 false
0.12345679 false
3.4028235e+38 true
答案2
得分: 0
是的。检查该值是否不超过上限或下限值。然后确保52-23位的最低有效位为0。(简而言之)
英文:
Yes. Check that the value does not exceed the upper or lower value limit. Then ensure the 52 - 23 least significant bits are 0. (in a nutshell)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论