How is data stored in memory

Posted by nakago on Sun, 26 Dec 2021 14:11:03 +0100

Data storage

1. When lifting unsigned integer

int main()
{
	char a = -1;
	// -Binary bit of 1
	// Original code: 1000000000000001
	// Inverse code 11111111111111111111111111111111111111110
	// Complement 11111111111111111111111111111111111
	// Truncated 11111111 complement
	// The computer stores the complement 11111111
	// Integer promotion by sign bit
	//          11111111111111111111111111111111111111111111 complement 
	//          11111111111111111111111111111111111110 inverse code
	//          1000000000000001 original code - 1
	signed char b = -1;

	unsigned char c = -1;
	//11111111
	//Integer promotion because 1111111 is an unsigned bit, integer promotion is filled with 0
	// 00000000000000000000000011111111 
	// Unsigned integer does not have the concept of original complement inverse code, because it is unsigned and equivalent to storing complement
 
	printf("a=%d,b=%d,c=%d", a, b, c);//-1 -1  255

	return 0;
}
int main()
{
	char a = -128;
	// Original code 1000000000000000000000000000000000000000
	// Inverse code 1111111111111111111111101111111
	// Original code 1111111111111111111111111110000000
	// Truncation 1000000
	// Integer lift 11111111111111111111111111111111110000000

	printf("%u\n", a);
	return 0;
 }
int main()
{
	char a = 128;
	//000000000000000000000000100000000
	//10000000
	//111111111111111111111111100000000
	printf("%u\n", a);
	return 0;

}

2. Integer lifting

The integer arithmetic operation of C is always performed at least with the precision of the default integer type.
To achieve this precision, the characters and short operands in the expression are converted to normal integers before use. This conversion is called integer
promote.

Significance of integer promotion

The integer operation of the expression shall be executed in the corresponding operation device of the CPU, and the byte length of the operand of the integer operator (ALU) in the CPU
Generally, it is the byte length of int and the length of the general register of CPU.
Therefore, even if the two char types are added, they actually have to be converted to the standard length of integer operands in the CPU when executed by the CPU
Degrees.
General purpose CPU (general purpose CPU) is difficult to directly realize the direct addition of two 8-bit bytes (although machine instructions
There may be such a byte addition instruction in). Therefore, all integer values whose length may be less than int length in the expression must be converted first
Change to int or unsigned int, and then send it to the CPU to execute the operation.
science and technology

Arithmetic conversion

Arithmetic conversion
If the operands of an operator belong to different types, unless one of the operands is converted to the class of the other operand
Type, otherwise the operation cannot be carried out. The following hierarchy is called ordinary arithmetic conversion.
long double
double
float
unsigned long int bottom up
long int
unsigned int
int
If the type of an operand is low in the above list, it must first be converted to the type of another operand and then run
Count.
Warning: but the arithmetic conversion should be reasonable, otherwise there will be some potential problems.
than

int main()

{
	int i = -20;
	// 10000000000 10100 original code
	// 1111111111111111111111111111101011
	// 1111111111111111111111111111101100 complement

	unsigned int j = 10;
	// 1111111111111111111111111111101100 complement converted to unsigned
//Add 10 0000000000000000001010 to the above
// 
	// 1111111111111111111111111111111111110110 complement
	// Because% d sees the above complement as the storage mode of signed integer, the original inverse complement operation is required
	// 1111111111111111111111111111111111111110101 inverse code
	// 
	// 100000000000001010 original code

	// -10 

	printf("%d\n", i + j);

	return 0;
}
#include<windows.h>
int main()
{
	unsigned int i;
    //Because i is an unsigned integer type, the cyclic judgment condition will treat the complement of i as an unsigned number every time, and the unsigned number is always greater than or equal to 0  
	for(i = 9; i >= 0; i--)
	{
		// 1000000000000000000000010
		// 1111111111111111111111101
		// 1111111111111111111111110
		// 
		printf("%u\n", i);
		Sleep(1000);
	}
	return 0;
}
int main()
{
    //The value range of char is - 127 ~ 128
    //128 binary represents 11111111 when it adds 1 to 100000000
    // Beyond the range of char type, truncation occurs, and the last 8 bits are 00000000
	char a[1000];
	int i;
	for (i = 0; i < 1000; i++)
	{ 
		a[i] = -1 - i;// -1 -2 ......-127 128 127 126......0

	}
	printf("%d", strlen(a));// Stop printing 255 when 0 is encountered
	return 0;
}
unsigned char i = 0;
int main()
{
	for (i = 0; i <= 255; i++)
	{
        // 255 + 1 truncation back to 0
		printf("hello world\n");
	}// Dead cycle
	return 0;
}

Floating point storage

IEEE754 standard

The international standard IEEE 754 specifies the representation of binary floating point number V = = = = = = for any binary floating point number, it must be expressed in the following form:

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

Where (- 1) ^ S represents the sign bit. When S=0, V is a positive number; when S=1, V is a negative number.

M represents a significant number, M is greater than 1 and less than 2, and 2^E represents an exponential bit.

