Преобразование оператора Gloang - PullRequest
0 голосов
/ 05 марта 2019

Я не могу понять в Голанге, как 1<<s вернуть 0, если var s uint = 33.Но 1<<33 вернуть 8589934592.Как преобразование оператора сдвига заканчивается значением 0.

Я читаю спецификацию языка и застрял в этом разделе: https://golang.org/ref/spec#Operators

В частности, этот абзац из документов:

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

Некоторые примеры из официальных документов Голанга:

var s uint = 33
var i = 1<<s                  // 1 has type int
var j int32 = 1<<s            // 1 has type int32; j == 0
var k = uint64(1<<s)          // 1 has type uint64; k == 1<<33

Обновление:

Еще один очень связанный вопрос с примером:

package main

import (
    "fmt"
)

func main() {
v := int16(4336)
    fmt.Println(int8(v))
}

Эта программа возвращает -16

Какчисло 4336 становится -16 при преобразовании int16 в int8

Ответы [ 2 ]

0 голосов
/ 05 марта 2019

Вы собираете и запускаете программу в 32-битном режиме (ходите на игровую площадку?).В нем int имеет ширину 32 бита и ведет себя так же, как int32.

0 голосов
/ 05 марта 2019

Если у вас есть это:

var s uint = 33
fmt.Println(1 << s)

Тогда применяется цитируемая часть:

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

Поскольку s не является константой (это переменная), следовательно, 1 >> s являетсявыражение непостоянного сдвига.А левый операнд - 1, который является нетипизированной константой (например, int(1) будет типизированной константой), поэтому он преобразуется в тип, который он получит, если выражение будет просто 1 вместо 1 << s:

fmt.Println(1)

В приведенном выше примере нетипизированная константа 1 будет преобразована в int, поскольку это ее тип по умолчанию.Тип констант по умолчанию: Spec: Константы:

Нетипизированная константа имеет тип по умолчанию , который является типом, в который константа неявно преобразуется вконтексты, где требуется типизированное значение, например, в кратком объявлении , например i := 0, где нет явного типа.Тип по умолчанию для нетипизированной константы - bool, rune, int, float64, complex128 или string соответственно, в зависимости от того, является ли она логической, рунической, целой, с плавающей точкой, сложнойили строковая константа.

И результат вышеизложенного зависит от архитектуры.Если int равно 32 битам, это будет 0.Если int равно 64 битам, это будет 8589934592 (потому что смещение 1 бита 33 раза сместит его из 32-битного int числа).

На игровой площадке Go,размер int составляет 32 бита (4 байта).См. Этот пример:

fmt.Println("int size:", unsafe.Sizeof(int(0)))

var s uint = 33

fmt.Println(1 << s)
fmt.Println(int32(1) << s)
fmt.Println(int64(1) << s)

Вышеуказанные выходы (попробуйте на Go Playground ):

int size: 4
0
0
8589934592

Если я запустил вышеуказанное приложение на моем 64-В битовом компьютере вывод:

int size: 8
8589934592
0
8589934592

Также см. Блог Go: Константы о том, как константы работают в Go.

Обратите внимание, что если вы напишите 1 << 33это не то же самое, это не выражение неконстантного сдвига, к которому применяется ваша цитата: «левый операнд выражения неконстантного сдвига» .1<<33 является выражением с постоянным сдвигом, которое вычисляется в «постоянном пространстве», и результат будет преобразован в int, который не вписывается в 32-битный int, следовательно, ошибка времени компиляции.Он работает с переменными, потому что переменные могут переполняться.Константы не переполняются:

Числовые константы представляют собой точные значения произвольной точности и не переполняются.

См. Как Go выполняет арифметику для констант?

Обновление:

Отвечая на ваше добавление: преобразование из int16 в int8 просто сохраняет младшие 8 бит.И целые числа представлены в формате 2 в дополнении , где старший бит равен 1, если число отрицательное.

Это подробно описано в Спецификация: Преобразования:

При преобразовании между целочисленными типами, если значение является целым числом со знаком, оно расширяется до неявной бесконечной точности;в противном случае это ноль продлен. Затем оно усекается для соответствия размеру типа результата. Например, если v := uint16(0x10F0), то uint32(int8(v)) == 0xFFFFFFF0.Преобразование всегда дает действительное значение;нет никаких признаков переполнения.

Так что при преобразовании значения int16 в int8, если номер источника имеет 1 в битовой позиции 7 (8-й бит), результат будетбыть отрицательным, даже если источник не был отрицательным.Точно так же, если источник имеет 0 в битовой позиции 7, результат будет положительным, даже если источник отрицательный.

См. Этот пример:

for _, v := range []int16{4336, -129, 8079} {
    fmt.Printf("Source    : %v\n", v)
    fmt.Printf("Source hex: %4x\n", uint16(v))
    fmt.Printf("Result hex: %4x\n", uint8(int8(v)))
    fmt.Printf("Result    : %4v\n", uint8(int8(v)))
    fmt.Println()
}

Вывод (попробуйтена игровой площадке Go ):

Source    : 4336
Source hex: 10f0
Result hex:   f0
Result    :  -16

Source    : -129
Source hex: ff7f
Result hex:   7f
Result    :  127

Source    : 8079
Source hex: 1f8f
Result hex:   8f
Result    : -113

См. связанные вопросы:

При приведении int64 к uint64 знак сохраняется?

Форматировать печать 64-битного целого числа -1 как шестнадцатеричное отклонение между golang и C

...