Sprintf and floating point representation

Source: Internet
Author: User
Tags 04x bitset decimal to binary print format sprintf

When constructing various types of data into strings, the powerful features of sprintf seldom disappoint you. Since sprintf and printf have almost the same usage, but the printing destination is different, the former is printed into the string, and the latter is directly output on the command line. This also makes sprintf much more useful than printf.
Sprintf is a variable parameter function, which is defined as follows:
Int sprintf (char * buffer, const char * Format [, argument]...);
In addition to the fixed types of the first two parameters, you can take over multiple parameters later. But its essence is obviously in the second parameter:
Format the string.

Both printf and sprintf use formatted strings to specify the string format. Some format specifiers (format specifications) starting with "%" are used inside the format string to occupy a position, the variable is provided in the variable parameter list, and the function will replace the specifier with the variable at the corresponding position to generate the string that the caller wants.

Format a numeric string
One of the most common applications of sprintf is to print Integers to strings. Therefore, spritnf can replace ITOA in most cases.
For example:
// Print the integer 123 into a string and save it in S.
Sprintf (S, "% d", 123); // generate "123"
You can specify the width. If the width is insufficient, spaces are filled on the left:
Sprintf (S, "% 8d % 8d", 123,456 7); // generate: "123 4567"
Of course, you can also align left:
Sprintf (S, "%-8d % 8d", 123,456 7); // generate: "123 4567"
You can also print the data in hexadecimal format:
Sprintf (S, "% 8x", 4567); // lowercase hexadecimal notation, with 8 width positions and right alignment
Sprintf (S, "%-8x", 4568); // in hexadecimal notation, the width occupies 8 positions and is left aligned.

In this way, the hexadecimal string of an integer is easy to obtain, but when printing the hexadecimal content, we usually want an equal-width format with 0 on the left, what should we do? Simply add 0 to the number that represents the width.
Sprintf (S, "% 08x", 4567); // generate: "201711d7"
You can also use this left-side 0 Complement Method to print the 10-in-hexadecimal format with "% d" above.

Pay attention to a symbol extension problem: for example, if we want to print a short INTEGER (short)-1 memory hexadecimal representation, on the Win32 platform, A short type occupies 2 bytes, So we naturally want to print it with 4 hexadecimal numbers:
Short Si =-1;
Sprintf (S, "% 04x", Si );
Why is "ffffffff" generated? Because spritnf is a Variable Parameter Function, except the first two parameters, the following parameters are not of type security, there is no way for a function to know whether a 4-byte integer or a 2-byte short integer is pressed in the parameter stack before the function call through a "% x "., therefore, a 4-byte processing method is adopted, which leads to the symbol extension during parameter pressure stack, which is a 32-bit integer-1. When printing, the four locations are insufficient, print out the 8-bit 16 hexadecimal values of 32-bit integer-1.

If you want to see the original form of Si, you should let the compiler do 0 extension instead of symbol extension (during expansion, the left side of the binary complement 0 instead of the sign bit ):
Sprintf (S, "% 04x", (unsigned short) Si );
You can. Or:
Unsigned short Si =-1;
Sprintf (S, "% 04x", Si );

Sprintf and printf can also print integer strings in octal, using "% O ". Note that both the octal and hexadecimal formats do not print negative numbers. They are all unsigned. In fact, they are the direct hexadecimal or octal representation of the variable's internal code.

Control floating point print format
The printing and format control of floating point numbers is another common function of sprintf. Floating Point Numbers are controlled by the format character "% F". By default, the six digits after the decimal point are retained, for example:
Sprintf (S, "% F", 3.1415926); // generate "3.141593"
But sometimes we want to control the print width and decimal places by ourselves, then we should use the format "% m. NF", where M indicates the print width, N indicates the number of digits after the decimal point. For example:
Sprintf (S, "% 10.3f", 3.1415626); // generate: "3.142"
Sprintf (S, "%-10.3f", 3.1415626); // generate: "3.142"
Sprintf (S, "%. 3f", 3.1415626); // The total width is not specified, resulting in: "3.142"

Pay attention to one question, you guess
Int I = 100;
Sprintf (S, "%. 2f", I );
What will it do? 100.00 "? Right? Try it on your own and try the following:
Sprintf (S, "%. 2f", (double) I );
The first one is definitely not the correct result, because, as mentioned above, the caller does not know that the format controller corresponding to I is "% F" when the parameter is pressed ". When a function is executed, the function itself does not know that the number pushed to the stack in the current year is an integer, so the four bytes that saved the integer I were forcibly interpreted as a floating-point number. However, if someone is interested in manually encoding a floating point number, you can use this method to check whether the result of your manual arrangement is correct.

