Types numériques en Golang

GolangBeginner
Pratiquer maintenant

Introduction

Bienvenue, Gophers, dans ce nouveau chapitre. Dans cette section, nous allons découvrir les types numériques. Le contenu comprend les types entiers couramment utilisés, les types à virgule flottante, les types booléens, ainsi que les nombres complexes et la syntaxe des valeurs littérales introduite dans la version 1.13.

Points clés (Knowledge Points):

  • Types entiers (Integer types)
  • Types à virgule flottante (Floating-point types)
  • Types booléens (Boolean types)
  • Nombres complexes (Complex numbers)
  • Syntaxe des valeurs littérales (Literal value syntax)

Entiers (Integers)

Les entiers peuvent être globalement divisés en deux catégories : les entiers non signés (unsigned integers) et les entiers signés (signed integers). Les entiers signés sont les plus largement utilisés.

Non signé (Unsigned) signifie qu'il ne peut représenter que des nombres non négatifs (0 et les nombres positifs), tandis que les nombres signés (signed) peuvent représenter à la fois les nombres négatifs et non négatifs.

Les entiers non signés peuvent être divisés en quatre tailles : 8 bits, 16 bits, 32 bits et 64 bits, représentés respectivement par uint8, uint16, uint32 et uint64. Les entiers signés correspondants sont int8, int16, int32 et int64. Le tableau suivant indique les différentes plages représentées par chaque type :

Type Description Plage (Range)
uint8 Entier non signé 8 bits (8-bit unsigned int) 0 à 255
int8 Entier signé 8 bits (8-bit signed int) -128 à 127
uint16 Entier non signé 16 bits (16-bit unsigned int) 0 à 65535
int16 Entier signé 16 bits (16-bit signed int) -32768 à 32767
uint32 Entier non signé 32 bits (32-bit unsigned int) 0 à 4294967295
int32 Entier signé 32 bits (32-bit signed int) -2147483648 à 2147483647
uint64 Entier non signé 64 bits (64-bit unsigned int) 0 à 18446744073709551615
int64 Entier signé 64 bits (64-bit signed int) -9223372036854775808 à 9223372036854775807

Prenons uint8 et int8 comme exemples. Ce sont tous deux des entiers de 8 bits et peuvent représenter 256 valeurs. Dans le type entier non signé uint8, la plage qu'il peut représenter va de 0 à 255, tandis que dans le type entier signé int8, la plage qu'il peut représenter va de -128 à 127.

En plus des 8 types ci-dessus, il existe trois autres types d'entiers spéciaux, uint, int et uintptr, où uint et int peuvent représenter différentes plages sur différentes plateformes, et uintptr est utilisé pour stocker les adresses de pointeur.

| Type | Plage (Range) the current environment is %d bits\n", unsafe.Sizeof(a)*8)

var b int8 = 125
// Use the %d placeholder in fmt.Printf to output the value of the integer
// Use the %T placeholder in fmt.Printf to output the type of the variable
fmt.Printf("The value of b is %d, and the type is %T\n", b, b)

// Integer operations
// Declare integers c and d, and calculate their sum
c, d := 2, 3
fmt.Printf("c + d = %d\n", c+d)
// 10 - 5
fmt.Printf("10 - 5 = %d\n", 10-5)
// 8 * 10
fmt.Printf("8 * 10 = %d\n", 8*10)

// Example of uintptr usage - storing an address
var ptr uintptr
x := 42
// Convert the pointer to x to uintptr
ptr = uintptr(unsafe.Pointer(&x))
fmt.Printf("The address of x stored in ptr is: %v\n", ptr)

}

After executing the program, we get the following output:

