当前位置:网站首页>Storage of data (integer, floating-point, super detailed)

Storage of data (integer, floating-point, super detailed)

2022-06-10 03:17:00 Cloud C

Data type introduction

Basic built-in type :

char		// Character data type  // Occupy memory 1 Bytes 
short		// Short  // Occupy memory 2 Bytes 
int			// integer  // Occupy memory 4 Bytes 
long		// Long integer  // Occupy memory 4/8 Bytes 
long long	// Longer plastic surgery  // Occupy memory 8 Bytes 
float		// Single-precision floating-point  // Occupy memory 4 Bytes 
double		// Double precision floating point  // Occupy memory 8 Bytes 

The meaning of type :

  1. Type determines the amount of space the data will open up in memory
  2. Type determines the perspective of memory spatial data

Basic classification of types :

The integer family :

char
    signed char
    unsigned char
short
    signed short [int]
    unsigned short [int]
int 
    signed int
    unsigned int
long 
    signed long [int]
    unsigned long [int]
long long
    signed long long [int]
    unsigned long long [int]

Floating point family :

float
double

Construction type :

>  An array type 
>  Type of structure 	struct
>  Enumeration type 	enum
>  Joint type 	union

Pointer types :

char* pc;
short* ps;
int* pi;
long* pl;
long long* pll;
float* pf;
double* pd;
void* pv;
.
.
.

Empty type :

void Indicates empty type ( No type )

Usually applied to the return type of a function 、 The parameters of the function 、 Pointer types .

// first void Represents no return value 
// the second void Represents that no parameters are required 
void test1(void)
{
    
	;
}
//void* For accepting any type of pointer 
void test2(void* p)
{
    
	;
}

Shaping storage in memory

Variables are created to open up space in memory , The size of the open space is determined by the type of variable .

After the space is opened up , There must be a problem of how to store data

And in the computer , Integers are stored in the form of binary complements of the data .

Original code 、 Inverse code 、 Complement code

There are three binary representations of integers in a computer , The original code 、 Inverse and complement .

There are three ways of expression Sign bit and Value bits Two parts :

  1. Sign bit : It's all used 0 Express “ just ”, use 1 Express “ negative ”.

  2. Value bits :

    1. The original code of a positive number 、 Inverse code 、 The complement is the same .
    2. The three representations of negative numbers are different .

    Original code

    The original code can be obtained by directly translating the numerical value into binary in the form of positive and negative numbers .

    Inverse code

    Change the sign bit of the original code , The reverse code can be obtained by inverting other bits in turn .

    Complement code

    Inverse code + 1 You can get the complement .

For integers : Data stored in memory is actually stored in the complement

In computer system , All values are represented and stored by complements . The reason lies in , Use complement , Symbol bits and value fields can be treated in a unified way ;

meanwhile , Addition and subtraction can also be handled in a unified way (CPU Only adders ) Besides , Complement code and original code are converted to each other , Its operation process is the same , No need for additional hardware circuits .

example :

int a = 20;
 Decimal system :20
 Binary system :
     Original code :0000 0000 0000 0000 0000 0000 0001 0100( Positive numbers , Symbol bit 0)
     because 20 Positive number , So the original 、 Inverse code 、 The complement is the same 
     Inverse code :0000 0000 0000 0000 0000 0000 0001 0100
     Complement code :0000 0000 0000 0000 0000 0000 0001 0100( What is actually stored is the complement )

int a = -20;
 Decimal system :-20
 Binary system :
     Original code :1000 0000 0000 0000 0000 0000 0001 0100( negative , Symbol bit 1)
     because -20 It's a negative number , So follow the three code conversion rule for negative numbers 
     Inverse code :1111 1111 1111 1111 1111 1111 1110 1011( The sign bits remain the same , The other bits are reversed bit by bit )
     Complement code :1111 1111 1111 1111 1111 1111 1110 1100( Inverse code +1)( What is actually stored is the complement )