Character/ASCII code comparison
We know that in C/C ++, char is also a common scalable type. In addition to the word length, it has no essential difference with short, Int, and long, it is used to represent characters and strings. (Maybe this type was called "byte" in the past, and now we can use byte or short to define char through typedef according to the actual situation, which is more appropriate, print a character using "% d" or "% x" to obtain its 10-or 16-digit ASCII code. Conversely, print an integer using "% C" to see its ASCII characters. The following section prints the ASCII code table of all visible characters to the screen (printf is used here, note that "#" and "% x" are automatically prefixed with "0x" in hexadecimal notation ):
For (INT I = 32; I <127; I ++ ){
Printf ("[% C]: % 3d 0x % # 04x/N", I, I );
}

Connection string
Since the sprintf format control string can insert various things and "connect them together", it can naturally connect strings, which can replace strcat in many occasions, but sprintf can connect multiple strings at a time (it can also insert other content in them at the same time, in short, it is very flexible ). For example:
Char * Who = "I ";
Char * Whom = "csdn ";
Sprintf (S, "% s love % S.", who, whom); // generate: "I love csdn ."
Strcat can only connect strings (an array of characters ending with '/0' or a character buffer, null-terminated-string), but sometimes we have two character buffers, they do not end with '/0. For example, many character arrays returned from third-party library functions, and hidden streams read from hardware or network transmission, they may not end with a corresponding '/0' after each character sequence. If you connect directly, whether sprintf or strcat will certainly lead to illegal memory operations, and strncat also requires at least the first parameter to be null-terminated-string. What should we do? We naturally remember that the width can be specified when we first introduced printing integers and floating-point numbers, and the strings are the same. For example:
Char A1 [] = {'A', 'B', 'C', 'D', 'E', 'F', 'G '};
Char A2 [] = {'h', 'I', 'J', 'k', 'l', 'M', 'n '};
If:
Sprintf (S, "% S % s", a1, a2); // don't do that!
In, something went wrong. Can it be changed:
Sprintf (S, "% 7 S % 7 s", a1, a2 );
It's not good where to go. The correct one should be:
Sprintf (S, "%. 7 S %. 7 s", a1, a2); // generate: "abcdefghijklmn"
This can be analogous to printing the "% m. NF ", in" % m. in "ns", "M" indicates the width occupied (when the string length is insufficient, fill in spaces. If the length exceeds the limit, print it according to the actual width). "N" indicates the maximum number of characters used from the corresponding string. Generally, M is useless when printing strings, but n is usually used after the dot. Naturally, you can only take part of the characters before and after:
Sprintf (S, "%. 6 S %. 5 s", a1, a2); // generate: "abcdefhijkl"
In many cases, we may also want the numbers in these format controllers to specify length information to be dynamic, rather than static, because many times, when the program is running, it will know exactly how many characters in the character array need to be taken. This dynamic width/precision setting function is also taken into account in the implementation of sprintf, sprintf uses "*" to occupy a position that originally requires a constant number with a specified width or accuracy. Similarly, the actual width or accuracy can be provided like other printed variables, so the preceding example can be changed:
Sprintf (S, "%. * S %. * s", 7, A1, 7, A2 );
Or:
Sprintf (S, "%. * S %. * s", sizeof (A1), A1, sizeof (A2), A2 );
In fact, the printed characters, integers, and floating-point numbers described above can all dynamically specify the constant values, such:
Sprintf (S, "%-* D", 4, 'A'); // generate "65"
Sprintf (S, "% #0 * X", 8,128); // generate "0x000080", "#" generate 0x
Sprintf (S, "% *. * F", 10, 2, 3.1415926); // generate "3.14"

Print address information
Sometimes, when debugging a program, we may want to view the addresses of some variables or members. Because the addresses or pointers are only 32-bit numbers, you can print the unsigned integers "% u:
Sprintf (S, "% u", & I );
However, people usually prefer to use hexadecimal instead of hexadecimal to display an address:
Sprintf (S, "% 08x", & I );
However, these are indirect methods. For address printing, sprintf provides a special "% P ":
Sprintf (S, "% P", & I );
I think it is actually equivalent:
Sprintf (S, "% 0 * X", 2 * sizeof (void *), & I );
Use sprintf's Return Value
Few people pay attention to the return values of the printf/sprintf function, but sometimes it is useful. spritnf returns the number of characters that the function call finally prints to the character buffer. That is to say, after each sprinf call ends, you do not need to call strlen again to know the length of the result string. For example:
Int Len = sprintf (S, "% d", I );
For a positive integer, Len is equal to the 10-digit digits of the integer I.
The following is a complete example, which generates a random number between 10 [0,100) and prints them into an array of characters (s) separated by commas.
# Include <stdlib. h>
# Include <stdio. h>
# Include <time. h>
Int main (){
Srand (unsigned) Time (null ));
Char s [64];
Int offset = 0;
For (INT I = 0; I <10; I ++ ){
Offset + = sprintf (S + offset, "% d,", Rand () % 100 );
}
S [offset-1] = '/N'; // Replace the last comma with a line break.
Printf (s );
Return 0;
}
Imagine that when you extract a record from the database and want to connect each of their fields into a string according to certain rules, you can use this method. Theoretically, it should be more efficient than the continuous strcat, because each call to strcat needs to first find the last '/0' position, and in the example given above, every time we use the sprintf return value to write down this position directly.

