There are consequences of using the float constant when using a double constant or vice versa:
Many constants change value. For example, .3 is not equal to .3f. When the source text contains a decimal digit, the compiler must convert it to a value represented in floating point. The 1999 C standard requires it to be converted either to the nearest higher representable value, or to the nearest lower representable value. A good compiler converts it to the closest value in any direction and, in case of equality, selects one in which the least significant bit is zero. (Returning this value is called "proper rounding.") When properly rounded to the floating point formats that are commonly used, .3 becomes 0x1.3333333333333p-2, and .3f becomes 0x1.333334p-2. (These are hexadecimal floating-point constants. The part after "0x" and before "p" is the hexadecimal digit, and the part after "p" is the decimal value for 2. Thus, 0x3.cp4 is 0x3.c times 2 4 which equals (3 + 12/16) β
2 4 = 60.)
Type of expression change. When the operands of an arithmetic operator contain both float and double operands, the float operand is converted to double. This can lead to a change in the calculated values. The 1999 C standard allows the compiler to represent a floating-point value with greater precision and range than is required for its type, so float values ββcan be stored in double registers and work with double arithmetic. However, if you use double constants, you need the compiler to use double arithmetic. So, if float x contains 0x1.24924ap-3 (approximately 1/7), then "x + .5f" can give 0x1.492492p-1, while "x +.5" should produce 0x1.4924928p-1.
In Objective-C, C ++, and other languages ββthat can choose which function to call based on argument types, changing the type of the argument can completely change the code that is executed. A call to "foo (.5f)" can call a procedure that allocates memory, and "foo (.5)" is written to the network socket. This is usually a poor design for the called object, but there are special circumstances in which the objects must be sensitive to the types of their arguments. It is also technically possible in C (as in tgmath.h), but it is rare (and usually involves using a preprocessor to define "foo" as an expression that checks the size of its arguments).
The above list is not intended to be used on an all-inclusive basis.
On the other hand, code or data size is rarely a problem. There is no guarantee that if you write .5f or .5, the compiler will actually save the float or double. For example, if I write "printf ("% g ", 3. * 4. + 5.)", the Compiler should not store 3, 4 or 5. It just needs to create a program that writes "17" to standard output, and he can do this by storing 3, 4, and 5 and doing calculations at runtime, or saving 17 and passing it to printf at runtime, or keeping only the string β17β and writing it to standard output with puts, without saving any numbers or without calling printf at all.
So, as a rule, the important thing about which type to use is to correctly express the calculation that you want to perform, and not to worry about optimization. If you write "foo (.5)" and foo has a double parameter, the compiler can store .5f in program constants and generate code that at run time loads .5f in double register and passes it to foo, I would expect that a good compiler will save constants in the smallest form that can be loaded without the additional cost of execution.
Eric Postpischil
source share