C language operators, plastic promotion, truncation, and plastic floating-point storage

C language operators, plastic promotion and truncation

Preface

1. First, you need to understand the mechanism of integer storage;
2. The relationship between the original code, one's complement, and one's complement;
2. (1) The original code, one's complement, and one's complement of a positive integer are the same, so you can directly perform binary conversion; ( 2^32-1 positive integers)
2. (2) The first bit of the 32-bit negative integer is the sign bit (1 represents a negative number); (2^31-1 signs)
Let’s take an example, for example: -1
first Write out the complement of -1 as:
1000 0000 0000 0000 0000 0000 0000 0001 ----- the original code
Next, find the inverse code: the sign bit remains unchanged, and the other bits are inverted to get the inverted code
1111 1111 1111 1111 1111 1111 1111 1110 -----Inverse code
Finally, the inverse code +1 gets the complement, and all the complements stored in the memory are
1111 1111 1111 1111 1111 1111 1111 1111 1111 -----Complement
3. Shaping promotion, truncation

part 1. bitwise operators

(1) The bitwise AND operator &: If the binary digits of the two corresponding positions are both 1, the bit result is 1, otherwise it is 0. Look at the following example:

    int a = 15;   //0000 0000 0000 0000 0000 0000 0000 1111 ---15的二进制表示
    int b = 19;   //0000 0000 0000 0000 0000 0000 0001 0011 ---19的二进制表示
    int c = a & b;//0000 0000 0000 0000 0000 0000 0000 0011 ---a&b=3的二进制表示

(2) Bitwise OR operator |: If the binary digits of the two corresponding positions are both 0, the bit result is 0, otherwise it is 1. Look at the following example:

    int a = 15;   //0000 0000 0000 0000 0000 0000 0000 1111 ---15的二进制表示
    int b = 19;   //0000 0000 0000 0000 0000 0000 0001 0011 ---19的二进制表示
    int c = a | b;//0000 0000 0000 0000 0000 0000 0001 1111 ---a|b=31的二进制表示

(3) Bitwise XOR operator ^: If the binary bits of the two corresponding positions are the same, the result is 0, otherwise it is 1. Look at the following example:

    int a = 15;   //0000 0000 0000 0000 0000 0000 0000 1111 ---15的二进制表示
    int b = 19;   //0000 0000 0000 0000 0000 0000 0001 0011 ---19的二进制表示
    int c = a^b;  //0000 0000 0000 0000 0000 0000 0001 1100 ---a^b=28的二进制表示

The following uses XOR to achieve the exchange of two numbers:

#include <stdio.h>
int main()
{
    
    
 int a = 10;  //0000 0000 0000 0000 0000 0000 0000 1010 ---10的二进制表示
 int b = 20;  //0000 0000 0000 0000 0000 0000 0001 0100 ---20的二进制表示
 a = a^b;   //a=0000 0000 0000 0000 0000 0000 0001 1110 ---30的二进制表示
 b = a^b;   //b=0000 0000 0000 0000 0000 0000 0000 1010 ---10的二进制表示
 a = a^b;   //c=0000 0000 0000 0000 0000 0000 0001 0100 ---20的二进制表示
 printf("a = %d b = %d\n", a, b);
 return 0;
}

The exchange of two numbers through XOR will not overflow.
(4) Left shift operator <<: shift rule: abandon on the left and fill with 0 on the right. Look at the following example:

int main(){
    
    
    int a = 19;  //00000000000000000000000000010011
    int b = a<<1;//00000000000000000000000000100110
    printf("%d", b);//输出结果38
    return 0;
}

(5) Right shift operator >>:
shift rule: First, there are two right shift operations:

  1. Logical shift is filled with 0 on the left and discarded on the right
  2. The left side of the arithmetic shift is filled with the sign bit of the original value, and the right side is discarded

Insert picture description here
Insert picture description here
(6) Bitwise negation operator ~: For the binary bit of the number, if the bit is 1, the result is 0, and if it is changed to 0, the result is 1. E.g:

    int a = 19;//00000000000000000000000000010011
    int b = ~a;//11111111111111111111111111101100(内存中的补码)
               //11111111111111111111111111101011(反码,即补码-1)
               //10000000000000000000000000010100(原码,即反码符号位不变,其余位数取反)

part 2. Plastic lifting and truncation

C integer arithmetic operations are always performed at least with the precision of the default integer type.
In order to obtain this precision, the characters and short integer operands in the expression are converted to ordinary integers before they are used. This conversion is called integral promotion.

//负数的整形提升
char c1 = -1;
变量c1的二进制位(补码)中只有8个比特位:
1111111
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为1
提升之后的结果是:
1111 1111  1111  1111  1111  1111  1111  1111

//正数的整形提升
char c2 = 1;
变量c2的二进制位(补码)中只有8个比特位:
00000001
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为0
提升之后的结果是:
0000 0000 0000 0000 0000 0000 0000 0001

