¿Funciona la evaluación del comstackdor difieren para la expresión constante y otras expresiones

¿Por qué el código siguiente no se puede comstackr?

package main import ( "fmt" "unsafe" ) var x int = 1 const ( ONE int = 1 MIN_INT int = ONE << (unsafe.Sizeof(x)*8 - 1) ) func main() { fmt.Println(MIN_INT) } 

Me sale un error

main.go: 12: constante 2147483648 desborda int

La statement anterior es correcta. Sí, 2147483648 desborda int (en architecture de 32 bits). Pero la operación de cambio debería dar como resultado un valor negativo, es decir, -2147483648.

Pero el mismo código funciona, si cambio las constantes en variables y obtengo el resultado esperado.

 package main import ( "fmt" "unsafe" ) var x int = 1 var ( ONE int = 1 MIN_INT int = ONE << (unsafe.Sizeof(x)*8 - 1) ) func main() { fmt.Println(MIN_INT) } 

Existe una diferencia en la evaluación entre expresiones constantes y no constantes que surge de que las constantes son precisas:

Las constantes numéricas representan valores exactos de precisión arbitraria y no se desbordan .

Las expresiones constantes escritas no pueden desbordarse; si el resultado no puede representarse por su tipo, es un error en tiempo de comstackción (esto puede detectarse en tiempo de comstackción).

Lo mismo no se aplica a las expresiones no constantes, ya que esto no se puede detectar en tiempo de comstackción (solo se pudo detectar en el tiempo de ejecución). Las operaciones en variables pueden desbordarse.

En su primer ejemplo, ONE es una constante tipada con tipo int . Esta expresión constante:

 ONE << (unsafe.Sizeof(x)*8 - 1) 

Es una expresión de cambio constante, se aplica lo siguiente: Spec: expresiones constantes:

Si el operando izquierdo de una expresión de cambio constante es una constante sin tipo, el resultado es una constante entera; de lo contrario, es una constante del mismo tipo que el operando izquierdo, que debe ser de tipo entero .

Entonces, el resultado de la expresión de cambio debe caber en un int porque esta es una expresión constante; pero como no es así, es un error en tiempo de comstackción.

En su segundo ejemplo, ONE no es una constante, es una variable de tipo int . Entonces la expresión de cambio aquí puede desbordarse, y lo hará, dando como resultado el valor negativo esperado.

Notas:

Si cambiara ONE en el segundo ejemplo a una constante en lugar de una variable, obtendría el mismo error (ya que la expresión en el inicializador sería una expresión constante). Si cambia ONE a una variable en el primer ejemplo, no funcionaría ya que las variables no pueden usarse en expresiones constantes (debe ser una expresión constante porque inicializa una constante).

Expresiones constantes para encontrar valores min-max

Puede usar la siguiente solución que produce los valores máximos y mínimos de los tipos uint e int :

 const ( MaxUint = ^uint(0) MinUint = 0 MaxInt = int(MaxUint >> 1) MinInt = -MaxInt - 1 ) func main() { fmt.Printf("uint: %d..%d\n", MinUint, MaxUint) fmt.Printf("int: %d..%d\n", MinInt, MaxInt) } 

Salida (pruébalo en el área de juegos Go ):

 uint: 0..4294967295 int: -2147483648..2147483647 

La lógica detrás de esto radica en la especificación: expresiones constantes:

La máscara utilizada por el operador de complemento bit a bit unario coincide con la regla para los no constantes: la máscara es todo 1 para las constantes sin signo y -1 para las constantes con y sin signo.

Entonces, la expresión constante ^uint(0) es de tipo uint y es el valor máximo de uint : tiene todos sus bits establecidos en 1 . Dado que los enteros se representan utilizando el complemento de 2 : al desplazar esto a la izquierda en 1 obtendrás el valor de max int , desde el cual el valor min int es -MaxInt - 1 ( -1 debido al valor 0 ).

Razonamiento por el comportamiento diferente

¿Por qué no hay desbordamiento de expresiones constantes y desbordamiento de expresiones no constantes?

Esto último es fácil: en la mayoría de los otros lenguajes (de progtwigción) hay desbordamiento. Entonces este comportamiento es consistente con otros lenguajes y tiene sus beneficios.

La verdadera pregunta es la primera: ¿por qué no se permite el desbordamiento de expresiones constantes?

Las constantes en Go son más que valores de variables tipadas: representan valores exactos de precisión arbitraria . Permanecer en la palabra exacta , si tiene un valor que desea asignar a una constante escrita , permitir el desbordamiento y asignar un valor completamente diferente en realidad no está a la altura de la exacta .

En el futuro, este tipo de control y desestimación del desbordamiento puede detectar errores como este:

 type Char byte var c1 Char = 'a' // OK var c2 Char = '世' // Compile-time error: constant 19990 overflows Char 

¿Qué pasa aquí? c1 Char = 'a' funciona porque 'a' es una constante de rune , y rune es alias para int32 , y 'a' tiene valor numérico 97 que se ajusta al rango válido de byte (que es 0..255 ).

Pero c2 Char = '世' da como resultado un error en tiempo de comstackción porque la runa '世' tiene un valor numérico 19990 que no cabe en un byte . Si se permitiera el desbordamiento, su código se comstackría y asignaría 22 valores numéricos ( '\x16' ) a c2 pero obviamente esta no era su intención. Al no permitir el desbordamiento, este error se detecta fácilmente y en tiempo de comstackción.

Para verificar los resultados:

 var c1 Char = 'a' fmt.Printf("%d %q %c\n", c1, c1, c1) // var c2 Char = '世' // Compile-time error: constant 19990 overflows Char r := '世' var c2 Char = Char(r) fmt.Printf("%d %q %c\n", c2, c2, c2) 

Salida (pruébalo en el área de juegos Go ):

 97 'a' a 22 '\x16' 

Para leer más sobre las constantes y su filosofía, lea la publicación del blog: The Go Blog: Constants

Y un par de preguntas más (+ respuestas) que se relacionan y / o son interesantes:
Golang: desbordamiento interno en el propósito
¿Cómo realiza Go arithmetic en constantes?
Encontrar la dirección de constante en go
¿Por qué estos dos float64s tienen valores diferentes?
¿Cómo cambiar un número de float64 a uint64 de una manera correcta?
Escribir potencias de 10 como constantes de forma compacta