Skip to main content

Command Palette

Search for a command to run...

Overflow silencioso em Go

Short

Updated
3 min read
Overflow silencioso em Go

Aprendi hoje com Go que, tipos são importantes, o que de certa forma é obvio dada a relação da linguagem com tipos, porém lendo o Go Programing Language do A. Donovan e B. Kernighan, lendo o capitulo 3 sobre tipos, na parte que aborda inteiros, me deparei com essa afirmação aqui.

Se o resultado de uma operação aritmética, seja com ou sem sinal, tiver mais bits do que possam ser representados no tipo do resultado, dizemos que há um transbordamento (overflow). Os bits de mais alta ordem que não couberem serão silenciosamente descartados

A segunda parte foi o que me chamou mais atenção,

Serão silenciosamente descartados

Isso me preocupou um pouco, e não que isso seja algo incomum, em outras linguagens como C++, Rust e afins, a mesma coisa acontece, decidi testar em go. O código:

package main

import "fmt"

func main() {
    var a int8 = 70
    var b int8 = 70
    c := a + b
    fmt.Printf("decimal  %d -> binario %08b\n", a, uint8(a))
    fmt.Printf("decimal %d -> binario %08b\n", b, uint8(b))
    fmt.Printf("decimal %d -> binario %08b\n", c, uint8(c))
}

Resulta em:

decimal 70 -> binario 01000110
decimal 70 -> binario 01000110
decimal -116 -> binario 10001100

Porém se fizermos a soma de 01000110 + 01000110 (70 + 70) o resultado é 10001100 que em decimal é, de fato, 140, porém o jump do cat está na forma como o compilador interpreta o resultado, estamos dizendo pra ele que esse resultado é um int8, ou seja, um inteiro com sinal, sendo assim o compilador lê esses bits como um número que deveria ter sinal, nesse caso negativo pois o bit mais a esquerda é 1. Se quisermos tirar a prova basta aplicarmos o complemento de dois nos bits do resultado, que consiste em inverter os bits e somar 1, ou seja pegamos 10001100 invertemos os bits 01110011 e somamos 1, resultando em 01110100 que é 116, e como o bit mais significativo (bit da esquerda) é 1, o número é negativo, ou seja -116.

O mesmo aconteceria se usarmos uint8 por exemplo e somarmos 200 + 100, o resultado seria um surpreendente 44, pois o valor máximo que um uint8 pode armazenar é 255, e o resultado 300 em binário é 100101100, ou seja, 9 bits, e o bit mais a esquerda (bit 8) seria silenciosamente descartado, sobrando apenas 00101100 que é 44 em decimal.

É um detalhe simples, e é o tipo de coisa que geralmente aprendemos na faculdade, mas que por vezes passa despercebido no dia a dia, pois geralmente usamos int que na maioria das arquiteturas resulta em um inteiro de 64 bits, muito longo para termos um overflow na maioria dos casos, porém é algo importante de termos guardado na memória e revisitarmos as vezes, dado que são detalhes como esse que podem causar bugs dificeis de serem detectados.