There are two ways to convert a complement to an original code :

  1. According to the original code - - -> The order of complement is reversed : Complement code -1 Get the inverse , The reverse sign bit remains unchanged , Other bits are reversed to get the original code .
  2. Or according to the original code - - -> The complement sequence is pushing forward : First, the sign bit remains unchanged , The other bits are reversed bit by bit , And then again +1 You get the original code .( This also explains the conversion between complement and source code , Its operation process is the same , No need for additional hardware circuits .)

When two integers are added and subtracted :

// Add up 
int a = 10;
int b = 20;
int c = a + b;
    a Complement :0000 0000 0000 0000 0000 0000 0000 1010 //10
    b Complement :0000 0000 0000 0000 0000 0000 0001 0100 //20
    c Complement :0000 0000 0000 0000 0000 0000 0001 1110 //30
// Subtracting the 
int a = 10;
int b = -20;
int c = a - b;// because CPU Only adders , So the actual implementation here is int c = a + (-b);
	a Complement :0000 0000 0000 0000 0000 0000 0000 1010 //10
   -b Complement :1111 1111 1111 1111 1111 1111 1110 1100 //-20
    c Complement :1111 1111 1111 1111 1111 1111 1111 0110 //-10
    // This explains why there is a complement 

stay VS2019 View memory storage in

image-20220524123802651

You can find , The order actually stored in memory is different from what we think . This involves the problem of the size end

Introduction to big and small end

What is big end 、 The small end :

Big end ( Storage ) Pattern , The low bit of data is stored in the high address of memory , And the high end of the data , Stored in a low address in memory ;

The small end ( Storage ) Pattern , The low bit of data is stored in the low address of memory , And the high end of the data , Stored in a high address in memory .

Why there are big end and small end :

Why are there big and small end patterns ? This is because in a computer system , We are in bytes , Each address corresponds to a byte , A byte is 8bit. But in C In language, except 8bit Of char outside , also 16bit Of short type ,32bit Of int Type and so on are greater than 8bit Data type of . in addition , For digits greater than 8 Bit processor , for example 16 Bits or 32 Bit processor , Because the register width is larger than one byte , So there must be a problem of how to arrange multiple bytes . So it leads to big end storage mode and small end storage mode .

for example : One 16bit Of short type x, The address in memory is 0x0010,x The value of is 0x1122, that 0x11 For high byte ,0x22 Is low byte . For big end storage mode , will 0x11 Put it in the low address , namely 0x0010 in ,0x22 Put it in a high address , namely 0x0011 in . The small end model , Just the opposite . That we use a lot x86 The structure is small end mode , and KEIL C51 It's the big end mode . A great deal of ARM,DSP It's all small end mode . There are some ARM The processor can also choose the big end mode or the small end mode by the hardware .

Big and small end exercises :

Design a program to judge the byte order of the current machine

#include<stdio.h>
void  CheckByteOrder()
{
    
    int a = 1;
    return *(char*)(&a);
}
int main()
{
    
    if(CheckByteOrder())
    {
    
        printf(" The small end \n");
    }
    else
    {
    
        printf(" Big end \n");
    }
    return 0;
}

explain :

The difference between the big endian byte order and the small endian byte order is nothing more than the two order of high and low bytes and high and low addresses , And because we take the low address , So we only need to know whether the low address stores low bytes or high bytes . Because we only need to know the byte information at the low address , Then we need to isolate the low address , So cast the extracted address into type and dereference it .

image-20220524153144854

practice

// What is the output result ?
#include <stdio.h>
int main()
{
    
    char a = -1;
    signed char b = -1;
    unsigned char c = -1;
    printf("%d %d %d\n", a, b, c);// The answers are -1、-1、255
    return 0;
}
// explain :
-1	 Original code 	10000000 00000000 00000000 00000001
     Inverse code 	11111111 11111111 11111111 11111110
     Complement code 	11111111 11111111 11111111 11111111
 Again because a yes char type , therefore a The complement of is  11111111
 use %d To output char Type a, Let's first look at the data type for integer promotion , Look again at the sign bit 
     because a yes char type , Generally, it defaults to signed char, So raise the original sign bit 
     Complement code 	11111111 11111111 11111111 11111111
 The sign bit after integer promotion is 1, It's a negative number , Convert to the original code and then output 
     Inverse code 	11111111 11111111 11111111 11111110
     Original code 	10000000 00000000 00000000 00000001
 therefore a The output is -1, because b and a The same is signed char, So it also outputs -1