FAQ about using sprintf
Sprintf is a variable parameter function, which often causes problems when used, and as long as there is a problem, it is usually a memory access error that can cause the program to crash, but fortunately the problems caused by sprintf misuse are serious, but it is easy to find out, nothing more than a few situations, usually with the eyes of the error code to see a few more eyes.
The length of the first parameter is too short. If you don't want to say it, just give it a bigger one. Of course, it may also be a problem with the following parameters. We recommend that you be careful with the corresponding parameters. When printing a string, try to use the "%. Ns" format to specify the maximum number of characters.
The first parameter is missing.
Low-level users cannot have low-level problems. They are too familiar with printf. // It is common. :. (
An error occurred while changing the parameter.
Generally, you forget to provide variable parameters corresponding to a certain format character, which leads to misplacement of all subsequent parameters. Check the parameters. You
Are all parameters corresponding to "*" provided? Do not map an integer to "% s". The compiler will think that you
It's so bad (the compiler is the mother of OBJ and exe, it should be a female, P ).
Strftime
Sprnitf also has a good cousin: strftime, which is specially used to format the time string. Its usage is very similar to that of her cousin. It is also a lot of format control characters, but after all, the girl's family is fine, she also needs the caller to specify the maximum length of the buffer, probably to shirk responsibility in case of problems. Here is an example:
Time_t t = time (0 );
// Generate a string in the format of "YYYY-MM-DD hh: mm: SS.
Char s [32];
Strftime (S, sizeof (s), "% Y-% m-% d % H: % m: % s", localtime (& T ));
Sprintf can also find its Zhiyin: cstring: format in MFC, and strftime naturally has her same path in MFC:
Ctime: format, which is more elegant because of the sponsorship of the object-oriented code.

 

 Floating point representation 

Two years ago, I knew that the equal number of floating point numbers should not be determined by the = number, because there is a precision problem, but I have never cared much about these things. In fact, although I know about the floating point structure, it is not clear. as a C ++ enthusiast, I should try to figure out every problem, so I have figured out the internal representation and implementation of floating point numbers. in the absence of major problems, everything is based on easy understanding and memory. first, let's talk about the original, reverse, complement, and shift code. the transfer is actually equal to the completion code, but the opposite is the symbol. for positive numbers, the original, reverse, and complement codes are the same. For negative numbers, in addition to the symbol bit, the reverse code is reversed based on the original code, and the complement Code is based on the reverse code, add 1 to the bitwise of the token. when the request is to be transferred, the request is still to be completed first, and then the symbol is changed. floating Point Numbers are divided into float and double, which occupy bytes, namely 32-bit and 64-bit. I only use a 32-bit float as an example, with double. in the ieee754 standard, the 32-bit float is defined as follows:
Symbol bit (S) 1 Level Code (e) 8 Tail (m) 23
Note the following three points:
A, the order code is represented by a shift code. Here there will be a 127 offset. Its 127 is equivalent to 0. If it is less than 127, It is negative. If it is greater than 127, it is positive. For example: 10000001 indicates that the exponent is 129-127 = 2, indicating that the true value is 2 ^ 2, and 01111110 indicates 2 ^ (-1 ). b. The ending number is the number C after the decimal point, but the ending number is omitted as 1. Therefore, when the ending number is all 0, it is also 1. 0... 00; next, you only need to explain a few questions. Taking 123.456 as an example, the binary value is: n (2) = 1111011. 01110100101111001, here, it will shift 6 places to the right and get N (2) = 1.111011 01110100101111001*2 ^ 6. This form can be used in the representation format.

Symbol bit (s) 0 Order code (e) 00000110 Tail (m) 11101101110100101111001

