При преобразовании int64 в uint64 знак сохраняется? - PullRequest
0 голосов
/ 12 июня 2018

У меня есть переменная 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 голосов
/ 12 июня 2018

Преобразование числа без знака в число без знака не останется отрицательным, это невозможно, поскольку допустимый диапазон типов без знака не содержит отрицательных чисел.Если вы напечатаете uint(interval), вы обязательно увидите положительное число.

То, что вы испытываете, является детерминированным, и вы можете на него положиться (но это не значит, что вы должны).Это результат того, что Go (и большинство других языков программирования) хранит целочисленные типы со знаком с использованием дополнения 2 .

Это означает, что в случае отрицательных чисел используется nбит, значение -x (где x положительно) сохраняется как двоичное представление положительного значения 2^n - x.Преимущество этого заключается в том, что числа могут быть добавлены поразрядно, и результат будет правильным независимо от того, являются ли они отрицательными или положительными.

Поэтому, когда у вас есть отрицательное число со знаком, оно в основном сохраняется в памяти, как если бывычел бы его абсолютное значение из 0.Это означает, что если вы преобразуете отрицательное значение со знаком в unsigned и добавляете его в значение без знака, результат будет правильным, потому что переполнение произойдет полезным способом.

Преобразование значения типа *От 1017 * до uint64 не изменяется макет памяти, только тип.Итак, что у 801 байта у int64, то у преобразованных uint64 будут те же 8 байтов.И, как упомянуто выше, представление, хранимое в этих 8 байтах, является битовой комбинацией, идентичной битовой комбинации значения 0 - abs(x).Таким образом, результатом преобразования будет число, которое вы получите, если вычтете abs(x) из 0 в мире без знака.Да, это не будет отрицательным (так как тип является беззнаковым), вместо этого это будет «большое» число, начиная с максимального значения типа uint64.Но если вы добавите число y больше abs(x) к этому «большому» числу, произойдет переполнение, и результат будет примерно таким: y - abs(x).

Посмотрите на этот простой пример, демонстрирующий, что происходит (попробуйтеэто на 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

Как уже упоминалось выше, вы не должны полагаться на это, так как это может вызвать путаницу.Если вы собираетесь работать с отрицательными числами, используйте подписанные типы.

А если вы работаете с базой кода, которая уже использует значения uint64, то вместо сложения выполните вычитание, используя значения uint64:

interval := uint64(3600)
endTime -= interval

Также обратите внимание, что если у вас есть значения time.Time, вы должны воспользоваться его Time.Add() методом:

func (t Time) Add(d Duration) Time

Вы можете указать time.Duration для добавления ко времени, которое может быть отрицательным, если вы хотите вернуться назад во времени, например:

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

time.Duration isболее выразительно: мы видим, что указанное выше значение явно использует секунды.

...