unsigned char c The difference from the first two is , When integer lifting ,c Look at the data type 
 It's a sign free , So promote 0
     Complement code 	00000000 00000000 00000000 11111111
 And print %d Because the sign bit is 0, So I think it's a positive number , Print out the complement directly 
 therefore c Print out 255
// What is the output result ?
#include <stdio.h>
int main()
{
    
    char a = -128;
    printf("%u\n",a);// Output 4294967168
    printf("%d\n",a);// Output -128
    return 0;
}
// explain :
a	 Original code 	10000000 00000000 00000000 10000000
     Inverse code 	11111111 11111111 11111111 01111111
     Complement code 	11111111 11111111 11111111 10000000
 Again because a yes char type , therefore a The complement of is 	10000000
 When the output , First, integer promote according to the data type , because a yes char Consider signed 
 So lifting the sign bit is 1
     Complement code 	11111111 11111111 11111111 10000000
 use %u When the output , Consider unsigned , Direct output complement , Output 4294967168
 use %d When the output , Consider signed , First convert the bit source code , Then the output 
	 Inverse code 	11111111 11111111 11111111 01111111
     Original code 	10000000 00000000 00000000 10000000
     The original output code is -128

Floating point storage in memory

Common floating point numbers ( decimal )

3.14159

1E10( Scientific enumeration ) 1.0 × 1 0 10 1.0×10^{10} 1.0×1010

The family of floating-point numbers includes :float、double、long double type .

The range represented by floating point numbers :float.h In the definition of .

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

image-20220526094325940

The reason for this is that the storage rules and shaping of floating-point numbers in memory are completely different

Floating point storage rules

According to international standards IEEE( Institute of electrical and Electronic Engineering )754, Any binary floating point number V V V It can be expressed in the following form :

  • ( − 1 ) S × M × 2 E (-1)^S×M×2^E (1)S×M×2E
  • ( − 1 ) S (-1)^S (1)S The sign bit , When S = 0 S=0 S=0, V V V Is a positive number ; When S = 1 S=1 S=1, V V V It's a negative number .
  • M M M Represents a significant number , 1 ⩽ M < 2 1\leqslant M<2 1M<2
  • 2 E 2^E 2E Indicates the index bit .

for instance :

Decimal 15.0 15.0 15.0, According to the scientific counting method, it will be written as 1.5 × 1 0 1 1.5×10^1 1.5×101

The binary 15.0 15.0 15.0 Namely 1111.0 1111.0 1111.0, According to the scientific counting method, it will be written as 1.111 × 2 3 1.111×2^3 1.111×23

that , According to the above V V V The format of , We can draw S = 0 , M = 1.111 , E = 3 S=0,M=1.111,E=3 S=0,M=1.111,E=3

The binary − 15.0 -15.0 15.0 Namely − 1111.0 -1111.0 1111.0, Write in scientific notation − 1.111 × 2 3 -1.111×2^3 1.111×23

We can draw S = 1 , M = 1.111 , E = 3 S=1,M=1.111,E=3 S=1,M=1.111,E=3

IEEE 754 Regulations

about 32 Floating point number of bits , The highest bit is the sign bit S S S, And then 8 Bits are exponents E E E, The rest 23 Bits are significant numbers M M M.

image-20220526101501255

about 64 Floating point number of bits , The highest bit is the sign bit S S S, And then 11 Bits are exponents E E E, The rest 52 Bits are significant numbers M M M.

image-20220526101525647

IEEE 754 For significant figures M And the index E, There are some special rules :

As I said before , 1 ⩽ M < 2 1\leqslant M <2 1M<2, in other words , M M M It can be written. 1.xxxxxx In the form of , among xxxxxx Represents the fractional part .

