When casting an int64 to uint64, is the sign retained?

核能气质少年 提交于 2019-12-08 03:38:23

问题


I have an int64 variable which contains a negative number and I wish to subtract it from an uint64 variable which contains a positive number:

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

The above code appears to work but I wonder if I can rely on this. I am surprised, being new to Go, that after casting a negative number to uint64 that it remains negative -- I had planned to subtract the now positive value (after casting) to get what I wanted.


回答1:


Converting a signed number to an unsigned number will not remain negative, it can't, as the valid range of unsigned types doesn't include negative numbers. If you print uint(interval), you will certainly see a positive number printed.

What you experience is deterministic and you can rely on it (but it doesn't mean you should). It is the result of Go (and most other programming languages) storing signed integer types using the 2's completement representation.

What this means is that in case of negative numbers, using n bit, the value -x (where x is positive) is stored as the binary representation of the positive value 2^n - x. This has the advantage that numbers can be added bitwise, and the result will be correct regardless of whether they are negative or positive.

So when you have a signed negative number, it is basically stored in memory like if you would subtract its absolute value from 0. Which means that if you convert a negative, signed value to unsigned, and you add that to an unsigned value, the result will be correct because overflow will happen, in a useful way.

Converting a value of type int64 to uint64 does not change the memory layout, only the type. So what 8 bytes the int64 had, the converted uint64 will have those same 8 bytes. And as mentioned above, the representation stored in those 8 bytes is the bit pattern identical to the bit pattern of value 0 - abs(x). So the result of the conversion will be a number that you would get if you would subtract abs(x) from 0, in the unsigned world. Yes, this won't be negative (as the type is unsigned), instead it will be a "big" number, counting down from the max value of the uint64 type. But if you add an y number bigger than abs(x) to this "big" number, overflow will happen, and the result will be like y - abs(x).

See this simple example demonstrating what's happening (try it on the Go Playground):

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

As mentioned above, you should not rely on this, as this may cause confusion. If you intend to work with negative numbers, then use signed types.

And if you work with a code base that already uses uint64 values, then do a subtraction instead of addition, using uint64 values:

interval := uint64(3600)
endTime -= interval

Also note that if you have time.Time values, you should take advantage of its Time.Add() method:

func (t Time) Add(d Duration) Time

You may specify a time.Duration to add to the time, which may be negative if you want to go back in time, like this:

t := time.Now()
t = t.Add(-3600 * time.Second)

time.Duration is more expressive: we see the value we specified above uses seconds explicitly.



来源:https://stackoverflow.com/questions/50815512/when-casting-an-int64-to-uint64-is-the-sign-retained

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!