Преобразование числа без знака в число без знака не останется отрицательным, это невозможно, поскольку допустимый диапазон типов без знака не содержит отрицательных чисел.Если вы напечатаете 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более выразительно: мы видим, что указанное выше значение явно использует секунды.