Note that the first digit of the order code above is positive in the 0 table, and the ending number is 1 less than the first digit indicated by N (2). That is, the first digit is 1 by default. because in the process of converting decimal to binary, it is often not possible to convert exactly the same (of course, there will be no loss such as 4.0, and the inevitable loss such as 1.0/3.0 ), so the precision of floating point numbers is generated. In fact, the first 8 digits of the decimal point can be affected by the 23-bit binary number after the decimal point. Why? At this time, the average person is often confused. In fact, it is very simple. In the ending number shown above, it is binary, and there are 23 digits after the decimal point. When the value of the last digit is 1, it is 1/2 ^ 22 = 0.000000238. The actual value must be 0.0000002. That is to say, for a float floating point number, the valid bits are 7 digits from left to right (including the default 1 is 7 digits). When the above 8th bits are reached, they are unreliable, however, the maximum output value of vc6 is 1.0/3.0, which is mainly caused by the compiler. This does not mean that the 16 digits after the floating point are valid. if you do not believe it, you can try the double type 1.0/3.0, and the result will also be 17 digits after the decimal point... in addition, compilers or circuit boards generally have the "Noise Removing" "correction" capability, which can make the number of decimal digits that exceed 7 digits even if they are invalid, this is why the output is always 333 instead of 345 ,. you can try it like this: Float F = 123456789;
Cout <F <Endl; // 123456789 is returned here. here is a forgotten question: how can a decimal point be converted to a decimal point in decimal order? In fact, it is very easy to multiply the decimal part in decimal order by 2, write the corresponding binary into 1. therefore, when we convert N (2) = 1.111011 01110100101111001*2 ^ 6; Back to the decimal number, it is likely that it is no longer 123.456. well, the accuracy issue should be clear. the value range is as follows. the number of digits of the order code is an 8-shift code. The maximum value is 127 and the minimum value is-127. Here, 127 is used as the index of 2, so it is 2 ^ 127, it is about 1.7014*10 ^ 38, and we know that the float value range is-3.4*10 ^ 38-----3.4*10 ^ 38, this is because all the 24 digits of the ending number (the first digit is 1 by default) is 1, which is very close to 2, 1. 11 .. 11 is obviously about 2, so the floating point range comes out. double is similar to float, but its internal form is

Symbol bit (S) 1 Level Code (e) 11 Tail (m) 52
The main difference is that its level code has 11 digits, which is 2 ^ 1023 about equal to 0.8572*10 ^ 308, And the ending number of 53 digits is about 2, therefore, the value range of double is-1.7*10 ^ 308. ------ 1.7*10 ^ 308. as for its accuracy, 1.0/2 ^ 51 = 4.4*10 ^ (-16 ). the value is 15 digits after the decimal point plus the default one. Therefore, for Double Floating Point numbers, the 16 digits from left to right are reliable. sometimes, we will hear the word "Fixed Point decimal". Single-Chip Microcomputer (such as mobile phones) generally only uses fixed points. When confused, we will think float a = 23.4; this is a fixed point decimal, float a = 2.34e1 is a floating point number. In fact, this is incorrect. The above is only a different representation of the same floating point number, all of which are floating point numbers. this method is used to specify a decimal point. The decimal point is placed after a single digit, And the decimal point is 0. the pure decimal point can also be considered as a fixed point decimal point, but it can only represent a pure decimal point smaller than 1. then let's talk about several functions in C/C ++. In C ++, the 5 decimal places are output by default, but you can set two methods: Call setpression or use cout. expression, but the effect is different: Float Mm = 123.456789f;
Cout <mm <Endl; // although the default value of 123.457 is the last five digits without limit, this is only true for the integer.
Setprecision (10); // set the number of digits after the decimal point. However, when the integer has two digits, it is no different from the default one.
Cout <mm <Endl; // 123.457
Cout. Precision (4); // set the total number of digits.
Cout <mm <Endl; // 123.4 in short, the effect is quite strange. I personally think that although this seems uncertain, it is actually limited by the hardware system. it is understandable. for the actual expression of 0, some people think that + 0 can be absolutely 0, while-0 may represent an extremely small number. therefore, I have come up with a good verification method, proving that no matter + 0 or-0, it is 2 ^ (-127), the Code is as follows: Float fdigital = 0.0f;
Unsigned long nmem; // temporary variable used to store the memory data of Floating Point Numbers
// Copy the memory by bit to the temporary variable for use. The nmem is not equal to fdigital, And it is copied by bit.
Nmem = * (unsigned long *) & fdigital;
Cout <nmem <Endl; // generally, a large integer is obtained. bitset <32> mybit (nmem); // The output here is the memory representation of 32float. finally, we can see it intuitively.
Cout <mybit <Endl; // 00000000000000000000000000000000 use-0.0 for the test. if you still think that the above long string 0 represents absolute 0, read this article again. in fact, this is a clever practice. The above fdigital is represented by any other floating point number. This bitset number can reflect its memory representation. there is a reason for the shift code to indicate the order code, mainly because the shift code facilitates the operation of the order, so as to compare the size of two floating point numbers. note that the level code cannot reach 11111111. IEEE stipulates that when the level code of the compiler is 0xff, an overflow command is called. in short, when the order is converted into an integer, the range is-127 ~ 127. In the end, there is a place where experts often face shame. Remember that the unsigned usinged float/Double Floating Point Numbers are incorrect. I am not very familiar with it. Thank you for your criticism.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.