英文:
Golang strconv.ParseFloat fails to parse the string correctly when using 32 for bitSize parameter
问题
为什么转换后的 float64 变量的值是 1.590000033378601
?
1.59
看起来足够小,可以适应 32 位的存储空间。
package main
import (
"fmt"
"strconv"
)
func main() {
str := `1.59`
float, err := strconv.ParseFloat(str, 32)
if err != nil {
fmt.Println("err: ", err)
}
fmt.Printf("%T -> %+v\n", float, float)
}
英文:
Why the converted float64 variable has 1.590000033378601
as value?
1.59
seems small enough to be fitted into 32 bits:
package main
import (
"fmt"
"strconv"
)
func main() {
str := `1.59`
float, err := strconv.ParseFloat(str, 32)
if err != nil {
fmt.Println("err: ", err)
}
fmt.Printf("%T -> %+v\n", float, float)
}
答案1
得分: 4
这是32位变量的精度问题,与Go语言无关。请参考以下链接:
你将会理解在这段C代码中发生了什么。
#include <stdio.h>
int
main(int argc, char* argv[]) {
float f = 1.59;
if (f == 1.59) {
puts("float: 相等!");
} else {
puts("float: 不相等!");
}
double d = 1.59;
if (d == 1.59) {
puts("double: 相等!");
} else {
puts("double: 不相等!");
}
return 0;
}
1.590000033378601在32位变量中表示为1.59。因此,你可以使用将其转换为float32的值,即float32(1.590000033378601)。
package main
import (
"fmt"
"strconv"
)
func main() {
str := `1.59`
f64, err := strconv.ParseFloat(str, 32)
if err != nil {
fmt.Println("err: ", err)
}
f32 := float32(f64)
fmt.Printf("%T -> %+v\n", f32, f32)
}
更新
在大多数计算机上,浮点数值存储为以下元素。
- 符号(零或一)
- 尾数(系数)
- 指数
例如,表示0.75的方式是+1.1 x 2^-1
+
是符号,.1
是尾数,-1
是指数。在32位内存空间上存储如下。
符号 尾数
+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
+-+-+-+-+-+-+-+-+
指数
例如,0.75
被存储如下
0-01111110-10000000000000000000000
+- 尾数
|
+ 1[.1] x 2 ^ -1
| | |
+- 符号 | +- 指数
|
+------ 基数
+ 1.1 x 2^-1 = 1 x 2^0 + 1 x 2^-1 x 2^-1 = 0.75
由于浮点数值的表示方式如上所示,它与数学值和计算机的值是不同的。这是一个精度问题。
英文:
It is precision issue of 32bit variable. This is not an issue of Go. See following URLs.
Single-precision floating-point format - Wikipedia
You will understand what happen with 1.59 in this C code.
#include <stdio.h>
int
main(int argc, char* argv[]) {
float f = 1.59;
if (f == 1.59) {
puts("float: equal!");
} else {
puts("float: not equal!");
}
double d = 1.59;
if (d == 1.59) {
puts("double: equal!");
} else {
puts("double: not equal!");
}
return 0;
}
1.590000033378601 is 1.59 in 32bit variables. So you can use the value with cast to float32. i.e. float32(1.590000033378601)
package main
import (
"fmt"
"strconv"
)
func main() {
str := `1.59`
f64, err := strconv.ParseFloat(str, 32)
if err != nil {
fmt.Println("err: ", err)
}
f32 := float32(f64)
fmt.Printf("%T -> %+v\n", f32, f32)
}
UPDATE
On Most of computer, floating point value is stored as following elements.
- Sign (zero or one)
- Significand (Coefficient)
- Exponent
For example, representations 0.75 is +1.1 x 2^-1
+
is Sign, .1
is Significand, -1
is Exponent. This is stored like below on 32bit memory space.
Sign Significand
+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
+-+-+-+-+-+-+-+-+
Exponent
For example, 0.75
is stored like below
0-01111110-10000000000000000000000
+- Significand
|
+ 1[.1] x 2 ^ -1
| | |
+- Sign | +- Exponent
|
+------ Base
> + 1.1 x 2^-1 = 1 x 2^0 + 1 x 2^-1 x 2^-1 = 0.75
Since floating point value is represented as above, it is different between the mathematical value and the computer's value. This is precision issue.
答案2
得分: 2
十进制数1.59
看起来很短,但这只是因为我们使用的是十进制数系统。
当计算机尝试用二进制科学计数法(a * 2^b
)表示相同的数时,需要更多的小数位数:1.1001011100001010001111010111000010100011110101110001 * 2^0
。
如果将这个值四舍五入为32位,存储在float32
类型的变量中,然后以十进制形式打印,你会得到1.59
,但如果将相同的四舍五入值存储在float64
类型的变量中,并以十进制形式打印,你会得到1.590000033378601
。
函数strconv.ParseFloat
返回的是float64类型,但由于你指定了32位的精度,你应该在使用之前将该值转换为float32类型。
英文:
The decimal number 1.59
looks short, but that's only because we are using the <b>decimal number system</b>.
When the computer tries to represent the same number in <b>binary scientific notation</b> (a * 2^b
) it requires a lot more fractional digits: 1.1001011100001010001111010111000010100011110101110001 * 2^0
.
If you round this value to 32 bits, store it in a variable of type float32
and then print as decimal, you get 1.59
, but storing the same rounded value in a float64
variable and printing as decimal, you get 1.590000033378601
.
Function strconv.ParseFloat
returns float64, but since you have specified 32 bits of precision, you are supposed to cast the value to float32 prior to using.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论