Numeri
Tipi numerici in Go
Go offre una vasta gamma di tipi numerici per gestire interi, numeri decimali e numeri complessi. Ogni tipo ha una dimensione precisa in bit, il che permette un controllo fine sull’utilizzo della memoria e sull’intervallo di valori rappresentabili.
Tipi interi con segno
I tipi interi con segno possono rappresentare numeri positivi e negativi:
package main
import "fmt"
func main() {
var a int8 = 127 // 8 bit: da -128 a 127
var b int16 = 32767 // 16 bit: da -32768 a 32767
var c int32 = 2147483647 // 32 bit: da -2^31 a 2^31-1
var d int64 = 9223372036854775807 // 64 bit: da -2^63 a 2^63-1
fmt.Println(a, b, c, d)
}
Il tipo int ha una dimensione che dipende dalla piattaforma: 32 bit su sistemi a 32 bit e 64 bit su sistemi a 64 bit. Nella maggior parte dei casi, int e il tipo da utilizzare per i numeri interi:
var numero int = 42 // 32 o 64 bit a seconda della piattaforma
Tipi interi senza segno
I tipi senza segno (uint) rappresentano solo numeri positivi (e zero), con un intervallo doppio rispetto alla controparte con segno:
package main
import "fmt"
func main() {
var a uint8 = 255 // 8 bit: da 0 a 255
var b uint16 = 65535 // 16 bit: da 0 a 65535
var c uint32 = 4294967295 // 32 bit: da 0 a 2^32-1
var d uint64 = 0 // 64 bit: da 0 a 2^64-1
fmt.Println(a, b, c, d)
}
Esiste anche il tipo uintptr, usato per memorizzare valori di puntatori come interi, utile principalmente nella programmazione di sistema.
Tipi speciali: byte e rune
Go definisce due alias importanti per i tipi interi:
package main
import "fmt"
func main() {
// byte e un alias per uint8
var b byte = 'A'
fmt.Printf("byte: %c = %d\n", b, b) // A = 65
// rune e un alias per int32
// rappresenta un code point Unicode
var r rune = 'Z'
fmt.Printf("rune: %c = %d\n", r, r) // Z = 90
// Le rune supportano caratteri Unicode
var emoji rune = '\u2764'
fmt.Printf("rune Unicode: %c\n", emoji)
}
Tipi floating-point
I numeri decimali (in virgola mobile) sono rappresentati da due tipi:
package main
import "fmt"
func main() {
var x float32 = 3.14 // precisione singola (32 bit)
var y float64 = 3.141592653589793 // precisione doppia (64 bit)
fmt.Printf("float32: %.10f\n", x) // perde precisione dopo ~7 cifre
fmt.Printf("float64: %.15f\n", y) // perde precisione dopo ~15 cifre
}
Il tipo float64 e il tipo predefinito per i numeri decimali e dovrebbe essere usato nella maggior parte dei casi per la sua maggiore precisione:
piGreco := 3.14159 // tipo float64 (inferito)
Attenzione alla precisione
I numeri in virgola mobile hanno problemi di precisione intrinseci alla loro rappresentazione binaria:
package main
import "fmt"
func main() {
a := 0.1
b := 0.2
c := a + b
fmt.Println(c) // 0.30000000000000004
fmt.Println(c == 0.3) // false!
// Per confronti sicuri, usa una tolleranza
const epsilon = 1e-9
fmt.Println(c-0.3 < epsilon) // true
}
Numeri complessi
Go supporta nativamente i numeri complessi con parte reale e parte immaginaria:
package main
import (
"fmt"
"math/cmplx"
)
func main() {
// Dichiarazione diretta
var c1 complex64 = 3 + 4i
var c2 complex128 = 5 + 12i
// Con la funzione complex()
c3 := complex(2.5, 7.3) // complex128
fmt.Println(c1) // (3+4i)
fmt.Println(c2) // (5+12i)
fmt.Println(c3) // (2.5+7.3i)
// Estrarre parte reale e immaginaria
fmt.Println(real(c2)) // 5
fmt.Println(imag(c2)) // 12
// Modulo di un numero complesso
fmt.Println(cmplx.Abs(c2)) // 13
}
Letterali numerici
Go supporta diverse notazioni per i letterali numerici:
package main
import "fmt"
func main() {
// Decimale
dec := 1000000
// Con separatore underscore (Go 1.13+)
grande := 1_000_000
fmt.Println(dec == grande) // true
// Binario (prefisso 0b)
bin := 0b1010 // 10 in decimale
fmt.Printf("Binario: %d\n", bin)
// Ottale (prefisso 0o)
ott := 0o17 // 15 in decimale
fmt.Printf("Ottale: %d\n", ott)
// Esadecimale (prefisso 0x)
hex := 0xFF // 255 in decimale
fmt.Printf("Esadecimale: %d\n", hex)
// Floating-point con esponente
sci := 1.5e3 // 1500.0
fmt.Printf("Scientifico: %f\n", sci)
// Esadecimale floating-point (Go 1.13+)
hexF := 0x1.0p10 // 1024.0
fmt.Printf("Hex float: %f\n", hexF)
}
Operazioni aritmetiche
Go supporta le operazioni aritmetiche standard per i numeri:
package main
import "fmt"
func main() {
a, b := 17, 5
fmt.Println("Somma: ", a+b) // 22
fmt.Println("Sottrazione:", a-b) // 12
fmt.Println("Prodotto: ", a*b) // 85
fmt.Println("Divisione: ", a/b) // 3 (divisione intera!)
fmt.Println("Resto: ", a%b) // 2
// Divisione decimale: almeno un operando deve essere float
x, y := 17.0, 5.0
fmt.Println("Divisione decimale:", x/y) // 3.4
}
Divisione intera
Un aspetto importante: la divisione tra interi restituisce un intero, troncando la parte decimale:
package main
import "fmt"
func main() {
fmt.Println(7 / 2) // 3 (non 3.5!)
fmt.Println(-7 / 2) // -3
// Per ottenere il risultato decimale
fmt.Println(float64(7) / float64(2)) // 3.5
}
Il pacchetto math
Il pacchetto math della libreria standard fornisce costanti e funzioni matematiche:
package main
import (
"fmt"
"math"
)
func main() {
// Costanti
fmt.Println("Pi:", math.Pi)
fmt.Println("E:", math.E)
fmt.Println("MaxInt64:", math.MaxInt64)
fmt.Println("MaxFloat64:", math.MaxFloat64)
// Funzioni comuni
fmt.Println("Sqrt(16):", math.Sqrt(16)) // 4
fmt.Println("Pow(2, 10):", math.Pow(2, 10)) // 1024
fmt.Println("Abs(-42):", math.Abs(-42)) // 42
fmt.Println("Ceil(3.2):", math.Ceil(3.2)) // 4
fmt.Println("Floor(3.8):", math.Floor(3.8)) // 3
fmt.Println("Round(3.5):", math.Round(3.5)) // 4
fmt.Println("Max(5, 9):", math.Max(5, 9)) // 9
fmt.Println("Min(5, 9):", math.Min(5, 9)) // 5
}
Conversioni tra tipi numerici
Go richiede conversioni esplicite tra tipi numerici diversi:
package main
import "fmt"
func main() {
var intero int = 42
var piccolo int8 = int8(intero) // possibile perdita di dati
var grande int64 = int64(intero)
var decimale float64 = float64(intero)
var indietro int = int(decimale) // tronca la parte decimale
fmt.Println(piccolo, grande, decimale, indietro)
// Attenzione all'overflow
var x int8 = 127
y := x + 1
fmt.Println(y) // -128 (overflow silenzioso!)
}
Conclusione
Go offre un sistema numerico completo e preciso. I tipi interi con e senza segno permettono di scegliere la dimensione giusta per ogni esigenza. I tipi floating-point gestiscono i numeri decimali con diversi livelli di precisione. I numeri complessi sono supportati nativamente. I letterali numerici supportano diverse basi e il separatore underscore per la leggibilita. Il pacchetto math completa l’offerta con funzioni matematiche essenziali.