如何以正确的方式将float64数字更改为uint64?

时间:2015-06-17 16:25:22

标签: go floating-point type-conversion floating-point-conversion

package main

func main() {
    var n float64 = 6161047830682206209
    println(uint64(n))
}

输出将是:

6161047830682206208

float64更改为uint64时,该部分将被丢弃。

2 个答案:

答案 0 :(得分:7)

这里的问题是常量和浮点数的表示。

常量以任意精度表示。浮点数使用IEEE 754标准表示。

Spec: Constants:

  

数字常量表示任意精度的值,不会溢出。

Spec: Numeric types:

float64     the set of all IEEE-754 64-bit floating-point numbers

在IEEE 754中,使用64位(Go中为float64 53位的双精度用于存储数字。这意味着可以表示的最大数字(最大数字)是2<<52的数字位数(1位用于符号):

2<<52        : 9007199254740992
Your constant: 6161047830682206209

准确地说15.95位数(16位数,但不是所有可以用16位数描述的值,只能达到9007199254740992)。

您尝试将类型为float64的变量放入的整数常量不适合为52位,因此必须舍入并且数字(或位)将被截断(丢失)。

您可以通过打印原始n float64号码来验证这一点:

var n float64 = 6161047830682206209
fmt.Printf("%f\n", n)
fmt.Printf("%d\n", uint64(n))

输出:

6161047830682206208.000000
6161047830682206208

问题不在于转换,问题是您尝试转换的float64值已经不等于您尝试分配给它的常量。

只是出于好奇:

尝试使用更大的数字:与第一个const相比+500:

n = 6161047830682206709 // +500 compared to first!
fmt.Printf("%f\n", n2)
fmt.Printf("%d\n", uint64(n2))

输出仍然相同(最后的数字/位被截断,包括+500!):

6161047830682206208.000000
6161047830682206208

尝试使用较小的数字,其数字可以使用52位(小于~16位)精确表示:

n = 7830682206209
fmt.Printf("%f\n", n)
fmt.Printf("%d\n", uint64(n))

输出:

7830682206209.000000
7830682206209

Go Playground上尝试。

答案 1 :(得分:3)

问题不在于转换,而是该数字太大而不能精确地存储为float64的整数。 n的赋值会导致重要性下降。

如果考虑一下,您的数字(大约6e18)非常接近最大的uint64(2 ^ 64-1或大约18e18)。 uint64使用其全部64位来存储整数,而float64必须使用其全部64位来存储指数,因此记住尾数的位数将减少。

总而言之,如果您为uint64分配一个大于约10 ^ 15的float64(或整数常量),然后又返回一个uint64,您将获得收益,但可能值不完全相同。

[顺便说一句,依察的答案是正确的。我希望这个答案是简单的总结。]