Golang的strconv.ParseFloat在使用32作为bitSize参数时无法正确解析字符串。

huangapple go评论81阅读模式
英文:

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)
}

Go playground 链接

英文:

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)
}

Go playground link

答案1

得分: 4

这是32位变量的精度问题,与Go语言无关。请参考以下链接:

IEEE 754 - 维基百科

单精度浮点数格式 - 维基百科

你将会理解在这段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;
}

http://ideone.com/WobYbU

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)
}

Go Playground

更新

在大多数计算机上,浮点数值存储为以下元素。

  • 符号(零或一)
  • 尾数(系数)
  • 指数

例如,表示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.

IEEE 754 - Wikipedia

Single-precision floating-point format - Wikipedia

You will understand what happen with 1.59 in this C code.

#include &lt;stdio.h&gt;

int
main(int argc, char* argv[]) {
  float f = 1.59;
  if (f == 1.59) {
	puts(&quot;float: equal!&quot;);
  } else {
	puts(&quot;float: not equal!&quot;);
  }

  double d = 1.59;
  if (d == 1.59) {
	puts(&quot;double: equal!&quot;);
  } else {
	puts(&quot;double: not equal!&quot;);
  }
  return 0;
}

http://ideone.com/WobYbU

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 (
	&quot;fmt&quot;
	&quot;strconv&quot;
)

func main() {
	str := `1.59`
	f64, err := strconv.ParseFloat(str, 32)
	if err != nil {
		fmt.Println(&quot;err: &quot;, err)
	}
	f32 := float32(f64)
	fmt.Printf(&quot;%T -&gt; %+v\n&quot;, f32, f32)
}

Go Playground

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.

huangapple
  • 本文由 发表于 2017年7月5日 16:44:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/44921193.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定