[Linguagem C avançada 3 - armazenamento de dados (3) - armazenamento e recuperação de números de ponto flutuante na memória]


prefácio

Este artigo então aprende o conteúdo relacionado ao armazenamento de dados, principalmente aprendendo o armazenamento e recuperação de números de ponto flutuante na memória.


3. Armazenamento de ponto flutuante na memória

  • Números comuns de ponto flutuante: 3,14159, 1E10
  • A família de ponto flutuante inclui: tipos float, double, long double
  • O intervalo de números de ponto flutuante: definido em float.h

3.1 Exemplo de armazenamento de número de ponto flutuante

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

À primeira vista, a saída é: 9, 9,0, 9, 9,0

O resultado da execução é mostrado na figura a seguir:
insira a descrição da imagem aqui

3.2 Regras de armazenamento de ponto flutuante

num e *pFloat são obviamente o mesmo número na memória, mas os resultados da interpretação de números de ponto flutuante e inteiros serão muito diferentes.Para entender esse resultado, você precisa entender como os números de ponto flutuante são representados dentro do computador.

De acordo com o padrão internacional IEEE (Institute of Electrical and Electronics Engineering) 754, qualquer número V de ponto flutuante binário pode ser representado da seguinte forma:

  • (-1)^S * M * 2^E
  • (-1)^s representa o bit de sinal, quando s=0, V é positivo; quando s=1, V é negativo
  • M representa um número significativo, maior ou igual a 1, menor que 2
  • 2^E significa bit expoente

por exemplo:

  • 5.0 em decimal, escrito em binário é 101.0, que é equivalente a 1.01×2^2
  • De acordo com as regras de armazenamento acima, podemos obter s=0, M=1,01, E=2.
  • Decimal -5.0, escrito em binário é -101.0, equivalente a -1.01×2^2
  • De acordo com as regras de armazenamento acima, s=1, M=1,01, E=2

3.3 Regulamentos IEEE 754

  • Para um número de ponto flutuante de 32 bits, o 1 bit mais alto é o bit de sinal s, os próximos 8 bits são o expoente E e os 23 bits restantes são o significante M.
    insira a descrição da imagem aqui

  • Para números de ponto flutuante de 64 bits, o 1 bit mais alto é o bit de sinal S, os próximos 11 bits são o expoente E e os 52 bits restantes são o significante M.
    insira a descrição da imagem aqui

3.4 Disposições especiais do IEEE 754 para dígitos significativos M

  • No regulamento, 1≤M<2, ou seja, M pode ser escrito na forma de 1.xxxxxx, onde xxxxxx representa a parte fracionária
  • Ao salvar M no computador, o primeiro dígito desse número é sempre 1 por padrão, portanto, pode ser descartado, e apenas a parte xxxxxx seguinte é salva
  • Por exemplo, ao salvar 1.01, apenas 01 é salvo, e quando é lido, o primeiro 1 é adicionado. O objetivo disso é economizar 1 algarismo significativo
  • Tomando como exemplo um número de ponto flutuante de 32 bits, restam apenas 23 bits para M. Depois de arredondar o 1 no primeiro dígito, é equivalente a salvar 24 dígitos significativos.

3.5 Disposições especiais para o expoente E no IEEE 754

3.5.1 Armazenar na memória é um requisito do E

E é um inteiro sem sinal (int sem sinal)

  • Se E é de 8 bits, seu intervalo de valores é de 0 a 255
  • Se E for 11 bits, seu intervalo de valores é 0~2047

No entanto, E em notação científica pode ser negativo, então o IEEE 754 estipula que o valor real de E deve ser adicionado com um número intermediário quando armazenado na memória:

  • Para um E de 8 bits, o número do meio é 127
  • Para um E de 11 dígitos, o número do meio é 1023
  • Por exemplo, o E de 2^10 é 10, então quando é armazenado como um número de ponto flutuante de 32 bits, deve ser armazenado como 10+127=137, ou seja, 10001001

3.5.2 Regulagem de E ao buscar na memória

1. E não é todo 0 ou nem todo 1

  • O número de ponto flutuante é representado pelas seguintes regras, ou seja, o valor calculado do expoente E é subtraído de 127 (ou 1023) para obter o valor real, e então o número significativo M é somado com o primeiro 1
  • Por exemplo: a forma binária de 0,5 (1/2) é 0,1, pois a parte positiva deve ser 1, ou seja, a vírgula é deslocada para a direita em 1, então é 1,0*2^(-1), e seu código de pedido é -1+127= 126, representado como 01111110
  • Se a mantissa 1.0 remover a parte inteira como 0 e preencher de 0 a 23 dígitos de 00000000000000000000000, sua
    representação binária será 0 01111110 00000000000000000000000

2. E é todo 0

  • O expoente E do número de ponto flutuante é igual a 1-127 (ou 1-1023) é o valor real
  • O dígito significativo M não é mais adicionado com o primeiro 1, mas é reduzido a um decimal de 0,xxxxxx. Isso é feito para representar ±0, e números muito pequenos próximos de 0

3. E é todo 1

  • Se os dígitos significativos M forem todos 0, significa ± infinito (positivo ou negativo depende do bit de sinal s)

