In C and Objective-C, should we use 0.5f or 0.5?

14,457

Solution 1

0.5 is a double constant, while 0.5f is a float constant.

is there advantage of using 0.5f?

yes. you either want to specify the type (read: precision) for the calculation or avoid implicit narrowing (and associated compiler warnings).

the example you provide isn't compelling, beyond avoiding compiler warnings or just being very exacting regarding the parameter type.

however, less-than-obvious promotions to double precision can be costly.

To answer "Should we use 0.5f or 0.5": Because there are appropriate times for each, you should be using both in your programs. You should either be using the type appropriate for the calculation/precision you need, the type for the parameter you pass, or the type appropriate for other values in the expression. This is just like integers -- there are appropriate times to consider widths, and there appropriate times to suffix (e.g. U and L). There are times when the distinction is important, and (naturally) times when it is not important.

Solution 2

The constant 0.5 declares a double in Objective-C, putting an f on the end - 0.5f declares the constant as a (32-bit) float.
Take a look at "f" after number/float in Objective-C/C post.

Solution 3

The trailing f forces the constant to be a single-precision float, as floating-point constants default to being doubles. This was more important a few compiler revisions ago, as I do not believe there was automatic type conversion for doubles to floats, as the compiler could not ensure that there were no side-effects to doing so. The result ended up being that a lot of code kept the f specifier to avoid burning cycles on loading double-precision floating-point registers when only single-precision ones were needed.

Solution 4

There are consequences to using a float constant when a double constant is appropriate or vice-versa:

  1. Many constants change value. For example, .3 does not equal .3f. When the source text contains a decimal numeral, the compiler must convert it to a value representable in floating-point. The 1999 C standard requires it be converted to either the nearest higher representable value or the nearest lower representable value. A good compiler will convert it to the nearest value in either direction and, in case of a tie, will choose the one in which the lowest bit is zero. (Returning such a value is called “correct rounding”.) When correctly rounded to the floating-point formats commonly in use, .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 a hexadecimal numeral, and the part after the “p” is a decimal exponent for 2. So 0x3.cp4 is 0x3.c times 24, which is (3 + 12/16)⋅24 = 60.)

  2. Expressions change type. When the operands of an arithmetic operator contain both float and double operands, the float operand is converted to double. This can cause the calculated values to change. The 1999 C standard permits the compiler to represent a floating-point value with more precision and range than its type requires, so float values might be held in double registers and operated on with double arithmetic. However, if you use a double constants, you are requiring the compiler to use double arithmetic. So, if the float x contains 0x1.24924ap-3 (approximately 1/7), then “x + .5f” may produce 0x1.492492p-1 while “x + .5” must produce 0x1.4924928p-1.

  3. In Objective C, C++, and other languages that may select which function to call based on argument types, changing the type of an argument can completely change the code that is executed. The call “foo(.5f)” can call a routine that allocates memory while “foo(.5)” writes to a network socket. This would generally be bad design of the called object, but there are special circumstances where objects need to be sensitive to the types of their arguments. Also, this is technically possible in C (as in tgmath.h), but it is rare (and generally involves using the preprocessor to define “foo” to be an expression that tests the size of its arguments).

The above list is not intended to be all-inclusive.

On the other hand, code or data size is rarely an issue. There is no guarantee that whether you write .5f or .5, the compiler will actually store a float or a double. For example, if I write “printf("%g", 3.*4.+5.)“, the compiler is not required to store 3, 4, or 5. It merely needs to produce a program that writes “17” to standard output, and it may do this by storing 3, 4, and 5 and doing the calculation at run time or by storing 17 and passing it to printf at run time or by storing only the string “17” and writing that to standard output with puts, not storing any numbers or calling printf at all.

So, usually, the important thing about which type to use is to properly express the computation you want performed and not to worry about optimization. If you write “foo(.5)“ and foo has a double parameter, the compiler may store .5f in the program’s constants and generate code that, at run-time, loads the .5f into a double register and passes it to foo. I would expect a good compiler to store constants in the smallest form that can be loaded with no extra execution cost.

Share:
14,457
Jeremy L
Author by

Jeremy L

Updated on July 01, 2022

Comments

  • Jeremy L
    Jeremy L almost 2 years

    There are many places that I saw an author uses:

    sprite.anchorPoint = CGPointMake(1, 0.5f);
    

    that is, why not use 0.5 instead of 0.5f -- is there advantage of using 0.5f?