suffixes for intgers and floating point

1. Literal constant
What exactly is literal constant?

int x = 35;
Literal constants are literal numbers inserted into the code. They are constants because you can’t change their values.


Q1: But can't we change those values through the means of code (code operation, not directly by a human)?

Q2: Are we suggesting that a literal constant is anything that we inserted and declared in a code, such as int x = 35, char character = 'a' ?

Suffixes
So what is the idea behind this use of suffix?
In compilers using the amount of storage for double and long double numbers, these two data types are identical. A float literal is indicated by appending an f or F to the number, and a long double is created by appending an l or L to the number. In the absence of these suffixes, a floating-point number defaults to a double. For example,

9.234 indicates a double literal
9.234F indicates a float literal
9.234L indicates a long double literal

The only difference in these numbers is the amount of storage the computer can use for them.


I am confused by this explanation. I know there are three types of floating-point data types: float, double, and long double.

2(a). For the following code, are we getting a double, or a float?
float x = 4.232;


2(b). Are we allow to do this?
float x = 4.232L; // added L


2(c): Can someone please explain what exactly does the suffix serve in programming?

Thank you.
But can't we change those values through the means of code (code operation, not directly by a human)?
No. The value of, for example, '35' is fixed.

2a. double.
2b. Yes. It will just perform an implicit conversion while giving a data loss warning.
2c. Mostly to prevent implicit conversions. For example:
1
2
3
float a=3.0f;
float b=a/3.0;
float c=a/3.0f;
b and c end up with the same value, but computing c was faster because the right hand operand for the division on line 2 was a double. Since operands are always converted to the larger type, the value of a will be converted to double before performing the division. The result of the division will then be converted back to float to assign it to b.
To compute c, both a and 3.0f have the same type, so no conversion is necessary. The result of the division also has the same type as c, so again no conversion is necessary.
2(a). If your compiler is retarded, it will convert double to float. It most likely is not.
2(b). Same here
2(c). Doesn't seem like it. I tested this code, and it produced identical disassembly for both b and c.

FPU registers are 80 bits if I recall correctly, so conversion is inevitable. Though maybe if there was a 10 byte floating point value, that could be faster..

As for uses, I can only think of templates or overloaded functions.
Hi, thank you for the reply.

Q1: So the literal constant is a plain scalar that we don't change throughout the program? An initialization of int x = 35 may be a literal constant, if and only if x is not assigned to a different value later?

Q2: Referring back to 2a. So what about float a? Is it still a double?

Q3: To determine the use of float over double, we shall consider the amount of precision, right? A float is usually 7 (or 6 some sources), and for double it's at least 15.

Q4: Referring back to 2c, and your explanation
I see how the conversion is done, and that the default for the right operand is a double.

If neither a nor the right operand (3.0 or 3.0f) contains the suffix F, such as
1
2
float a = 3.0;
float b = a / 3.0

Are we expecting a double-precision number returned to b after the division, since both a and 3.0 are double-precision?

Q5: When we say float a = 3.0, we are stating that a has a double precision, which is usually 3.00000000000000 15 significant digits, right?

Q6: Is it faster to just assign double a = 3.0, and double b = a / 3.0?

Thank you.



A1: x is not a constant. 35 is.

A2: when you have a float, it stays float, no matter what you assign to it.

A3: Yes. Required precision and available memory have to be considered.

A4: see A2. a and b are floats.

A5: No, because a is float. helios claims, that 3.0 has a double precision and is then converted to single precision and only then assigned to a.

A6: Probably no, though whichever is faster, the difference should be negligible. I'll run some tests..
1. No. The literal constant is the number '35'. That's why it's called literal, instead of symbolic. The number itself is the constant.
2. My mistake. I didn't see that x was a float. See my previous answer to 2b.
3. More or less, but the precision of a floating point value cannot be easily expressed in those terms.
4.
If neither a [...] contains the suffix F
Only literals can have suffixes applied to them. Not variables.
Are we expecting a double-precision number returned to b after the division, since both a and 3.0 are double-precision?
Yes, but a isn't a double. The value of a is copied to a temporary double to perform the division.
5. No. The literal '3.0' is the one with double precision. 'a' still has single precision.
6. Yes.
Last edited on
I ran some tests.