3.6 Exemplo 1

int main()
{
    
    
	float f = 5.5; //浮点数
	101.1 二进制表示
	(-1)^0 * 1.011* 2^2 IEEE 745规定
	s=0 //代表正数
	E=2  //代表指数,左移2位  ,存储是时要+127 =129
	M=1.011 //有效数字
	0 10000001 01100000000000000000000
	0100 0000 1011 0000 0000 0000 0000 0000
	0x40 b0 00 00 小端存储
}

Pode-se ver na análise acima que a forma de armazenamento do número de ponto flutuante 5.5 na memória é mostrada na figura a seguir: o
insira a descrição da imagem aqui
depurador descobriu que o resultado é consistente com o processo de análise e está armazenado em little endian.
insira a descrição da imagem aqui

3.7 Exemplo 2

int main()
{
    
    
	float f = 0.5; 浮点数
	0.1 二进制表示
	(-1)^0 * 1.0*2^-1 IEEE 745规定
	S = 0   代表正数
	M = 1.0  有效数字
	E = -1   代表指数,右移1位  ,存储是要+127 =126
	0 01111110 00000000000000000000000
	0011 1111 0000 0000 0000 0000 0000 0000
	0x3f 00 00 00
	
	return 0;
}

Pode-se ver a partir da análise acima que a forma de armazenamento do número de ponto flutuante 0,5 na memória é mostrada na figura a seguir: o
insira a descrição da imagem aqui
depurador descobriu que o resultado é consistente com o processo de análise e está armazenado em little endian.
insira a descrição da imagem aqui

3.8 Exemplo 3

O seguinte é um exemplo de 3.1 para explicar:

int main()
{
    
    
	int n = 9;
	第一步:正数9在内存存储的形式:
	00000000000000000000000000001001
	float *pFloat = (float*)&n;
	第二步:将正数强制转换位浮点型,认为pfloat指向的内容是浮点数
	存储在内存中的形式
	0 00000000 00000000000000000001001
	0000 0000 0000 0000 0000 1001
	0x00 00 00 09
	s=0
	E= -126  因为E是全为0的特殊情况,取出就是1-127固定的
	M= 0.00000000000000000001001 后面23位都是小数位
	第三步:从内存中取出浮点数
	(-1^0 * 0.00000000000000000001001 * 2^-126 
	结果为极限接近0的非常小的数
	printf("n的值为:%d\n",n); 输出9
	printf("*pFloat的值为:%f\n",*pFloat);输出浮点数0.00000
	
	*pFloat = 9.0;
	第一步:浮点数9.0的二进制形式:
	1001.0
	(-1)^0 * 1.001 * 2^3
	s=0
	E=3   代表指数,左移3位  ,存储是要+127 =130
	M=1.001
    第二步:浮点数9.0在内存存储的形式:
    0 10000010 00100000000000000000000
    0100 0001 0001 0000 0000 0000 0000
    0x41 10 00 00
    第三步:%d打印,上面的补码就是正数的补码了,三码合一
    打印原码: 1,091,567,616
	printf("num的值为:%d\n",n);
	printf("*pFloat的值为:%f\n",*pFloat); 9.0
	return 0;
}

Neste ponto, olhando para os resultados, ficará claro à primeira vista:

- Número de ponto flutuante de saída 0.00000, número de ponto flutuante no formulário de armazenamento de memória 0x00 00 00 09, formulário de armazenamento little endian:
insira a descrição da imagem aqui

  • Número positivo de saída 1.091.567.616, número positivo é armazenado na memória na forma de 0x41 10 00 00 e no formato de armazenamento little-endian:
    insira a descrição da imagem aqui
    o resultado de saída é consistente com a análise:
    insira a descrição da imagem aqui

3.9 Determine se dois números de ponto flutuante são iguais?

Dois números de ponto flutuante não podem ser julgados diretamente se são iguais ou não, deve-se julgar se a diferença entre eles está dentro de um determinado intervalo, que pode atender aos seus próprios requisitos de uso.

int main()
{
    
    
	int a = 0;
	if (a == 1)//整数可以直接判断
	{
    
    

	}
	float b = 0.00001;//基本接近0,但不是0
	if (b==0.0)//不能这样判断,会出问题
	{
    
    

	}
}

Resumir

O conteúdo de dados relacionado ao armazenamento é o primeiro ponto de conhecimento no estágio avançado da linguagem C. Está intimamente relacionado à melhoria da modelagem . Também é necessário estar familiarizado com tipos de variáveis, bits de sinal, intervalos de tipos, complemento de código original , etc. Esta parte do conteúdo é mais Tenha em mente a natureza do tipo de armazenamento variável, você não pode tomar os resultados como garantidos, e cada passo do pensamento deve ser baseado nele .

É necessário rever o antigo e aprender o novo e, através desta parte do estudo, fornecer-se uma ideia diferente no futuro programa para encontrar erros.

O próximo artigo começa a aprender o conteúdo avançado de ponteiros.

Acho que você gosta

Origin blog.csdn.net/taibudong1991/article/details/124045057
Recomendado
Clasificación