Corrected: Using type suffixes with floating point constants

I

Ioannis Vranos

ISO/IEC 9899:1990/1995 says (from K&R2):

“A6.4

When a less precise floating value is converted to an equally or more precise floating type, the value is
unchanged.

==> When a more precise floating value is converted to a less precise floating type, and the value is within
representable range, the result may be either the next higher or the next lower representable value.

If the result is out of range, the behavior is undefined”.


Question: Does the above mean that it is a good practice or *always* needed to use the appropriate type
suffixes with floating point constants?



An example of this:


#include <stdio.h>


int main(void)
{
float f1 = 0.33439F;

float f2= 0.33439f;

printf("\nf1= %f, f2= %f\n", f1, f2);


double d1= 0.33439;

double d2= 0.33439;

printf("\nd1= %f, d2= %f\n", d1, d2);


/* It doesn't work with MINGW, compiler is broken regarding long double. */
long double ld1= 0.33439L;

long double ld2= 0.33439l; /* 'l' is the lower case 'L'. */

printf("\nld1= %Lf, ld2= %Lf\n\n", ld1, ld2);


return 0;
}
 
I

Ioannis Vranos

Ioannis said:
ISO/IEC 9899:1990/1995 says (from K&R2):

“A6.4

When a less precise floating value is converted to an equally or more
precise floating type, the value is unchanged.

==> When a more precise floating value is converted to a less precise
floating type, and the value is within representable range, the result
may be either the next higher or the next lower representable value.

If the result is out of range, the behavior is undefined”.


Question: Does the above mean that it is a good practice or *always*
needed to use the appropriate type suffixes with floating point constants?



An example of this:


#include <stdio.h>


int main(void)
{
float f1 = 0.33439F;

float f2= 0.33439f;

printf("\nf1= %f, f2= %f\n", f1, f2);


double d1= 0.33439;

double d2= 0.33439;

printf("\nd1= %f, d2= %f\n", d1, d2);


/* It doesn't work with MINGW, compiler is broken regarding long
double. */
long double ld1= 0.33439L;

long double ld2= 0.33439l; /*
'l' is the lower case 'L'. */

printf("\nld1= %Lf, ld2= %Lf\n\n", ld1, ld2);


return 0;
}


In my system the sizes differ:


#include <stdio.h>


int main(void)
{
printf("sizeof(0.33439F)= %lu, sizeof(0.33439)= %lu\n\n",
(unsigned long)sizeof(0.33439F), (unsigned long)sizeof(0.33439));


return 0;
}


john@ubuntu:~/Projects/anjuta/c/src$ ./foobar
sizeof(0.33439F)= 4, sizeof(0.33439)= 8

john@ubuntu:~/Projects/anjuta/c/src$
 
I

Ioannis Vranos

Forwarded:

(e-mail address removed) said:



If 0.33439 can be represented exactly in a float, it can also be
represented exactly in a double. In that case, the above code has the
same effect whether the F is present or not.

If 0.33439 cannot be represented exactly in a float, the constant on
the right hand side will be the appropriate float approximation (the
next higher or the next lower representable value). If the F were not
present, the constant on the right hand side would be the appropriate
double approximation (or possibly the exact representation) and when
it is assigned to f1 it will be adjusted to the next higher or the
next lower representable value. I imagine it is possible that a
perverse compiler could choose the lower approximation when the F is
present and the higher approximation when the F is not present.

About the only real benefit for the F is that without it some
compilers (and probably some lint tools) will issue diagnostics
regarding the possible loss of significance.

On the other hand,
float f1 = 0.123456; /*with or without the F*/
float f2 = f1 +0.33439F;
will perform the arithmetic on floats but without the F on the
constant would perform the arithmetic on doubles. This could impact
performance. More importantly, at the edges of significance it could
result is slightly different results.

On the third hand, almost all floating point values are approximations
and should be treated as such.

-- Remove del for email
 
I

Ioannis Vranos

Ioannis said:
If 0.33439 can be represented exactly in a float, it can also be
represented exactly in a double. In that case, the above code has the
same effect whether the F is present or not.

If 0.33439 cannot be represented exactly in a float, the constant on
the right hand side will be the appropriate float approximation (the
next higher or the next lower representable value). If the F were not
present, the constant on the right hand side would be the appropriate
double approximation (or possibly the exact representation) and when
it is assigned to f1 it will be adjusted to the next higher or the
next lower representable value. I imagine it is possible that a
perverse compiler could choose the lower approximation when the F is
present and the higher approximation when the F is not present.

About the only real benefit for the F is that without it some
compilers (and probably some lint tools) will issue diagnostics
regarding the possible loss of significance.

On the other hand,
float f1 = 0.123456; /*with or without the F*/
float f2 = f1 +0.33439F;
will perform the arithmetic on floats but without the F on the
constant would perform the arithmetic on doubles. This could impact
performance. More importantly, at the edges of significance it could
result is slightly different results.

