Подтвердить что ты не робот

Как работает 1 << 64 - 1?

В http://tour.golang.org/#14 они показывают пример, где число 1 сдвинуто на 64 бит. Это, конечно, приведет к переполнению, но затем оно вычитается на 1, и все хорошо. Как половина выражения приводит к сбою, а все выражение в целом работает нормально?

Мысли:
Я бы предположил, что установка без знака на большее число, чем то, что он позволяет, вызывает взрыв. Казалось бы, память распределяется более свободно в правой части выражения, чем слева? Это правда?

4b9b3361

Ответ 1

Результат выражения - это константа (время компиляции), и поэтому выражение вычисляется во время компиляции. Спецификация языка требует, чтобы

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

const Huge = 1 << 100         // Huge == 1267650600228229401496703205376  (untyped integer constant)
const Four int8 = Huge >> 98  // Four == 4                                (type int8)

https://golang.org/ref/spec#Constant_expressions

Ответ 2

Это потому, что компилятор GO обрабатывает постоянные выражения как числовые константы. В отличие от типов данных, которые должны подчиняться закону диапазона, биты хранения и побочные эффекты, такие как переполнение, числовые константы никогда не теряют точность.

Числовые константы выводятся только с типом данных с ограниченной точностью и диапазоном в тот момент, когда вы назначаете их переменной (которая имеет известный тип и, следовательно, числовой диапазон и определенный способ хранения номера в битах). Вы также можете заставить их выводиться на обычный тип данных, используя их как часть уравнения, содержащего нечетные константные типы.

Confused? Я тоже..

Ниже приведена длинная запись типов данных и как обрабатываются константы: http://www.goinggo.net/2014/04/introduction-to-numeric-constants-in-go.html?m=1

Ответ 3

Фактически 1 << 64 - 1 не всегда приводит к сдвигу влево 64 и минус 1. Оператор - применяется перед оператором << на большинстве языков, по крайней мере, в любом из известных мне (например, С++, Java,...). Поэтому 1 << 64 - 1 <= > 1 << 63.

Но Go ведет себя иначе: https://golang.org/ref/spec#Operator_precedence

Оператор - приходит после оператора <<.

Результат сдвига влево на 64 бит основан на типе данных. Это точно так же, как добавление 64 из 0 справа, при разрезании любого Бит, расширяющего тип данных с левой стороны. В некоторых языках переполнение может быть допустимым, а в другом - не.

Компиляторы могут также вести себя по-разному на основе интерпретации, когда ваш сдвиг больше или равен фактическому размеру типа данных. Я знаю, что компилятор Java уменьшит фактический сдвиг так часто по размеру типа данных, пока он не станет меньше размера байт данных.

Звучит сложно, но здесь и простой пример для типа данных long с размером 64 Bit.

поэтому i << 64 <= > i << 0 <= > i

или i << 65 <= > i << 1

или i << 130 <= > i << 66 <= > i << 2.

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

Для обучения я бы предложил более общий язык, чем Go, возможно, как-то из семейства C.

Ответ 4

Я решил попробовать. По причинам, которые являются тонкими, выполнение выражения как постоянного выражения (1 << 64 -1) или по частям во время выполнения дает тот же ответ. Это связано с двумя различными механизмами. Постоянное выражение полностью оценивается с бесконечной точностью перед назначением переменной. Пошаговое выполнение явно допускает переполнение и переполнение через операции сложения, вычитания и сдвига, и, следовательно, результат тот же.

См. https://golang.org/ref/spec#Integer_overflow для описания того, как целые числа должны переполняться.

Однако, делая это в группах, то есть 1<<64, а затем -1 вызывает ошибки переполнения!

Вы можете сделать переполнение переменной, хотя и арифметикой, но вы не можете назначить переполнение переменной.

Попробуйте сами. Вставьте код ниже в http://try.golang.org/

Это работает:

// You can edit this code!
// Click here and start typing.
package main

import "fmt"

func main() {
  var MaxInt uint64 = 1
  MaxInt = MaxInt << 64
  MaxInt = MaxInt  - 1
  fmt.Println("%d",MaxInt)
}

Это не работает:

// You can edit this code!
// Click here and start typing.
package main

import "fmt"

func main() {
  var MaxInt uint64 = 1 << 64
  MaxInt = MaxInt  - 1
  fmt.Println("%d",MaxInt)
}