整形提升的例子:
//实例1
char a,b,c;
...
a = b + c;
//负数的整形提升
char c1 = -1;
变量c1的二进制位(补码)中只有8个比特位:
1111111
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为1
提升之后的结果是:
1111 1111 1111 1111 1111 1111 1111 1111
//正数的整形提升
char c2 = 1;
变量c2的二进制位(补码)中只有8个比特位:
0000 0001
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为0
提升之后的结果是:
0000 0000 0000 0000 0000 0000 0000 0001
//无符号整形提升,高位补0

//实例2
#include<stdio.h>
int main()
{
    
    
    char a = 0xb6;
    short b = 0xb600;
    int c = 0xb6000000;
    if (a == 0xb6)
    {
    
    
        printf("a");
    }
    if (b == 0xb600)
    {
    
    
        printf("b");
    }
    if (c == 0xb6000000)
    {
    
    
        printf("c");
    }
    return 0;
}
`

In Example 2, a and b need to be plastically promoted, but c does not need to be plastically promoted. After a, b is plastically promoted, it becomes a negative number, so the expression
a0xb6 , bThe result of 0xb600 is false, but c does not undergo plastic promotion, the result of the expression c==0xb6000000 is true. The result of
the program output is: c

Example 2:

#include <stdio.h>
int main()
{
    
    
    char a = -1;
    //1000 0000 0000 0000 0000 0000 0000 0001 -----原码
    //1111 1111 1111 1111 1111 1111 1111 1110 -----反码
    //1111 1111 1111 1111 1111 1111 1111 1111 -----补码
    //发生截断
    //1111 1111  ----a
    //由于要打印整形(%d)格式,所以发生整形提升,前面补充符号位
    //1111 1111 1111 1111 1111 1111 1111 1111 -----补码
    //1111 1111 1111 1111 1111 1111 1111 1110 -----反码
    //1000 0000 0000 0000 0000 0000 0000 0001 -----原码
    //a那打印就是-1

    signed char b = -1;
    unsigned char c = -1;
    //1000 0000 0000 0000 0000 0000 0000 0001 -----原码
    //1111 1111 1111 1111 1111 1111 1111 1110 -----反码
    //1111 1111 1111 1111 1111 1111 1111 1111 -----补码
    //发生截断
    //1111 1111  ----c
    //由于要打印整形(%d)格式,所以发生了整形提升,前面补充符号位,但c是无符号数,所以前面补0
    //由于是正数-----补码==反码==原码
    //0000 0000 0000 0000 0000 0000 1111 1111 
    printf("a=%d,b=%d,c=%d", a, b, c); //a= -1  b= -1 c= 255
    return 0;

}

Example 3:

#include <stdio.h>
int main()
{
    
    
    char a = -128;
    //1000 0000 0000 0000 0000 0000 1000 0000 -----原码
    //1111 1111 1111 1111 1111 1111 0111 1111 -----反码
    //1111 1111 1111 1111 1111 1111 1000 0000 -----补码
    //截断
    //1000 0000 ----a
    //由于要打印无符号整形(%u)格式,所以发生整形提升,前面补充符号位
    //1111 1111 1111 1111 1111 1111 1000 0000 -----补码==反码==原码  
    printf("%u\n", a);   //打印的值-----4294967168‬
    return 0;
}

Now that we have learned plastic lifting and truncation, let us write the following questions:
Topic 1: What will happen to the following code

#include<stdio.h>
int main()
{
    
    
	unsigned int i;
	for (i = 9; i >= 0; i--)        //无符号整形恒大于0,所以会死循环
	{
    
                                   //当i变成-1时,把有符号数转变位无符号数为32个1然后一直死循环下棋
                                  //1000 0000 0000 0000 0000 0000 0000 0001 -----原码
                                  //1111 1111 1111 1111 1111 1111 1111 1110 -----反码
                                  //1111 1111 1111 1111 1111 1111 1111 1111 -----补码 -1对应的无符号数
		printf("%d\n", i);
	}
	return 0;
}

Unsigned integer is always greater than 0, so it will loop endlessly.

Question 2: What is the value printed by the following code

#include<stdio.h>
#include<string.h>
int main()
{
    
    
    char a[1000];
    int i;
/*a是字符型数组,strlen找的是第一次出现尾零(即值为0)的位置。考虑到a[i]其实是字符型,
如果要为0,则需要 - 1 - i的低八位要是全0,也就是问题简化成了“寻找当 - 1 - i的结果第一次出现低八位全部为0的情况时,
i的值”(因为字符数组下标为i时第一次出现了尾零,则字符串长度就是i)。只看低八位的话,此时 - 1相当于255,所以i == 255的时候,
- 1 - i(255 - 255)的低八位全部都是0,也就是当i为255的时候,a[i]第一次为0,所以a[i]的长度就是255了。*/
    for (i = 0; i < 1000; i++)
    {
    
    
        a[i] = -1 - i;      //-128~127
    }
    printf("%d", strlen(a));   //strlen(a)----255
    return 0;
}

part 3. The large and small ends of the calculator

What is the big and small end

Insert picture description here
Let's use a code to see whether the platform we are currently using uses big-endian storage or little-endian storage.

#include<stdio.h>
int check_sys()
{
    
    
	int i =0x00000001;     
	char* p = (char *)&i;   //让它只能访问一个字节的内容
	return *p;
}
int main()
{
    
    
	if (check_sys() == 1)
	{
    
    
		//把一个数据的低位字节序的内容,存放在低地址处
		//            高位字节的内容,存放在高位地址处
		printf("小端\n");   //1在小端内存存储的十六进制----------01 00 00 00
	}
	else
	{
    
    
		//把一个数据的低位字节序的内容,存放在高位地址处
                  //高位字节序的内容,存放在低位地址处
		printf("大端\n");   //1在大端内存存储的十六进制----------00 00 00 01
	}
	return 0;
}

Insert picture description here
From the above figure, we find that the vs2019 storage is little-endian storage

part 4. Data storage-floating point

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:
(-1)^S * M * 2^E
(-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, which is greater than or equal to 1, and less than 2.
2^E represents the exponent bit.
For example: 5.0 in decimal, 101.0 in binary, equivalent to 1.01×2^2. Then, according to the format of V above, s=0,
M=1.01, and E=2 can be obtained .
Decimal -5.0, written in binary is -101.0, which is equivalent to -1.01×2^2. Then, s=1, M=1.01, and E=2.
IEEE 754 stipulates: For 32-bit floating-point numbers, the highest 1 bit is the sign bit s, the next 8 bits are the exponent E, and the remaining 23 bits are the significant digits M.
Insert picture description here
For a 64-bit floating point number, the highest 1 bit is the sign bit S, the next 11 bits are the exponent E, and the remaining 52 bits are the significant digit M.
Insert picture description here
IEEE 754 the number M and the effective exponent E, some special provisions . As mentioned earlier, 1≤M<2, that is to say, M can be written in the form of 1.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 discarded and only the following xxxxxx part is saved.
For example, when saving 1.01, only 01 is saved, and when it is read, the first 1 is added. The purpose of this is to save 1 significant figure.
Take a 32-bit floating-point number as an example. There are only 23 bits left for M. After rounding down the 1 in the first digit, it is equal to 24 significant digits.

As for the index E, the situation is more complicated.
First, E is an unsigned integer (unsigned int). This means that if E is 8 bits, its value range is 0~255; if E is 11 bits, its
value range is 0~2047. However, we know that, in the scientific notation E is a negative number, the IEEE 754 provides real time into memory E
real-valued intermediate must be combined with a number, for an 8-bit E, the middle number is 127 ; For an 11-digit E, the middle number is 1023. For example, the E
of2^10is 10, so when saving as a 32-bit floating point number, it must be saved as 10+127=137, which is 10001001.

Then, the exponent E is taken out of the memory and can be further divided into three cases:
E is not all 0 or not all 1,
then the floating-point number is expressed by the following rule, that is, the calculated value of the exponent E minus 127 (or 1023), Get the true value, and then
add 1 in the first digit before the effective number M. For example: the binary form of 0.5 (1/2) is 0.1, because the positive part must be 1, that is, the decimal point is shifted to the right by 1 place,
then it is 1.0*2^(-1), and its order code is -1+127= 126 is represented as 01111110, and the mantissa 1.0 removes the integer part to 0, and fills in 0 to 23 bits
00000000000000000000000, then its binary representation is:
E is all 0
At this time, the exponent E of the floating point number is equal to 1-127 (or 1- 1023) is the true value, the effective digit M is no longer added to the first 1 but is restored to
a decimal of 0.xxxxxx. This is done to represent ±0, and very small numbers close to zero.
E is all 1 at
this time, if the significant digits M are all 0, it means ± infinity (positive or negative depends on the sign bit s);

int main()
{
    
    
	int n = 9;
	//0 00000000 00000000000000000001001 ----整数存储形式 
	//(-1)^0 * 0.00000000000000000001001 * 2^-126   ----单精度浮点数存储形式
	float *pFloat = (float *)&n;
	printf("n的值为:%d\n", n);//9
	printf("*pFloat的值为:%f\n", *pFloat);//0.000000

	*pFloat = 9.0;
	//1001.0
	//(-1)^0 * 1.001*2^3
	//S=0  ----表示的是符号位 0为正 ,负数为1
	//M=1.001
	//E=3     +127
	//9.0 -> 1001.0 ->(-1)^01.0012^3 -> s=0, M=1.001,E=3+127=130
	//01000001000100000000000000000000
	printf("num的值为:%d\n", n);//直接打印整形时,就是2进制到十进制转换
	printf("*pFloat的值为:%f\n", *pFloat);//9.0
	return 0;
}

Okay, that's it.

Guess you like

Origin blog.csdn.net/weixin_47812603/article/details/113530893