IEEE 754 Regulations , Keep it in the computer M M M when , By default, the first digit of this number is always 1, So it can be discarded , Save only the back xxxxxx part . For example preservation 1.01 When , Save only 01, Wait until you read , Put the first 1 Add . The purpose of this is , Is to save one significant digit . With 32 For example, a floating-point number , Leave to M M M Only 23 position , Will come first 1 After giving up , It's equivalent to being able to save 24 Significant digits .

As for the index E, The situation is more complicated

First , E E E For an unsigned integer (unsigned int)

It means , If E E E by 8 position , Its value range is 0~255; If E E E by 11 position , Its value range is 0~2047. however , We know , In scientific counting E E E You can have negative numbers , therefore IEEE 754 Regulations , In memory E E E The true value of must be added with an intermediate number , about 8 Bit E E E, The middle number is 127; about 11 Bit E E E, The middle number is 1023. such as 2 10 2^{10} 210 Of E E E yes 10, So save it as 32 When floating-point numbers are in place , Must be saved as 10 + 127 = 137 10+127=137 10+127=137, namely 10001001.

then , Index E Fetching from memory can be further divided into three cases :

E Not all for 0 Or not all of them 1( General situation )

At this time , Floating point numbers are represented by this rule : The index E E E The calculated value of minus 127( or 1023), Get the real value , And then the significant number M M M Add the first 1.

such as :

When deposited ,0.5 The binary form of is 0.1, Since it is stipulated that the positive part must be 1, Move the decimal point one place to the right , Then for 1.0 × 2 − 1 1.0×2^{-1} 1.0×21, E E E by − 1 + 127 = 126 -1+127=126 1+127=126, Expressed as 01111110, and $M$1.0 Remove the integer part and make it 0, A filling 0 To 23 position 00000000000000000000000, Then its binary representation is :

0 01111110 00000000000000000000000

Take out the , Calculated value E E E01111110 subtract 127 Get the real value E = − 1 E=-1 E=1, And significant numbers M M M00000000000000000000000 add 1 Get the real value M = 1.0 M=1.0 M=1.0, And the sign bit S = 0 S=0 S=0

E E E All for 0( A special case )

At this time , Let the exponent of the floating point number E E E be equal to 1-127( perhaps 1-1023) That's the true value ,

Significant figures M M M No more first 1, It's reduced to 0.xxxxxx Decimals of . This is to show that ±0, And close to 0 A very small number of .

E E E All for 1( A special case )

At this time , If the significant number M M M All for 0, Express ± infinity ( The positive and negative depend on the sign S S S

Explain the code at the beginning :

9 Complement code 00000000 00000000 00000000 00001001
use %d When printing , Just print out directly and change the complement code to 9
use %f When printing , according to IEEE 754 The provisions of the , Divide the memory into three areas , Revert to scientific counting
The three areas are :S=0,E=00000000,M=00000000000000000001001
and E For all 0, Then order E=-126,M Do not add the first 1

Scientific enumeration : V = ( − 1 ) S × M × 2 E V=(-1)^{S}×M×2^{E} V=(1)S×M×2E namely V = ( − 1 ) 0 × 0.00000000000000000001001 × 2 − 126 V=(-1)^{0}×0.00000000000000000001001×2^{-126} V=(1)0×0.00000000000000000001001×2126 namely V = 1.001 × 2 − 146 V=1.001×2^{-146} V=1.001×2146

Obviously , V V V It's a very small one, close to 0 Positive number of , So the decimal number is 0.000000.

Storage 9.0 when , To binary first 1001.0, And then turn it into scientific counting 1.001 × 2 3 1.001×2^{3} 1.001×23.

namely S = 0 , E = 3 , M = 1.001 S=0,E=3,M=1.001 S=0,E=3,M=1.001 When deposited , E = 3 + 127 = 130 E=3+127=130 E=3+127=130, M M M Discarding whole digits 1

Stored in memory is 0 10000010 00100000000000000000000

While using %d When printing , Look at the sign bit 0, Print out the complement directly, that is 1091567616

use %f When printing , How to save it, how to take it out and print it , still 9.000000

原网站

版权声明
本文为[Cloud C]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/161/202206100303364980.html