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

vze*_*zex 1 floating-point type-conversion go floating-point-conversion

package main

func main() {
    var n float64 = 6161047830682206209
    println(uint64(n))
}
Run Code Online (Sandbox Code Playgroud)

输出将是:

6161047830682206208
Run Code Online (Sandbox Code Playgroud)

看起来当float64改变时uint64,分数被丢弃.

icz*_*cza 7

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

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

规格:常数:

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

规格:数字类型:

float64     the set of all IEEE-754 64-bit floating-point numbers
Run Code Online (Sandbox Code Playgroud)

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

2<<52        : 9007199254740992
Your constant: 6161047830682206209
Run Code Online (Sandbox Code Playgroud)

准确地说15.95位数(16位数字,但不是所有可以用16位数描述的数值,仅限于数字9007199254740992).

您尝试放入类型变量的整数常量float64根本不适合 52位,因此必须进行舍入,数字(或位)将被截断(丢失).

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

var n float64 = 6161047830682206209
fmt.Printf("%f\n", n)
fmt.Printf("%d\n", uint64(n))
Run Code Online (Sandbox Code Playgroud)

输出:

6161047830682206208.000000
6161047830682206208
Run Code Online (Sandbox Code Playgroud)

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

只是为了好奇:

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

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

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

6161047830682206208.000000
6161047830682206208
Run Code Online (Sandbox Code Playgroud)

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

n = 7830682206209
fmt.Printf("%f\n", n)
fmt.Printf("%d\n", uint64(n))
Run Code Online (Sandbox Code Playgroud)

输出:

7830682206209.000000
7830682206209
Run Code Online (Sandbox Code Playgroud)

Go Playground尝试一下.


AJR*_*AJR 5

问题不在于转换,而在于数字太大,无法完全存储为float64. 分配给n导致一些意义的损失。

如果你仔细想想,你的数字(大约 6e18)非常接近最大的uint64(2^64-1 或大约 18e18)。Auint64使用全部 64 位来存储整数,但float64必须使用其 64 位中的一部分来存储指数,因此它可以使用更少的位来记住尾数。

总而言之,如果您将uint64大于约 10^15的(或整数常量)分配给 afloat64并返回给 a,uint64您将获得一个接近但可能不完全相同的值。

[BTW Icza 的回答很好而且正确。我希望这个答案是简单的总结。]