为什么不能将int64的最小值存储到uint64中?

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

Why can't you store the minimum value of int64 into a uint64?

问题

理论上,int64可以适应uint64,当然它们的数值会有所不同,因为int64的第一位用于表示符号。然而,为什么我不能进行这种转换呢?在我看来,这是有效的。

import (
	"fmt"
	"math"
)

func main() {
	var randomNum uint64 = math.MinInt64
	fmt.Println(randomNum)
}

会产生错误:

constant -9223372036854775808 overflows uint64

在这里,math.MinInt64-9223372036854775808,从技术上讲,这是一个符合63位加符号的数字。

我主要关注这个问题,因为我想将8个字节转换回带有正确符号的int64,但到目前为止,我只看到返回uint64的函数。

binary.LittleEndian.Uint64(bytesArrays[0:8])
英文:

In theory an int64 fits in a uint64, of course their numeric values will be different due to the first bit being reserved for the sign on int64, however why can't I do this conversion? it seems valid to me

import (
	"fmt"
	"math"
)

func main() {
	var randomNum uint64 = math.MinInt64
	fmt.Println(randomNum)
}

yields error

constant -9223372036854775808 overflows uint64

Here math.MinInt64 is -9223372036854775808, technically a number that fits in 63 bits plus sign.

I'm mainly looking into this since I want to convert 8 bytes back into a int64 with proper sign, but so far I only have seen functions that return uint64

binary.LittleEndian.Uint64(bytesArrays[0:8])

答案1

得分: 6

一个int64确实可以适应于uint64,但问题中的代码并不是这样做的。代码将常量math.MinInt64赋值给uint64变量randomNum。编译器报错是因为该常量无法由uint64表示。

首先将其赋值给一个变量,这样常量的规则就不适用了:

x := int64(math.MinInt64)
var randomNum = uint64(x)
fmt.Println(randomNum)
英文:

An int64 fits does fit in a uint64, but that's not what the code in the question does. The code assigns the constant math.MinInt64 to the uint64 variable randomNum. The compiler reports an error because the constant is not representable by a uint64.

Assign to a variable first so that the rules for constants do not apply:

x := int64(math.MinInt64)
var randomNum = uint64(x)
fmt.Println(randomNum)

答案2

得分: 6

这个“转换”不起作用的原因是因为你的程序中没有进行转换。赋值操作不会隐式进行转换(除了接口类型)。

请参考可赋值性规则,它规定了常量值的赋值:

如果满足以下条件之一,值x可以赋值给类型为T的变量(“x可以赋值给T”):

  • x的类型与T完全相同。
  • ...
  • x是一个可以用类型T的值表示的无类型常量。

在这种情况下,math.MinInt64是一个无类型常量,但它不能用uint64类型的值表示,因为它在定义中如此描述:

uint64 所有无符号64位整数的集合(0到18446744073709551615)

负64位整数和无符号64位整数之间的转换是可能的,但你需要进行真正的转换。然而,你不能直接转换常量值,因为有一个熟悉的规则:

如果x是类型T的值可以表示的常量值,那么常量值x可以转换为类型T。

因此,正确的方法是首先将数字赋值给一个非常量值:

x := int64(math.MinInt64)
y := uint64(x)
fmt.Println(y)

Playground链接

英文:

The reason why this "conversion" doesn't work, is because there is no conversion in your program. Assignment does not implicitly perform conversion (except for interface types).

See the assignability rules that govern assignment of constant values:

> A value x is assignable to a variable of type T ("x is assignable to T") if one of the following conditions applies:
> - x's type is identical to T.
> - ...
> - x is an untyped constant representable by a value of type T.

In this case, math.MinInt64 is an untyped constant, but it is not representable by value of type uint64, because it is defined as so:
> uint64 the set of all unsigned 64-bit integers (0 to 18446744073709551615)

Conversion between negative 64 bit integers and unsigned 64 bit integers is possible, but you need a genuine conversion for that. Yet still, you can't convert the constant value directly, because of a familiar rule:
> A constant value x can be converted to type T if x is representable by a value of T.

So, the correct way is to first assign the number to a non-constant value:

x := int64(math.MinInt64)
y := uint64(x)
fmt.Println(y)

Playground link

答案3

得分: 3

谁说你不能呢?好吧,编译器有点这么说。但是编译器的意思是你不能将值为-9223372036854775808的内容放入一个uint64类型的变量中,这是正确的;你需要将作为int64类型表示-9223372036854775808的位模式(但作为uint64类型表示时的值)放入uint64中:

var randomNum uint64 = math.MinInt64 + math.MaxUint64 + 1

这样是可以的(参考这里),或者你也可以像GastroHealth展示的那样

我主要是在研究这个问题,因为我想将[8]字节转换回一个带有正确符号的int64,但到目前为止,我只看到返回uint64的函数(比如binary.LittleEndian.Uint64)。

我们可以这样做:

signed := int64(v ^ 0x8000000000000000 - 0x8000000000000000)

(嗯,看过几次之后就很明显了。)

这里有一个完整的示例(我使用了大端序,我更喜欢它用于人类使用)。请注意,在这里你也可以直接使用signed := int64(v);翻转和减法的技巧适用于你还没有一个变量的表达式。

英文:

Who says you can't? Okay, the compiler says that, sort of. But the compiler means that you can't put the value -9223372036854775808 into a uint64 variable, which is true; you need to put the bit pattern that, as an int64, represents -9223372036854775808, but representing the value it would be seen as a uint64 into the uint64:

var randomNum uint64 = math.MinInt64 + math.MaxUint64 + 1

which works fine—or of course do what GastroHealth showed.

> I'm mainly looking into this since I want to convert [8] bytes back into a int64 with proper sign, but so far I only have seen functions that return uint64 (such as binary.LittleEndian.Uint64).

We do the obvious:

signed := int64(v ^ 0x8000000000000000 - 0x8000000000000000)

(Well, it's obvious once you've seen it a few times.)

See a complete example here (I used big-endian, I like it better for human use). Note that you can just do signed := int64(v) here; the flip-and-subtract trick is good as an expression where you don't already have a variable.

huangapple
  • 本文由 发表于 2021年10月25日 05:27:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/69700891.html
匿名

发表评论

匿名网友

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

确定