Two different things are going on here.
1) Some letters when stuck on the end of a number take on meaning. 'l' is for long, 'u' is for unsigned, and 'f' is for float.
- "Long" is generally 64 bits wide vs int's 32 bits... but that can
vary wildly from machine to machine. DO NOT depend on bit width of
int and long.
- "Unsigned" means it doesn't bother to track positive or
negative values... assuming everything is positive. This about
doubles how high an integer can go. Look up "two's complement" for
further information.
- "Float" means "floating point". Non whole numbers. 1.5, 3.1415, etc. They can be very large, or very precise, but not both. Floats ARE 32 bits. "Double" is a 64-bit floating point value, which can permit some extreme values of size or precision.
2) Type Coercion, pronounced "co ER shun".
The compiler knows how to convert (coerce) from long to int, unsigned to int, or float to int. They're all just numbers, right? Note that converting from float to into "truncates" (drops) anything after a decimal place. ((int)3.00000001) == 3
. ((int)2.9999999) == 2
If you dial your warnings up to max sensitivity, I believe those statements will all trigger warnings because all those conversions could potentially lose data... though the exact phrasing of that warning will vary from compiler to compiler.
Bonus Information:
You can trigger this same behavior (accidentally) with classes.
struct Foo {
Foo(int bar) {...}
};
Foo baz = 42;
The compiler will treat the above constructor as an option when looking to convert from int to Foo. The compiler is willing to hop through more than one hoop to get there... so Foo qux = 3.14159;
would also compile. This is also true of other class constructors... so if you have some other class that takes a foo as it's only constructor param, you can declare a variable of that class and assign it something that can be coerced to a foo... and so on:
struct Corge {
Corge(Foo foo) {...}
};
corge grault = 1.2345; // you almost certainly didn't intend what will happen here
That's three layers of coercion. double to int, into to foo, and foo to corge. Bleh!
You can block this with the explicit
keyword:
struct Foo {
explicit Foo(int bar) {...}
};
Foo baz = 1; // won't compile
I wish they'd made explicit
the default and used some keyword to define conversion constructors instead, but that change would almost certainly break someone's code, so it'll never happen.