26

I am new to C# and want to understand how values work. If I look at a normal integer value, it has 3 important parts in it: the type, name and value.

int testInt = 3;
 |    |       |
Type Name   Value

But when I see a float value it confuses me a bit because of the suffix F.

float testFloat = 3.0F;
  |      |        |  |
 Type   Name  Value Type

Now there are two types in it, and without the F suffix the value would be a double. But why is this happening when I can declare the double variable with

double testDouble = 3.0D;

The double as the first word should be enough, shouldn't it? The same goes for the decimal value with the suffix M:

decimal testDecimal = 3.0M;

Then it starts really confusing me when it comes to the other suffixes:

ulong bigOne = 2985825802805280508UL;

I used ulong in a test before and know that the u is for "unsigned" and lets the value be twice as high as normal. Then you get the U again as suffix and the L for literal as google said. As I understand it, "literals" are value types that contain numbers. But what I don't understand is, why does this ulong work even without the suffix?

ulong bigOne = 2985825802805280508;

Then I tried something different to understand the importance of the suffix

byte testLong = 12312UL;

This didn't work because the value is too high for byte (254) and the suffix does not convert it to an long variable.

Why isn't the first word (type) not enough for a declaration? The first word should be enough to tell the type. Is the best practice to always give the values a suffix?

Peter Olson
  • 139,199
  • 49
  • 202
  • 242