```bash
go run integer.go
The type int in the current environment is 64 bits
The value of b is 125, and the type is int8
c + d = 5
10 - 5 = 5
8 * 10 = 80
The address of x stored in ptr is: 824634818784

Explanation of the output:

  • The type int in the current environment is 64 bits: This line shows that the int type on the system where the code is being run is 64 bits (or 8 bytes), indicating it is an int64. The unsafe.Sizeof(a) returns the size of variable a in bytes, and multiplying by 8 converts it to bits. This means that the int type can hold larger integer values.
  • The value of b is 125, and the type is int8: Here, we have declared a variable b of type int8 and assigned it the value 125. The output confirms this by showing both the value and the data type.
  • c + d = 5, 10 - 5 = 5, 8 * 10 = 80: These lines showcase basic integer arithmetic operations: addition, subtraction, and multiplication. The output confirms the correct results of these calculations.
  • The address of x stored in ptr is: 824634818784: This demonstrates how uintptr can be used to store a memory address. We're converting a pointer to the integer variable x to a uintptr type. The actual address value will vary each time the program runs. This is an advanced use case typically found in unsafe operations and system programming.

In this file, the unsafe.Sizeof() function can be used to obtain the number of bytes occupied by the current variable type. 1 byte (byte) is equal to 8 bits, so unsafe.Sizeof()*8 can obtain the number of bits occupied by the type. From the output, we can see that the online environment is 64 bits. The actual type of int in the online environment is int64.

We can use the following command in the terminal to determine the current system architecture:

dpkg --print-architecture
amd64

Nombres à virgule flottante

Les nombres à virgule flottante représentent les nombres réels avec des parties fractionnaires en Go en utilisant deux types : float32 et float64. Le type de virgule flottante par défaut est float64.

float32 et float64 représentent différentes précisions. La précision par défaut de float64 est supérieure à celle de float32.

La norme IEEE 754 est la norme de calcul à virgule flottante la plus largement utilisée dans les ordinateurs, et les CPU modernes prennent tous en charge cette norme. Comme d'autres langages de programmation, Go utilise également IEEE 754 pour stocker les nombres à virgule flottante.

Nous savons qu'un octet (byte) dans un ordinateur peut stocker 8 bits. float32 est un nombre à virgule flottante simple précision et occupe 4 octets, soit 32 bits. float64 est une double précision et occupe 8 octets, soit 64 bits.

Dans float32, le bit de signe occupe 1 bit, l'exposant occupe 8 bits et les 23 bits restants sont utilisés pour représenter la mantisse.

Dans float64, le bit de signe occupe également 1 bit, l'exposant occupe 11 bits et les 52 bits restants sont utilisés pour représenter la mantisse.

La valeur maximale que float32 peut représenter est d'environ 3.4e+38 en notation scientifique, et la valeur minimale est de 1.4e-45. La valeur maximale que float64 peut représenter est d'environ 1.8e+308, et la valeur minimale est de 4.9e-324. Nous pouvons voir que la plage de valeurs à virgule flottante peut aller de très petite à très grande.

Nous pouvons utiliser la constante math.MaxFloat32 pour représenter la valeur maximale dans float32. Utilisez la constante math.MaxFloat64 pour représenter la valeur maximale dans float64.

Représentation des nombres à virgule flottante (Representation of Floating-Point Numbers)

Lors de l'affichage des nombres à virgule flottante, nous pouvons utiliser le marqueur de substitution (placeholder) %f de la fonction Printf du package fmt. Voici un exemple :

cd ~/project
touch float.go
package main

import (
    "fmt"
    "math"
)

func main() {
    // Output without exponential form
    fmt.Printf("2.333 without exponential form: %f\n", 2.333)
    fmt.Printf("Pi without exponential form: %f\n", math.Pi)
    // Use %.2f to keep two decimal places for Pi
    fmt.Printf("Pi with two decimal places: %.2f\n", math.Pi)
    fmt.Printf("The maximum value of float32: %f\n", math.MaxFloat32)
    // Exponential form
    fmt.Printf("2.333 in exponential form: %e", 2.333)
}

Exécutez la commande et vous verrez la sortie suivante.

go run float.go
2.333 without exponential form: 2.333000
Pi without exponential form: 3.141593
Pi with two decimal places: 3.14
The maximum value of float32: 340282346638528859811704183484516925440.000000
2.333 in exponential form: 2.333000e+00

Explication de la sortie (Explanation of the output):

  • 2.333 without exponential form: 2.333000: Ceci montre le nombre 2.333 imprimé en utilisant le marqueur de substitution %f. Par défaut, le marqueur de substitution %f affiche 6 chiffres après la virgule, donc 2.333 devient 2.333000.
  • Pi without exponential form: 3.141593: Cette ligne affiche la valeur de la constante math.Pi, qui est une approximation de la constante mathématique π. Le marqueur de substitution %f l'affiche avec une précision totale.
  • Pi with two decimal places: 3.14: En utilisant %.2f, nous indiquons à fmt.Printf de formater le nombre à deux chiffres après la virgule, ce qui donne 3.14. Ceci est très utile lorsque vous n'avez besoin que d'une certaine précision pour la sortie.
  • The maximum value of float32: 340282346638528859811704183484516925440.000000: Ceci montre la valeur maximale qu'un float32 peut représenter. Notez que c'est un très grand nombre, et lorsqu'il est imprimé avec %f, il sera représenté avec de nombreux chiffres.
  • 2.333 in exponential form: 2.333000e+00: Cette ligne montre comment représenter un nombre à virgule flottante en notation exponentielle (scientifique) en utilisant le marqueur de substitution %e. Le e+00 à la fin indique que nous multiplions 2.333000 par 10 à la puissance de 0, ce qui est simplement 2.333. Si l'exposant était e+02, alors le nombre serait multiplié par 10^2 (100), ce qui donnerait 233.3.

Types Booléens

Le type bool n'a que deux valeurs possibles : true ou false, avec false étant la valeur par défaut. Il possède les caractéristiques suivantes :

  • Il ne peut pas être converti en d'autres types, comme convertir un entier en booléen ou convertir un booléen en entier.
  • Il ne peut pas participer à des opérations arithmétiques.

Les types booléens sont généralement utilisés conjointement avec des opérateurs relationnels, tels que =, >, et <. Créons un fichier nommé bool.go pour voir une démonstration :

cd ~/project
touch bool.go
package main

import (
    "fmt"
)

func main() {
    // Use the %t placeholder in fmt.Printf to represent a boolean value
    fmt.Printf("Is 3 equal to 2? %t\n", 3 == 2)
    fmt.Printf("Is 2 equal to 2? %t\n", 2 == 2)

    // Determine whether a and b are equal
    a, b := 1, 2
    fmt.Printf("a is %d, b is %d\n", a, b)
    if a > b {
        fmt.Println("a is greater than b")
    } else if a == b {
        fmt.Println("a is equal to b")
    } else {
        fmt.Println("b is greater than a")
    }
}

Après avoir exécuté le programme, nous obtenons la sortie suivante :

go run bool.go
Is 3 equal to 2? false
Is 2 equal to 2? true
a is 1, b is 2
b is greater than a

Explication de la sortie (Explanation of the output):

  • Is 3 equal to 2? false: L'expression 3 == 2 est évaluée à false, qui est ensuite imprimée en utilisant le marqueur de substitution %t.
  • Is 2 equal to 2? true: L'expression 2 == 2 est évaluée à true, qui est ensuite imprimée en utilisant le marqueur de substitution %t.
  • a is 1, b is 2: Cette ligne affiche les valeurs attribuées aux variables entières a et b.
  • b is greater than a: L'instruction conditionnelle if-else if-else est évaluée à a < b, qui est 1 < 2 (vrai), par conséquent l'instruction else est exécutée, ce qui entraîne l'impression par le programme de "b is greater than a".

Dans ce programme, nous utilisons d'abord %t dans fmt.Printf pour représenter une valeur booléenne, puis nous utilisons l'instruction if pour déterminer la relation entre deux valeurs. Dans fmt.Printf, nous pouvons utiliser le marqueur de substitution %d pour représenter un entier, le marqueur de substitution %f pour représenter un nombre à virgule flottante et le marqueur de substitution %t pour représenter une valeur booléenne.

Nombres Complexes

Go possède également des types de nombres complexes intégrés, qui peuvent être divisés en types complex64 et complex128. Dans complex64, les parties réelle et imaginaire sont toutes deux sur 32 bits, tandis que dans complex128, les parties réelle et imaginaire sont toutes deux sur 64 bits. Nous pouvons facilement effectuer des opérations sur les nombres complexes en Go.

Créez un fichier nommé complex.go et entrez le code suivant :

cd ~/project
touch complex.go
package main

import (
    "fmt"
)

func main() {
    // Initialize complex numbers in different ways
    c1 := complex(3, 1)
    c2 := 4 + 5i

    // Complex number operations
    c3 := c1 + c2
    c4 := c1 * c2
    // Use the real() function to obtain the real part of a complex number, and the imag() function to obtain the imaginary part of a complex number
    fmt.Printf("The real part of c1 is %v, the imaginary part is %v\n", real(c1), imag(c1))
    // %v in fmt.Printf can be used to represent complex numbers
    fmt.Printf("c1 + c2 is %v\n", c3)
    fmt.Printf("c1 * c2 is %v\n", c4)
}

Après avoir exécuté le programme, nous obtenons la sortie suivante :

go run complex.go
The real part of c1 is 3, the imaginary part is 1
c1 + c2 is (7+6i)
c1 * c2 is (7+19i)

Explication de la sortie (Explanation of the output):

  • The real part of c1 is 3, the imaginary part is 1: La fonction real(c1) extrait la partie réelle du nombre complexe c1, qui est 3, et imag(c1) extrait la partie imaginaire de c1, qui est 1. Ces valeurs sont ensuite imprimées en utilisant le marqueur de substitution %v.
  • c1 + c2 is (7+6i): Cette ligne montre le résultat de l'addition des nombres complexes c1 et c2. c1 est défini comme 3 + 1i et c2 comme 4 + 5i. L'addition des parties réelle et imaginaire séparément donne (3 + 4) + (1 + 5)i, soit 7 + 6i.
  • c1 * c2 is (7+19i): Cette ligne montre le résultat de la multiplication des nombres complexes c1 et c2. (3 + 1i) * (4 + 5i) est calculé comme (3*4 - 1*5) + (3*5 + 1*4)i, ce qui se simplifie en (12-5) + (15+4)i, et finalement en 7+19i.

Dans ce programme, nous avons démontré comment initialiser et effectuer des opérations sur des nombres complexes en utilisant la fonction complex et les opérateurs + et *. real() et imag() sont utilisés pour extraire respectivement les parties réelle et imaginaire d'un nombre complexe. Le verbe %v dans fmt.Printf est utilisé comme un marqueur de substitution général pour afficher les nombres complexes.

Syntaxe des Valeurs Littérales

Dans la version 1.13, Go a introduit la Numeric Literal Syntax (syntaxe des littéraux numériques). Elle définit la représentation des nombres dans différents systèmes numériques. Examinons ses règles.

  • Binaire (Binary): Ajoutez 0b avant l'entier. Par exemple, 0b101 équivaut à 5 en décimal.
  • Octal (Octal): Ajoutez 0o ou 0O avant l'entier. Par exemple, 0o11 équivaut à 9 en décimal.
  • Hexadécimal (Hexadecimal): Ajoutez 0x ou 0X avant l'entier. Par exemple, 0x1b équivaut à 27 en décimal.
  • Utilisez _ pour séparer les chiffres dans l'entier, par exemple, 0b1000_0100_0010_0001 équivaut à 0b1000010000100001. Cela peut améliorer la lisibilité.

Illustrons cela en détail :

cd ~/project
touch literals.go
package main

import "fmt"

func main() {
    // Binary, add 0b at the beginning
    var a int = 0b101
    fmt.Printf("Binary a is %b, decimal is %d\n", a, a)

    // Octal, add 0o or 0O at the beginning
    var b int = 0o11
    fmt.Printf("Octal b is %o, decimal is %d\n", b, b)

    // Hexadecimal, add 0x or 0X at the beginning
    var c int = 0x1b
    fmt.Printf("Hexadecimal c is %x, decimal is %d\n", c, c)

    // Use separators
    d := 0b1000_0100_0010_0001
    e := 0b1000010000100001
    if d == e {
        fmt.Println("d is equal to e")
    }
}

Après avoir exécuté le programme, nous obtenons la sortie suivante :

go run literals.go
Binary a is 101, decimal is 5
Octal b is 11, decimal is 9
Hexadecimal c is 1b, decimal is 27
d is equal to e

Explication de la sortie (Explanation of the output):

  • Binary a is 101, decimal is 5: Cette ligne montre la représentation binaire de 0b101 qui est 101, et son équivalent décimal est 5. Le marqueur de substitution %b affiche la valeur binaire de a, tandis que %d affiche la représentation décimale.
  • Octal b is 11, decimal is 9: Le nombre octal 0o11 équivaut à 1*8^1 + 1*8^0 = 8 + 1 = 9 en décimal. Le marqueur de substitution %o affiche la représentation octale de b, et %d affiche sa valeur décimale.
  • Hexadecimal c is 1b, decimal is 27: Le nombre hexadécimal 0x1b équivaut à 1*16^1 + 11*16^0 = 16 + 11 = 27 en décimal. Le marqueur de substitution %x affiche la représentation hexadécimale de c, et %d affiche sa valeur décimale.
  • d is equal to e: Ceci montre que le littéral binaire 0b1000_0100_0010_0001 est égal à 0b1000010000100001 en valeur. Les underscores (tirets bas) sont purement pour la lisibilité et ne modifient pas la valeur du nombre. La condition if est correctement évaluée à true.

Dans ce programme, nous avons démontré la déclaration et la sortie de différents systèmes numériques, ainsi que l'utilisation de séparateurs. Dans fmt.Printf, nous avons utilisé différents marqueurs de substitution pour représenter différents systèmes numériques. Par exemple, %b représente le binaire et %x représente l'hexadécimal. Les maîtriser améliorera l'efficacité de la programmation.

Résumé

Récapitulons ce que nous avons appris dans cette section :

  • Les entiers (Integers) peuvent être divisés en entiers signés (signed integers) et entiers non signés (unsigned integers)
  • Les types d'entiers par défaut int et uint ont des plages (ranges) qui dépendent de la plateforme
  • Représentation des nombres à virgule flottante (floating-point numbers)
  • Comment utiliser les nombres complexes (complex numbers)
  • Introduction de la syntaxe des valeurs littérales (literal value syntax)
  • Comment utiliser différents marqueurs de substitution (placeholders)

Dans cette section, nous avons expliqué et démontré les types d'entiers courants, les types à virgule flottante et les types booléens. Nous avons discuté de leurs tailles, de leurs plages et de leur utilisation. Nous avons également présenté les nombres complexes et les constantes littérales. Les types numériques sont la pierre angulaire des programmes Go, et il est important que les apprenants les étudient attentivement.