On the third hand, almost all floating point values are approximations
and should be treated as such.

-- Remove del for email


What happens in the case of long double literals? If we use the default double literals we may loose both in
precision and value ranges, on assignment to a long double variable and at calculations.
 
E

Eric Sosman

Ioannis said:
ISO/IEC 9899:1990/1995 says (from K&R2):

“A6.4

When a less precise floating value is converted to an equally or more
precise floating type, the value is unchanged.

==> When a more precise floating value is converted to a less precise
floating type, and the value is within representable range, the result
may be either the next higher or the next lower representable value.

If the result is out of range, the behavior is undefined”.


Question: Does the above mean that it is a good practice or *always*
needed to use the appropriate type suffixes with floating point constants?

"Always" indicates an absolute statement. Never make
absolute statements.

For some decimal literals ###.### it will be the case that

float f1 = ###.###F;
float f2 = ###.###;

will give different values to f1 and f2. In the first line,
###.###F will be rounded[*] to a nearby float value and f1 will
be given this value. In the second, ###.### will be rounded[*]
to a nearby double value which will then be rounded[*] a second
time in the conversion to float; this twice-rounded value will
initialize f2. The singly- and doubly-rounded values may differ.
(For an example in decimal, consider rounding 1.49 to one digit,
yielding 1, or rounding to two digits and then to one, yielding
1.5 and then 2.)

Whether the difference between the singly- and doubly-rounded
values is important depends on the application. The very use of
float instead of double suggests that extreme precision may not
be a primary concern.

[*] A decimal floating-point literal in source converts to
the nearest representable value of its type or to one of the two
values adjacent to that one (6.4.4.2p2). When a double value is
converted to float, the result is one of the two float values
nearest to the double's value (6.3.1.5p2). For lack of a better
word, I've used "rounded" to describe both these perturbations.
 
I

Ioannis Vranos

Eric said:
Ioannis said:
ISO/IEC 9899:1990/1995 says (from K&R2):

“A6.4

When a less precise floating value is converted to an equally or more
precise floating type, the value is unchanged.

==> When a more precise floating value is converted to a less precise
floating type, and the value is within representable range, the result
may be either the next higher or the next lower representable value.

If the result is out of range, the behavior is undefined”.


Question: Does the above mean that it is a good practice or *always*
needed to use the appropriate type suffixes with floating point
constants?

"Always" indicates an absolute statement. Never make
absolute statements.

For some decimal literals ###.### it will be the case that

float f1 = ###.###F;
float f2 = ###.###;

will give different values to f1 and f2. In the first line,
###.###F will be rounded[*] to a nearby float value and f1 will
be given this value. In the second, ###.### will be rounded[*]
to a nearby double value which will then be rounded[*] a second
time in the conversion to float; this twice-rounded value will
initialize f2. The singly- and doubly-rounded values may differ.
(For an example in decimal, consider rounding 1.49 to one digit,
yielding 1, or rounding to two digits and then to one, yielding
1.5 and then 2.)

Whether the difference between the singly- and doubly-rounded
values is important depends on the application. The very use of
float instead of double suggests that extreme precision may not
be a primary concern.

[*] A decimal floating-point literal in source converts to
the nearest representable value of its type or to one of the two
values adjacent to that one (6.4.4.2p2). When a double value is
converted to float, the result is one of the two float values
nearest to the double's value (6.3.1.5p2). For lack of a better
word, I've used "rounded" to describe both these perturbations.


So isn't it better to use the F/f suffix for float constants?
 
E

Eric Sosman

Ioannis said:
So isn't it better to use the F/f suffix for float constants?

Since you cannot write a float constant without F or f,
"better" doesn't arise. It's like asking whether it's better
to have one thing or two things in a pair of things.
 
K

Keith Thompson

Eric Sosman said:
Since you cannot write a float constant without F or f,
"better" doesn't arise. It's like asking whether it's better
to have one thing or two things in a pair of things.

Sure, if you interpret the question painfully literally. Of course a
floating constant with no suffix is of type double.

The real question is whether using the F/f suffix is better than not
using it for a constant used to initialize an object of type float.
I'd say that in most cases it doesn't matter much, but the suffix can
have some slight advantages.
 
J

James Kuyper

Eric Sosman wrote:
....
Whether the difference between the singly- and doubly-rounded
values is important depends on the application. The very use of
float instead of double suggests that extreme precision may not
be a primary concern.

The use of float indicates only that the precision provided by float is
sufficient. It doesn't necessarily imply that having any less precision
that that is acceptable. In some cases, the precision provided by float
is just barely acceptable, and storing any value other than the best
float approximation is unacceptable.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,754
Messages
2,569,528
Members
45,000
Latest member
MurrayKeync

Latest Threads

Top