TD — Séance 2 — Représentation numérique
Bases, entiers, complément à deux, conversions, débordements, masques, IEEE 754
Lire et écrire des motifs de bits sans machine : changer de base, lire un dump little-endian avec différents types, prévoir une conversion, une extension, une troncature, un débordement, manipuler des champs de bits et encoder un flottant IEEE 754. Pour chaque réponse, nommer la règle appliquée. Machine x86-64 little-endian, Linux, gcc 13, options -std=c17 -Wall -Wextra : char 1 octet (signé), short 2, int et unsigned 4, long et size_t 8 ; entiers signés en complément à deux, flottants IEEE 754.
Exercice 1 : changements de base et complément à deux
Compléter le tableau (entiers non signés sur 8 bits) ; chaque ligne donne une seule des trois écritures.
binaire (8 bits) hexadécimal décimal 1110 01010x3d1720101 10110xc777Convertir 172 et 77 en binaire par divisions successives par 2, en écrivant chaque division et chaque reste.
Écrire
0x7e2den binaire sur 16 bits, puis \(1011\,0110\,0001\,1101_2\) en hexadécimal, sans passer par le décimal.Sur 8 bits, calculer \(-x\) par \(\mathord{\sim}x + 1\) pour \(x = 83\), \(x = 1\) et \(x = -128\), en écrivant \(x\), \(\mathord{\sim}x\) et \(\mathord{\sim}x + 1\) en binaire et en hexadécimal. Commenter la valeur obtenue pour \(-128\).
Calculer \(T2U_8(-73)\), \(U2T_8(201)\), \(T2U_{16}(-900)\) et \(U2T_{32}(2500000000)\).
Exercice 2 : un dump little-endian
Seize octets ont été relevés à partir de l’adresse 0x00 :
| Addr | +0 | +1 | +2 | +3 | +4 | +5 | +6 | +7 |
|---|---|---|---|---|---|---|---|---|
0x00 |
b5 |
4b |
e7 |
ff |
00 |
80 |
6b |
00 |
0x08 |
2e |
fb |
ff |
ff |
15 |
cd |
5b |
07 |
unsigned char *u = (unsigned char *)0x00; signed char *s = (signed char *)0x00;
unsigned short *uh = (unsigned short *)0x02; short *h = (short *)0x02;
unsigned int *un = (unsigned int *)0x08; int *n = (int *)0x08;u[0]ets[0].u[1]ets[1]; quel caractère ASCII ce code représente-t-il ('A'vaut 65) ?uh[0]eth[0], en hexadécimal puis en décimal.uh[1]eth[1]; que remarquez-vous ?h[2].un[0]etn[0].n[1], en hexadécimal puis en décimal.
Exercice 3 : extension et troncature
#include <stdio.h>
#include <stdint.h>
int main(void)
{
int8_t a = -41;
int16_t b = a;
uint16_t c = (uint8_t)a;
uint32_t d = a;
int e = 70000;
short f = (short)e;
unsigned char g = (unsigned char)e;
signed char h = (signed char)(e >> 8);
int k = 45000;
short m = (short)k;
printf("%d %u %u\n", b, c, d);
printf("%d %u %d %d\n", f, g, h, m);
return 0;
}Pour chaque variable, donner son motif en hexadécimal (sur la taille de son type) et sa valeur, en nommant la règle (extension par des zéros, extension de signe, troncature) et en disant si le résultat est défini ou défini par l’implémentation :
variable type hexadécimal valeur aint8_td7-41bint16_tcuint16_tduint32_teint0001117070000fshortgunsigned charhsigned charkint0000afc845000mshortÉcrire les deux lignes affichées.
Les variables
b,d,f,metesont rangées respectivement aux adresses0x00,0x04,0x08,0x0aet0x0c. Remplir le dump (les octets0x02et0x03valent00) :Addr +0 +1 +2 +3 +4 +5 +6 +7 0x000x08
Exercice 4 : débordements et comparaisons
- Poser en binaire les opérations suivantes sur
uint8_t, avec les retenues : \(185 + 90\), \(64 + 63\), \(3 - 10\). Donner chaque résultat, et dire si le tests < adétecte une retenue perdue, ou un emprunt pour la soustraction. Même question pour \(61000 + 7000\) suruint16_t. - Pour chaque ligne du programme suivant, dire si le résultat est défini, défini par l’implémentation ou indéfini, et donner la valeur quand elle existe.
#include <stdio.h>
#include <limits.h>
int main(void)
{
unsigned short p = USHRT_MAX;
p = p + 1;
short q = SHRT_MAX;
q = q + 1;
int r = INT_MIN;
r = r - 1;
unsigned int s = 0;
s = s - 2;
int t = 46341;
t = t * t;
long u = 46341L * 46341;
int v = -INT_MAX - 1;
printf("%u %d %d %u\n", p, q, r, s);
printf("%d %ld %d\n", t, u, v);
return 0;
}- Prévoir les six lignes affichées par le programme suivant, en donnant pour chaque comparaison le type dans lequel elle est faite. Quel avertissement gcc donne-t-il, et pour quelle ligne ?
#include <stdio.h>
int main(void)
{
int t = -5;
unsigned int n = 3;
unsigned short us = 3;
long d = t;
size_t L = 2;
printf("%d\n", t < n);
printf("%d\n", t < (int)n);
printf("%d\n", t < us);
printf("%d\n", d < n);
printf("%d\n", t + L > 0);
printf("%zu\n", t + L);
return 0;
}- La fonction suivante doit rendre 1 quand le nom dépasse
maxcaractères. Quel est le type delen - max? Que rend l’appelnom_trop_long("Gossas", 12), et pourquoi ? Corriger la condition.
#include <stdio.h>
#include <string.h>
int nom_trop_long(const char *nom, size_t max)
{
size_t len = strlen(nom);
if (len - max > 0)
return 1;
return 0;
}
int main(void)
{
printf("%d\n", nom_trop_long("Gossas", 12));
printf("%d\n", nom_trop_long("Ourossogui", 10));
printf("%zu\n", strlen("Gossas") - (size_t)12);
return 0;
}Exercice 5 : une date dans 16 bits
Le système de fichiers FAT range une date dans un uint16_t : les bits 15 à 9 donnent l’année depuis 1980, les bits 8 à 5 le mois, les bits 4 à 0 le jour.
- Écrire
0x2256en binaire, puis retrouver le jour, le mois et l’année. - Écrire les trois expressions C qui extraient le jour, le mois et l’année d’une date
d. - Encoder la date du 15 juin 2027 dans un
uint16_t e: donner l’expression C et la valeur en hexadécimal, puis les deux octets deedans un dump, de l’adresse la plus basse à la plus haute. - Écrire une expression qui remplace le mois de
epar 7 sans toucher aux autres champs, et donner le résultat. - Comment tester, avec un seul opérateur bit à bit, que le jour est impair ? Appliquer à
0x2256et àe.
Exercice 6 : IEEE 754
Encoder \(11{,}25\) et \(-0{,}375\) en
float: signe, binaire, forme normalisée, exposant biaisé, fraction, mot en hexadécimal.Un tableau
float t[4]est rangé en0x00sur une machine little-endian :Addr +0 +1 +2 +3 +4 +5 +6 +7 0x000000f442000020be0x08000040000100807fPour chaque élément, donner le mot de 32 bits, les champs \(s\), \(e\), \(f\), la catégorie (normalisé, dénormalisé, zéro, infini, NaN) et la valeur.
Donner le dump des 8 octets d’un
double x = 2.5;(biais 1023, 52 bits de fraction) :Addr +0 +1 +2 +3 +4 +5 +6 +7 0x00
Exercice 7 (approfondissement) : limites de la précision
#include <stdio.h>
int main(void)
{
float f = 16777216.0f;
float g = f + 1.0f;
printf("%.1f %d\n", g, g == f);
int tours = 0;
for (float x = 0.0f; x != 1.0f && tours < 20; x += 0.1f)
tours++;
printf("tours = %d\n", tours);
float x = 0.0f;
for (int k = 0; k < 10; k++)
x += 0.1f;
printf("%.9f %d\n", x, x == 1.0f);
return 0;
}- Pourquoi \(16777216 + 1\) n’est-il pas représentable en
float? Quel est le plus petit entier positif non représentable enfloat? Écrire la première ligne affichée. - La boucle
fors’arrêterait-elle sans le compteurtours? Prévoir la valeur affichée detours. - Prévoir la dernière ligne, et proposer un test qui reconnaît que
xvaut 1 « à l’arrondi près ».
Pour cette séance
Le cours
Les slides
À lire en ligne
À télécharger
- Code de départ du TP (ZIP, 8 Ko)