[C] A brief discussion on the storage of integers and floating point numbers in memory


Note: The IDE selected for this article is Microsoft Visual Studio 2022

1. Storage and reading of integers in memory

1.1 Range of integer types

First of all, I divide the integer types into two types: char and int.
Among them, char can be divided into unsigned char and char types.
int can be divided into short, unsigned short, int, unsigned int, long, unsigned long and longlong.
The range is as follows [^1]:

type minimum range take up space
char -128~127 1 byte
unsigned char 0~255 1 byte
short -32768~32767 2 byte
unsigned short 0~65535 2byte
int -2147483648~2147483647 4 byte
unsigned int 0~42949675 4 byte
long -2147483648~2147483647 4 byte
unsigned long 0~42949675 4 byte
long long -9223372036854775808~ 9223372036854775808 8 byte

1.2 Storage and reading process of integer type

1.2.1 Storage of integer types

First of all, we assume that everyone knows that when a computer stores a number, it stores the complement of the number. For students who are unclear about the original code, reverse code, and complement code, please search for information by yourself based on keywords.
First let's look at a piece of code:

#include <stdio.h>
int main ()
{
    
    
	int a = 1;
	printf("a=%d\n",a);
	return 0;
}

We can know that a is a variable of type int. The number can be represented as 0x00000001 in hexadecimal. When we save it, the int type can also receive 4 bytes of data. So, we, when we print the data, we can see that a=1;

1.2.2 Reading of integer types

Let's look at the next piece of code:

#include <stdio.h>
int main ()
{
    
    
	char a = -129;
	printf("a=%d\n",a);
	return 0;
}

If we run it we will find that the output is a=127. It does not match the -129 we assigned, so we need to analyze it carefully.

#include <stdio.h>
int main ()
{
    
    
	char a = -129;
	//存储过程
	//-129的原码1000 0000 0000 0000 0000 0000 1000 0001
	//-129的反码1111 1111 1111 1111 1111 1111 0111 1110
	//-129的补码1111 1111 1111 1111 1111 1111 0111 1111
	//a实际存的值(a只能存1个字节的),此时发生截断:0111 1111
	//当我们打印时,即是读取过程,此时发生整形提升
	//a打印的值补码为0000 0000 0000 0000 0000 0000 0111 1111 
	//符号位为0识别为正数
	//a打印的值原码为0000 0000 0000 0000 0000 0000 0111 1111
	//转化位10进制为127
	printf("a=%d\n",a);
	return 0;
}

To sum up, it is to save (original code-complement code-complement code-store) and take out (remove-complement code-complement code-original code). This is the process, and whether truncation or shaping improvement occurs will be discussed according to the specific situation.

Storage and reading of floating point numbers in memory

2.1 Range of floating point number types

First of all, floating point numbers are divided into four types, float, long float, double, and long double.
Because floating point numbers have decimals, floating point numbers must be processed before they can be stored in memory.
According to the international standard IEEE (Institute of Electrical and Electronic Engineering) 754, any binary floating point number V can be expressed in the following form [^2]:

V = (1^s * M ∗ 2^E 
//• (−1)S 表⽰符号位,当S=0,V为正数;当S=1,V为负数
//• M表⽰有效数字,M是⼤于等于1,⼩于2的
//• 2E 表⽰指数位

For example:
5.0 in decimal is 101.0 in binary, which is equivalent to 1.01×2^2.
Then, according to the format of V above, it can be concluded that S=0, M=1.01, and E=2.
-5.0 in decimal is -101.0 when written in binary, which is equivalent to -1.01×2^2. Then, S=1, M=1.01, E=2.
IEEE754 stipulates:
For 32-bit floating point numbers, the highest 1 bit stores the sign bit S, the next 8 bits store the exponent E, and the remaining 23 bits store the significant number M. For
64-bit floating point numbers, the highest 1 bit stores The sign bit S, the next 11 bits store the exponent E, and the remaining 52 bits store the significant number M
as shown below:
float memory allocation
Insert image description heredouble memory allocation
Insert image description hereThe range of floating point numbers is no longer specified here, because the formula has been given. It should be noted that the representation range of floating point numbers is limited and there are representation errors. Rounding errors and loss of precision may occur when performing calculations over very large or small numerical ranges.

2.2 Storage and reading process of floating point types

2.2.1 Stored procedures of floating point types

[^2] As mentioned before, if 1<=M<2, M is expressed in the form of 1.xxxxxxxx, where xxxxxxxx indicates the decimal part.
IEEE 754 stipulates that when M is stored inside the computer, the first digit of this number is always 1 by default. Therefore, you can put 1 aside first and save only the decimal part. When we need to read, add 1. This way we can have one more space for a valid number.
For E, it is stipulated as follows: From the above expression, we can see that floating point numbers are stored in scientific notation. When stored in scientific notation, the value of the exponent part can be a positive number or negative number. IEE 754 stipulates that the real value of E stored in memory must be added to an intermediate number. For float type, the intermediate number is 127; for double type, the intermediate number is 1023. For example: 2^13, this
E is originally 13, When saving a floating point number, it must be saved as 13+127=140, which is 10001100.

2.2.2 Reading process of floating point type

For data in scientific notation, the main determinant of its size is the exponent part n in 2^n. In the same way, when we read binary floating point numbers, we should mainly grasp the reading of E.
In the first case, when E is all 0, we need to know that we add 127 (or 1023) to get the value of E. You can imagine how small the original number is when it is saved. At this time, the effective digit M is no longer added with the first 1, but is reduced to a decimal of 0.xxxxxx. This is done to represent ±0, as well as very small numbers.
In the second case, E is not all 0 or not all 1. This is a normal read. At this time, we only need to subtract 127 (or 1023) from the calculated value of E to get the real value, and then add the first 1 in front of the significant number M. For example: when
we save float 5.5, S should be 0. M should be 01100000000000000000000, and E should be 2+127 (middle value) = 129. When stored, it is 0100 0000 1011 0000 0000 0000 0000 0000, which is (-1)^0 (sign bit S) * 011 0000 0000 0000 0000 0000(1011 (the value of M from 1011 changes to 011, followed by 0) * 2^1000 0001 (E has a value of 129) When we read, S remains unchanged, E is subtracted from 127, and 1 is added to the beginning of M, which should mean S is 0, E changes to 2, which is 00000010, and M is restored to 111 0000 0000 0000 0000 0000 .That is,
V = (−1)^0 * 1.011∗ 2^2=5.5.

In the third case, at this time, if the significant digits M are all 0, it means ± infinity (the sign bit depends on the sign bit s);

References:
[^1].https://blog.csdn.net/Dejan520/article/details/124837715
[^2]. Bit employment courseware, the way data is stored in memory

Guess you like

Origin blog.csdn.net/BlankXiangzw/article/details/132898628