user3772108
  • 854
  • 2
  • 14
  • 32
  • 3
    You're right, one type declaration is enough when there's also an assignment. That's why the var keyword was added to C#: https://msdn.microsoft.com/en-us/library/bb383973.aspx – Max Jan 06 '16 at 16:29
  • 3
    `U` is for unsigned, not unassigned. – Lee Taylor Jan 06 '16 at 16:32
  • 1
    Also what about calculations like 4.0 / 3? Should it use single or double precision? That really influences the result. Or consider 2000000000 * 3? Is it an overflow or just an long? You don't always directly appoint values. – dryman Jan 06 '16 at 16:32
  • 1
    Those are basically runtime casts to give directions to the compiler. 1000 is an int but if you initialize it as long you need to put an L at the end of it. You can find more [here](http://www.dotnetperls.com/suffix). – Mihai-Daniel Virna Jan 06 '16 at 16:32
  • Thanks for the tipp Lee Taylor I corrected it. – user3772108 Jan 06 '16 at 16:35
  • I will take a look at the links. Thanks Max and Mihai-Daniel Virna. – user3772108 Jan 06 '16 at 16:35
  • Thanks dryman but I already told that I want a double, float,.. at the beginning of the declaration i think? But with the link from Max it makes more sense. The first word is just something to start the declaration and the parts behind the = are important for its type. When I understand correct? – user3772108 Jan 06 '16 at 16:42
  • 1
    @user3772108 The fact that you've stated that you're creating a variable of type `float` does *not*, and can not, necessarily determine the type of the numeric literal you assign to it. – Servy Jan 06 '16 at 16:46

3 Answers3

27

You are confusing two different things here:

float testFloat = 3.0F;

The float tells the compiler that the variable testFloat will be a floating point value. The F tells the compiler that the literal 3.0 is a float. The compiler needs to know both pieces before it can decide whether or not it can assign the literal to the variable with either no conversion or an implicit conversion.

For example, you can do this:

float testFloat = 3;

And that's okay. Because the compiler will see 3 as a literal integer, but it knows it can assign that to a float without loss of precision (this is implicit conversion). But if you do this:

float testFloat = 3.0;

3.0 is a literal double (because that's the default without a suffix) and it can't implicitly (i.e. automatically) convert a double to a float because a float has less precision. In other words, information might be lost. So you either tell the compiler that it's a literal float:

float testFloat = 3.0f;

Or you tell it you are okay with any loss of precision by using an explicit cast:

float testFloat = (float)3.0;
Matt Burland
  • 44,552
  • 18
  • 99
  • 171
  • 4
    Exactly. If you do `var result = 4 / 3;`, the compiler must decide whether it's an integer or a float operation, and that only depends on what's on the right side of the assign operator. By default, integer values are considered `int` and decimal values are considered `double`. So that will make `result` an `int` of 1. If you do `4 / 3.0` instead, `result` will be a `double` with a 1.3333333 value. – Andrew Jan 06 '16 at 16:43
  • Now I understand better thanks. I never did a declaration with a calculation in it. Thought the dot between the numbers must be enough to understand for the compiler. – user3772108 Jan 06 '16 at 16:46
  • But even with the calculation example. The float at the beginning should be enough to tell I want a float? Because Byte to Long with just a suffix does not work? – user3772108 Jan 06 '16 at 17:07
  • 1
    @user3772108: No, it isn't. Because the compiler needs to know what the left-hand side and the right-hand side are before it can decide if the assignment is valid. As in Servy's answer, it can't decide the type of the right-hand side based on the context of the left-hand side. – Matt Burland Jan 06 '16 at 17:10
10

All1 expressions need to be resolvable to a type. So the expression 42 always needs to have exactly one type (it happens to be an int). It can't be an int if you assign it to an int variable and a double if you assign it to a double. The context that an expression is used in is never1 used to determine what type it resolves to.

This is why numeric literals can have suffixes; it's a way of defining the type of that expression in that expression.

Note that there are also implicit conversions between many of the numeric types, so if you write double d = 42; the expression 42 is actually an integer, but there is an implicit conversion operator being performed on it that will convert it into a double before the assignment.

1 There are a few exceptions here, such as lambdas, to which the type of the expression is dependent on how its used, and method groups; in a vacuum these expressions have no type.

Servy
  • 202,030
  • 26
  • 332
  • 449
2

Exists other way to declare a variable without specify the type before the name:

var myNumber = 10;

In this case, the variable type will be defined by the literal value.

If you use the type (double|float|int|...) instead "var", the compiler make a conversion of literal value to variable type (when is possible).

So, I think that suffix is important when you use "var" to declare variables and the literal value type is not the default associated when the suffix is not used;

There is another reason when use suffix is too useful, like in situation that you want make implicit conversions in expressions.

Gean Ribeiro
  • 1,025
  • 2
  • 10
  • 23
  • 3
    This is one situation in which the need to distinguish different numeric literals via suffixes is relevant, but it's not the *reason*, and it's certainly not the only such situation (after all, `var` was added several versions into the language, but the need for suffixes has been there since the start). – Servy Jan 06 '16 at 16:44
  • So I can declare every variable just with var and the correct suffix? – user3772108 Jan 06 '16 at 17:05
  • 1
    @user3772108 In most of case yes, there are some exceptions that you can not, like define a Func. But the most of declarations can be resolved with `var`, example: `var arrayOfInt = new [] {1,2,3}`. Can not use example: `Func add = (a,b) => a+b`, in this case you must define the variable type. You can learn more about `var` here https://msdn.microsoft.com/en-us/library/bb383973.aspx – Alberto Monteiro Jan 06 '16 at 17:07
  • And if I want a decimal in the array it would be `var arrayOfInt = new [] {1,2.0M,3}` ? – user3772108 Jan 06 '16 at 17:11
  • Thanks for the link Alberto Monteiro I will take a look. – user3772108 Jan 06 '16 at 17:18
  • @AlbertoMonteiro There is no local variable that cannot be declared using `var`. In the case of a lambda you'd need to alter the right hand side of the assignment, but you can always create a situation where `var` can be used. – Servy Jan 06 '16 at 17:25
  • 1
    Yes, I can do it like that `var func = new Func((a,b) => a+b)`. This other statement is valid `Func add = (a,b) => a+b`. But this statement is not possible `var func = (a,b) => a+b`. So there are. – Alberto Monteiro Jan 06 '16 at 17:52