Question 9
#include <stdio.h>int main(){ float f=0.0f; int i; for(i=0;i<10;i++) f = f + 0.1f; if(f == 1.0f) printf("f is 1.0 \n"); else printf("f is NOT 1.0\n"); return 0;}
Knowledge points:
Floating-point registers are part of FPU. The number and digits of floating point registers vary depending on the hardware architecture. The floating-point registers in the X86 architecture include:
1) Eight 80-bit data registers: fpr0 ~ Fpr7: the number of data registers determines the computer's computing accuracy. The more digits, the higher the computing accuracy;
2) Three 16-bit registers: one mark register, one control register, and one Status Register;
3) Two 48-bit registers: one instruction pointer and one Data Pointer.
FPU has its own set of commands for floating-point register operations. For data registers, the names of these eight registers cannot be directly used. These eight data registers are designed as the first and last stacks, ST (0) points to the top of the fprx stack.
- Binary representation of floating point numbers in registers
References:
Http://www.ruanyifeng.com/blog/2010/06/ieee_floating-point_representation.html
Http://floating-point-gui.de/
Http://en.wikipedia.org/wiki/IEEE_754
Http://en.wikipedia.org/wiki/Floating_point
According to the International Standard IEEE 754, any binary floating point V can be expressed in the following form:
V = (-1) S x m x 2e
The following table lists the digits of S, M, and E:
Type |
Total BITs |
S |
E |
M |
Exponent bias |
Bits precision |
Single |
32 |
1 |
8 |
23 |
127 |
24 |
Double |
64 |
1 |
11 |
52 |
1023 |
53 |
Double Extended |
80 |
1 |
15 |
64 |
16383 |
64 |
M: 1 ≤ m ≤ 2, that is, m can always be expressed as 1. in the format of XXX, because the first digit of M is always 1, when m is expressed in the register, the first digit 1 is removed and only the fractional part is saved, thus saving one valid digit. Taking a 32-bit floating point number as an example, M occupies 23 digits, but the number of valid digits is 24.
E: Add a fixed value to e when storing the edata. For 32-bit floating point numbers, the fixed value is 127. For 64-bit floating point numbers, the fixed value is 1023. For more information about E, see the first reference link.
Example:
float x = 0.15625;
0.15625 in binary format: 0.00101
S = 0, E =-3, M = 1.01
The part in the register that stores e should be-3 + 127;
The M part in the register should be stored 01
Therefore, 0.15625 is represented in the register:
0 01111100 01000000000000000000000
Description:
Floating point numbers cannot be accurately expressed in the computer, indicating a tiny error between the value and the actual value. This error will be superimposed with addition and subtraction. Do not simply use ">" "<" "=" "! =.
For more information about how to compare floating point numbers, see
Http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
Question 10
#include <stdio.h>int main(){ int a = 1,2; printf("a : %d\n",a); return 0;}
Knowledge points:
The form of a comma expression is as follows:
(Expression 1, expression 2, expression 3 ,......, Expression N)
(1) The comma expression is calculated one by one from left to right;
(2) The comma expression acts as a whole, and its value is the value of the last expression;
(3) The comma operator has the lowest priority among all operators.
Questions
Because the comma operator has the lowest priority
int a = 1,2;
Equivalent
int (a = 1),2
The compilation is incorrect.
Should be changed:
int a = (1,2);
11th questions
#include <stdio.h>int main(){ int i=43; printf("%d\n",printf("%d",printf("%d",i))); return 0;}
Description:
The return value of printf is the number of printed characters. The description in Man 3 printf is as follows:
Upon successful return, these functions return the number of characters printed (not including the trailing “\0”used to end output to strings).
Therefore, the output of this program is: 4321
12th questions
void duff(register char *to, register char *from, register int count) { register int n=(count+7)/8; switch(count%8){ case 0: do{ *to++ = *from++; case 7: *to++ = *from++; case 6: *to++ = *from++; case 5: *to++ = *from++; case 4: *to++ = *from++; case 3: *to++ = *from++; case 2: *to++ = *from++; case 1: *to++ = *from++; }while( --n >0); } }
Knowledge Point explanation
The notification compiler puts the register variable "as much as possible" into the Register to speed up access to the variable. When common variables are stored in the memory, the access speed is equivalent to the memory speed. The register variables are stored in registers, and the access speed is equivalent to the CPU speed. Defining variables that require frequent cyclic access as the register type can improve the program running efficiency.
A ++: Get the address of a, load its value into the Register, and then increase the value of A in the memory;
++ A: Get the address of a, increase the value of A in memory, and load the value of a into the register.
- CLanguage operator priority
For the C language computing priority, refer:
Http://www.slyar.com/blog/c-operator-priority.html
For * To ++, * and ++ operators of the same level, the combination direction is from right to left, so * To ++ = * (to ++ ), the procedure is as follows:
1. Get the value of to the Register;
2. Add 1 to the to value;
3. Take the value pointed to by the address in the register in step 1.
"* To ++ = * From ++;" means to copy a byte from the source address to the destination address, and move the original address and destination address to a byte.
For more information about Dafu devices, see:
Http://www.drdobbs.com/a-reusable-duff-device/184406208
The Duff device is an optimized Implementation of serial replication, which increases the program execution efficiency by reducing the number of cycles.
What method do you use to copy a piece of data from the source address to the target address?
Realm 1:
void my_copy_1(register char *to, register char *from, register int count){ while(count-- > 0) { *to++ = *from++; }}
Comment: This code is concise and easy to understand, but the execution efficiency is not high. After each byte is copied, three additional actions are performed: 1. Jump to the starting point of the loop; 2. Change the value of count; 3. Compare the value of count with 0. The time consumed by these three additional actions is much longer than the time consumed by the copy action, and the code is a little too heavy.
Think about it. If you are a CPU, every time you copy a byte, you will check whether the value of count exceeded once. It would be nice if the CPU could be smoothly copied without interrupting ......
Realm 2:
void my_copy_2(register char *to, register char *from, register int count){ register int n_block = count/8; register int n_left = count%8; while (n_block-- > 0) { *to++ = *from++; *to++ = *from++; *to++ = *from++; *to++ = *from++; *to++ = *from++; *to++ = *from++; *to++ = *from++; *to++ = *from++; } while (n_left-- > 0) { *to++ = *from; }}
Comment: When copying a large data segment, it is the fastest way for the CPU to copy it all the time, but we have to check whether the copied data volume has exceeded the set value. It is obviously unnecessary to go back one step by one. You can still consider it when you look back at N branch. In the preceding procedure, set N to 8. Of course, you can set N to another value as needed.
The process of "fragmented data" in the above program is still using the old method of "One step by one". Is there a simpler way to process this data?
Realm 3:
void my_copy_3(register char *to, register char *from, register int count){ register int n_block = (count+7)/8; int n_left = count%8; switch (n_left) { case 0: do{*to++ = *from++; case 7: *to++ = *from++; case 6: *to++ = *from++; case 5: *to++ = *from++; case 4: *to++ = *from++; case 3: *to++ = *from++; case 2: *to++ = *from++; case 1: *to++ = *from++; } while(--n_block > 0); }}
Comment: The above code first processes fragmented data, and then processes block data. Use the switch statement to jump to the corresponding case number based on the amount of fragmented data. If the number of fragmented data is 7, perform the assignment seven times one way, and assign the assignment six times one way, and so on ...... This method has the least number of comparative operations. The above serial replication method is called "Da fu device ".
Of course, we can also use macros to implement dual devices. The macros are defined as follows:
#define DUFF_DEVICE_8(aCount, aAction) do { int count_ = (aCount); int times_ = (count_ + 7) >> 3; switch (count_ & 7) { case 0: do{aAction; case 7: aAction; case 6: aAction; case 5: aAction; case 4: aAction; case 3: aAction; case 2: aAction; case 1: aAction; } while(--times_ > 0); } }while (0)
Comment: num divided by 8 and num has two ways to get the remainder of 8: one is num/8, num % 8; the other is num> 3, num & 7. The latter method is more efficient.
C puzzles [9-12]