将int64转换为uint64时,符号是否保留?

时间:2018-06-12 10:58:01

标签: go time integer unsigned-integer

我有一个包含负数的int64变量,我希望从包含正数的uint64变量中减去它:

var endTime uint64
now := time.Now().Unix()
endTime = uint64(now)
var interval int64
interval = -3600
endTime = endTime + uint64(interval)

上面的代码似乎有用,但我想知道我是否可以依赖它。我很惊讶,对Go来说是新手,在给负面数字输入uint64之后它仍然是负数 - 我原本计划减去现在的正值(在施法后)以得到我想要的。

1 个答案:

答案 0 :(得分:5)

将有符号数转换为无符号数不会保持为负数,也不能,因为无符号类型的有效范围不包含负数。如果您打印uint(interval),您肯定会看到正数字。

你所经历的是确定性的,你可以依赖它(但这并不意味着你应该这样做)。它是Go(和大多数其他编程语言)使用2's completement表示存储有符号整数类型的结果。

这意味着在负数的情况下,使用n位,值-x(其中x为正)存储为正值的二进制表示{ {1}}。这样做的好处是可以按位添加数字,无论结果是负数还是正数,结果都是正确的。

所以当你有一个带符号的负数时,它基本上存储在内存中,就好像你要从2^n - x中减去它的绝对值一样。这意味着如果将负的有符号值转换为无符号值,并将其添加到无符号值,结果将是正确的,因为溢出将以有用的方式发生。

将类型0的值转换为int64不会更改内存布局,只会更改类型。那么uint64有8个字节,转换后的int64将具有相同的8个字节。并且如上所述,存储在那8个字节中的表示是与值uint64的位模式相同的位模式。因此,转换的结果将是您在无符号世界中从0 - abs(x)减去abs(x)时得到的数字。是的,这不会是负数(因为类型是无符号的),而是一个“大”数,从0类型的最大值开始倒数。但是,如果您将大于uint64的{​​{1}}数字添加到此“大”数字,则会发生溢出,结果将类似于y

请参阅这个简单的示例,演示正在发生的事情(在Go Playground上尝试):

abs(x)

如上所述,你不应该依赖于此,因为这可能会引起混淆。如果您打算使用负数,请使用签名类型。

如果您使用已使用y - abs(x)值的代码库,则使用a := uint8(100) b := int8(-10) fmt.Println(uint8(b)) // Prints 226 which is: 0 - 10 = 256 - 10 a = a + uint8(b) fmt.Println(a) // Prints 90 which is: 100 + 226 = 326 = 90 // after overflow: 326 - 256 = 90 值进行减法而不是添加:

uint64

另请注意,如果您有time.Time个值,则应使用其Time.Add()方法:

uint64

您可以指定一个time.Duration添加到时间,如果您想要及时返回,可能会为负,如下所示:

interval := uint64(3600)
endTime -= interval

func (t Time) Add(d Duration) Time 更具表现力:我们看到上面指定的值明确使用秒。