[Langage C] Stockage à virgule flottante en mémoire

Nous savons que l'entier est stocké en mémoire sous la forme de son complément à deux, alors comment la virgule flottante est-elle stockée en mémoire ?

Prenons un exemple de problème pour étudier ce problème :

#include <stdio.h>

int main()
{
	int n = 9;
	float* pFloat = (float*)&n;
	printf("n的值为:%d\n", n);
	printf("*pFloat的值为:%f\n", *pFloat);
	*pFloat = 9.0;
	printf("n的值为:%d\n", n);
	printf("*pFloat的值为:%f\n", *pFloat);
	return 0;
}

Vous pouvez essayer de faire cette question, quel est le résultat de sortie ?

résultat de l'opération :

Règles de stockage des nombres à virgule flottante

et  *pFloat  sont évidemment le même nombre en mémoire, pourquoi y a-t-il un si grand écart de sortie ?

Selon la norme internationale IEEE (Institute of Electrical and Electronics Engineering) 754 , tout nombre binaire à virgule flottante V peut être exprimé sous la forme suivante :
  • (-1)^S * M * 2^E
  • (-1)^S représente le bit de signe, lorsque S=0 , V est un nombre positif ; lorsque S=1 , V est un nombre négatif.
  • M représente un nombre valide, supérieur ou égal à 1 et inférieur à 2 .
  • 2^E signifie bits d'exposant.

Beaucoup de gens peuvent être confus quand ils voient cela, qu'est-ce que c'est ? En fait, c'est très simple, (-1)^S * M * 2^E  peut être compris comme une formule, ^ signifie puissance, par exemple,  2^E est 2 à la puissance E, par exemple :

(-1)^{S}*M*2^{E}

Decimal 5.0 , écrit en binaire est 101.0 , et la virgule décimale est avancée de deux places, ce qui équivaut à 1.01*2^{2}écrire selon la formule ci-dessus, vous pouvez obtenir : (-1)^{0}*1.01*2^{2}, S=0, M=1.01, E=2.

IEEE 754 stipule :

Pour les nombres à virgule flottante de 32 bits, le bit le plus élevé est le bit de signe S , les 8 bits suivants sont l'exposant E et les 23 bits restants sont le significande M.  

  Pour les nombres à virgule flottante de 64 bits, le bit le plus élevé est le bit de signe S , les 11 bits suivants sont l'exposant E et les 52 bits restants sont le chiffre significatif M.

IEEE 754 a des réglementations spéciales sur le chiffre significatif M et l'exposant E.

Comme mentionné précédemment, 1≤M<2, c'est-à-dire que M peut s'écrire sous la forme 1.xxxxxx, où xxxxxx représente la partie décimale.

IEEE 754 stipule que lorsque M est enregistré dans l'ordinateur, le premier chiffre de ce numéro est toujours 1 par défaut, il peut donc être ignoré, et seule la partie xxxxxx suivante est enregistrée. Par exemple, lors de l'enregistrement de 1.01, enregistrez uniquement 01, puis ajoutez le premier 1 lors de la lecture. Le but est d'économiser 1 chiffre significatif. En prenant le nombre à virgule flottante 32 bits comme exemple, il ne reste plus que 23 bits pour M. Une fois le premier 1 supprimé, cela équivaut à enregistrer 24 chiffres significatifs.

Quant à l'indice E, la situation est plus compliquée.

Premièrement, E est un entier non signé (unsigned int)

Cela signifie que si E est de 8 bits, sa plage de valeurs est de 0 à 255. Si E est de 11 bits, sa plage de valeurs est de 0 à 2047. Cependant, nous savons que E en notation scientifique peut apparaître des nombres négatifs, donc IEEE 754 stipule que lors du stockage de la valeur réelle de E dans la mémoire, un nombre intermédiaire doit être ajouté. Pour E 8 bits, le nombre intermédiaire est 127. Pour E 11 bits, le nombre intermédiaire est 1023. Par exemple, Le E de 2^ 10 est 10, donc lors de son enregistrement en tant que nombre à virgule flottante 32 bits, il doit être enregistré sous 10+127=137, soit 10001001.

Ensuite, l'indice E est extrait de la mémoire et peut être divisé en trois cas :

E n'est pas tout à 0 ou pas tout à 1

À ce moment, le nombre à virgule flottante est représenté par les règles suivantes, c'est-à-dire que la valeur calculée de l'exposant E est soustraite par 127 (ou 1023) pour obtenir la valeur réelle, puis le premier chiffre 1 est ajouté avant le nombre effectif M.

Par exemple:

La forme binaire de 0,5 est 0,1. Puisqu'il est stipulé que la partie positive doit être 1, c'est-à-dire que la virgule est décalée vers la droite de 1, alors c'est 1.0*2^(-1), et son code d'ordre est -1+127=126, qui est exprimé par 01111110, tandis que la mantisse 1.0 supprime la partie entière pour être 0, remplit 0 à 23 chiffres 000000000000000000000000, alors sa représentation binaire est :

0 01111110 00000000000000000000000

E est tout 0

A ce moment, l'exposant E du nombre à virgule flottante égal à 1-127 (ou 1-1023) est la valeur réelle, et le nombre effectif M n'est plus additionné du premier chiffre de 1, mais est restauré à un décimal de 0.xxxxxx. Ceci est fait pour représenter ± 0 et de très petits nombres proches de 0.

E est tout 1

A ce moment, si les chiffres significatifs M sont tous 0, cela signifie ± l'infini (positif ou négatif dépend du bit de signe S).

Voilà, c'est tout pour les règles de représentation des nombres à virgule flottante.

expliquer le sujet précédent

#include <stdio.h>

int main()
{
	int n = 9;
	float* pFloat = (float*)&n;
	printf("n的值为:%d\n", n);	//9
	//n的值没有被改变,所以还是9

	printf("*pFloat的值为:%f\n", *pFloat);	//0.000000
	//整型9在内存中的补码:00000000 00000000 00000000 00001001
	//*pFloat是一个单精度浮点型,以浮点型的方式取出9:
	//0 00000000 00000000000000000001001
	//S=0, E=00000000, M=0000000000000000001001
	//可见E为全0,按照前面讲的规则,E的真实值=1-127=-126,M的真实值=0.00000000000000000001001
	//套用前面的公式:(-1)^0 * 0.00000000000000000001001 * 2^-126
	//显然,这是一个非常小的数字,而打印时只能打印出小数点后6位,所以是0.000000

	*pFloat = 9.0;
	printf("n的值为:%d\n", n);	//1091567616
	//n的值通过*pFloat指针被重新赋为浮点型的9.0
	//9.0直接转为二进制 -> 1001.0,按规则把小数点提前3位 -> 1.001 * 2^3,
	//且9.0是正数,最终转换为:(-1)^0 * 1.001 * 2^3
	//S=0, E=3+127=130, M=1.001
	//那么,第一位的符号位S=0,指数E=3+127=130,写成二进制形式:10000010,有效数字M=001,后面补20个0凑满23位
	//所以,写成二进制形式应该是S+E+M,即:
	//0 10000010 00100000000000000000000000
	//把这个二进制数字转换为十进制正是1091567616

	printf("*pFloat的值为:%f\n", *pFloat);	//9.000000
	//浮点数以%f直接取出就是9.000000
	return 0;
}

Je suppose que tu aimes

Origine blog.csdn.net/m0_73156359/article/details/131014063
conseillé
Classement