I did this: for(unsigned int i = 0; i < 0x7FFFFFF; i++) f = 0.5;

The times I got were 410 ms for floats and 488 for doubles.
I tried this both with and without suffixes. There was no difference.

I did the same thing, but with division. I got pretty much identical times with any combination.
Thank you for all these great inputs, and I think I get the ideas:

In essence, the suffix can only applied to constant.

The type float is 4-bytes, and therefore the range of value that a float can take is a lot smaller than a double, which is 8-bytes, and the range is therefore much larger (2^n).

Since a float typically has 7-digits of precision (and my compiler prints up to 6) and a double has 15...

1
2
3
4
        cout << setprecision(16);
	cout << 3.2323232323232323236456543534f << endl;
	cout << 3.23232323232323232364565435345656756645654645645646456 << endl;
	cout << 3.23232323232323232364565435345656756645654645645646456l << endl;


This will output

 ----   Hit any key to start.
3.232323169708252
3.232323232323232
3.232323232323232

 ----   Hit any key to continue.

I can see how the prefix plays the role to the literal constant.

However, if we declar something like
 
float a = 3.23232323232323232364565435345656756645654645645646456l

This will print out the exact output as the first one


 ----   Hit any key to start.
3.232323169708252

Instead of 3.232323232323232


I guess the whole purpose of having a f or a L for the floating literal is to keep the precision, if we don't want to allocate that many exceeded spaces using a double a_variable.

I guess if this is the code
 
float a_= numberL; // with suffix L 


The compiler first temporary stores number in a long-double precision and then when we cout a, we are getting the float (single-precision) form of number, which some of the digits are loss.

Am I correct?

If it is, how does the pc stores that temporary precision?





The compiler first temporary stores number in a long-double precision and then when we cout a, we are getting the float (single-precision) form of number, which some of the digits are loss.
Not quite.
float a=3.0 is translated to this:
1. Set up temporary of size double. We'll call it z.
2. Assign 3.0 to z.
3. Convert z to a float. z is now half as big.
4. Allocate a on the stack.
5. Assign z to a.
The precision is lost the moment a is assigned, not when a is printed.
As hamsterman has pointed out, the compiler may optimize some of these steps away, but that's not required.

As for where temporaries are stored, that depends a lot on the platform and program context. Temporary values for floating point operations on x86 are stored in FPU registers, which have a precision of 80 bits, IIRC.
Last edited on
Hi helios,

Thank you.

I think I am very clear, except I really want to confirm on the #1 step. I also understand precisions, and bytes and all that can be different depending on the architecture, compiler and system the program is running on.

Please bear with me, and thank you.


1. Set up temporary of size double. We'll call it z.

When this happen, which form does the compiler takes in? 3.0 or 3.000000xxxxxxxxxxxxx (up to 15 significant digits)?



3. Convert z to a float. z is now half as big.

I thought when we convert z to a float, we would lose the precision?

I am sorry if I am being dumb lol...

3.0 or 3.000000xxxxxxxxxxxxx (up to 15 significant digits)?
They both mean the same thing. '3.0' is just a token that gets translated to a string of bytes at compile time, in the same way that '9999' gets translated to 0F 27 00 00 (it may get translated to something different. This is just an example). What this means is that 3.1111 doesn't use more precision than 3.11. They're both doubles.
You should also stop thinking in decimal digits. They'll just cause you trouble down the road. Just to name one example, decimal 0.1 cannot be exactly represented as a floating point value because it's a repeating decimal in binary. The exact value in binary is 0.0 0011 0011 (0011) ... Since computers don't have infinite capacity, only an approximation to this value can be stored.

I thought when we convert z to a float, we would lose the precision?
That's right. Step #3 in my previous post doesn't contradict that fact.
Last edited on
Hi,

Thank you for your explination. I think as of now I will take these facts for granted, because it requires a very solid understanding of binary and numerical computation.

Thank you.

Jwxie
Topic archived. No new replies allowed.