IEEE 754 has some special provisions for the significant number m and index E. As mentioned earlier, 1 ≤ m < 2, that is, M can be written as 1 The form of xxxxxx, where xxxxxx represents the decimal part.
IEEE 754 stipulates that when saving M in the computer, the first digit of this number is always 1 by default, so it can be rounded off and only the following xxxxxx part is saved.

For example, when saving 1.01, only 01 is saved. When reading, add the first 1. The purpose of this is to save 1 significant digit.

Take the 32-bit floating-point number as an example. There are only 23 bits left for M. after rounding off the 1 of the first bit, it is equivalent to saving 24 significant digits.

Generally speaking, the storage method of M is to store only the number after the decimal point

If it is 0.1, it is necessary to move one bit to the left in storage

**IEEE754 stipulates: * * for 32-bit floating-point numbers, the highest bit is sign bit s, the next 8 bits are exponent E, and the remaining 23 bits are significant digits M.

As shown below:

[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-ZcDf6Fq9-1628663379407)(C:\Users \ original Yongkang \ Desktop \ screenshot \ screenshot 2021-08-10 220055.png)]

for example

Decimal 5.5 is expressed as

101.1 101 stands for binary 5 0.1. The 1 after the decimal point means 2 ^ (- 1) = = 0.5

101.1     1*2^2+0*2^1+1*2^0  The decimal point can be regarded as the power of 0   
    .1   1*2^-1
    .01  0*2^-1+1*2^-2

s=0

M=1.011

E=2 101.1 moved 2 bits to the right

Another example

9.0

1001.0

(-1)^0*1.001*2^3

The IEEE754 standard says that as long as the floating-point number V can be written into such an expression, the storage of V does not need to store the information of all binary numbers of the floating-point number

Just store its S M E

0.5

0.1

(-1)^0*1.0*2^-2

Storage mode of E

First, E is an unsigned int, which means that if E is 8 bits, its value range is 0 ~ 255; if E is 11 bits, its
The value range is 0 ~ 2047. However, we know that E in scientific counting can be negative, so IEEE 754 stipulates that e is true when stored in memory
The real value must be added with an intermediate number, which is 127 for 8-bit E; For an 11 bit e, the median is 1023. For example, e of 2 ^ 10
It is 10, so when saving as a 32-bit floating-point number, it must be saved as 10 + 127 = 137,

E+127=137, i.e. 10001001.

When E=-1 -1+127 =126, 126 is stored in memory as an unsigned integer

Index E is extracted from memory and divided into three cases

E is not all 0 or all 1

At this time, the floating-point number is represented by the following rule, that is, the calculated value of index E is subtracted by 127 (or 1023) to obtain the real value, and then the significant number M is preceded
Add the first 1. For example, the binary form of 0.5 (1 / 2) is 0.1. Since the positive part must be 1, that is, the decimal point is shifted to the right by 1 digit,
It is 1.0 * 2 ^ (- 1), and its order code is - 1 + 127 = 126, which is expressed as 01111110, while the mantissa of 1.0 is 0 after removing the integer part, and 0 to 23 bits are supplemented
00000000000000000000000000000, the binary representation is:

0 01111110 00000000000000000000000

s E M

E is all 0

At this time, the exponent E of the floating point number is equal to 1-127 (or 1-1023), which is the real value, and the significant number M is no longer added with the first 1, but restored to
0.xxxxxx decimal. This is done to represent ± 0 and small numbers close to 0.

E is all 1

At this time, if the significant digits M are all 0, it means ± infinity (infinity and infinitesimal)

(positive and negative depends on the sign bit s);

Stored E=255 real E 255-127 =128

1.XXX*2^128

± XXX*2^128

The following example

You can summarize what you have learned

int main()
{
	float f = 5.5f;
	//101.1
	//(-1)^0*1.011*2^2
	//s=0
	//M=1.011
	//E=2 +127 storage
	//0 10000001 0110000 000000000000000
//Hexadecimal 40 B0 00 00 4 bit s a hexadecimal number
	return 0; 
}
int main()
{
	int n = 9;
	//0 00000000 0000000000000001001
	//E is all zeros
	//E is directly 1-127 = - 126
	//M=0.0000000000000001001
	//0.0000000000000001001*2^-126
	//This is a very, very small number

	float* pFloat = (float*)&n;
	printf("n The value of is:%d\n", n);
	printf("*pFloat The value of is:%f\n", *pFloat);
	*pFloat = 9.0;
	//1001.0
	//(-1)^0*1.001*2^3
	//S=0
	//E=3
	//M=1.001
	//01000000100001
	printf("num The value of is:%d\n", n);
	printf("*pFloat The value of is:%f\n", *pFloat);
	return 0;
}//The value of n is: 9
//*The value of pFloat is: 0.000000
//The value of num is 1091567616
//*

/This is a very, very small number

float* pFloat = (float*)&n;
printf("n The value of is:%d\n", n);
printf("*pFloat The value of is:%f\n", *pFloat);
*pFloat = 9.0;
//1001.0
//(-1)^0*1.001*2^3
//S=0
//E=3
//M=1.001
//01000000100001
printf("num The value of is:%d\n", n);
printf("*pFloat The value of is:%f\n", *pFloat);
return 0;

}//The value of n is: 9
//The value of pFloat is: 0.000000
//The value of num is 1091567616
//