我有一个包含负数的int64变量,我希望从包含正数的uint64变量中减去它:
var endTime uint64
now := time.Now().Unix()
endTime = uint64(now)
var interval int64
interval = -3600
endTime = endTime + uint64(interval)
上面的代码似乎有用,但我想知道我是否可以依赖它。我很惊讶,对Go来说是新手,在给负面数字输入uint64之后它仍然是负数 - 我原本计划减去现在的正值(在施法后)以得到我想要的。
答案 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
更具表现力:我们看到上面指定的值明确使用秒。