C# Compiler Number Literals

C# compiler number literals

var y = 0f; // y is single
var z = 0d; // z is double
var r = 0m; // r is decimal
var i = 0U; // i is unsigned int
var j = 0L; // j is long (note capital L for clarity)
var k = 0UL; // k is unsigned long (note capital L for clarity)

From the C# specification 2.4.4.2 Integer literals and 2.4.4.3 Real literals. Take note that L and UL are preferred as opposed to their lowercase variants for clarity as recommended by Jon Skeet.

C# floating point literals : Why compiler does not DEFAULTS to the left hand side variable type

Someone else here can probably explain why the .NET developers decided against using some sort of prediction to determine how a number literal should be shoehorned into the type you're providing to it. But in the absence of any sort of philosophy, the answer is simple: they didn't want to do that, so they didn't. I'm not going to tell you that you outright shouldn't care about this, because of course it's not totally unreasonable to want a compiler to understand exactly what you want. It'd at least be nice, right?

(If I had to guess, though, I'd say it has a lot to do with allowing some sort of logical implicit typing - float f = 1.0 is float, but var d = 1.0 is double? This gets even less logical when you consider that f.Equals(d) would be false, despite the fact that 1.0 == 1.0 is true.)

But, assuming they had good reasons not to introduce any type-definition-specific prediction (and some more perfectly good reasons can be found in Rotem's post), the best reason I can imagine they decided that double num1 = 1.23 is acceptable, and float num1 = 1.23 is not, is that double is frankly more useful in most cases. The CPU and IO penalties of using a 64-bit floating point value as opposed to 32-bit are negligible in most use cases, but the usefulness of not having to worry about exceeding the bounds of that data type is significant.

Note that you could make the exact same argument about why 1.23 can't be assigned to a decimal without a suffix. The people who designed C# decided it was easier to make assumptions about the type of a number literal of any given format, and double is a totally reasonable assumption given that the majority of people who write 1.23 want a double, or at least should be using one.

edit: user fixed title.

C# short/long/int literal format?

var d  = 1.0d;  // double
var d0 = 1.0; // double
var d1 = 1e+3; // double
var d2 = 1e-3; // double
var f = 1.0f; // float
var m = 1.0m; // decimal
var i = 1; // int
var ui = 1U; // uint
var ul = 1UL; // ulong
var l = 1L; // long

I think that's all... there are no literal specifiers for short/ushort/byte/sbyte

Why some types do not have literal modifiers

Why long int has a literal modifier, but short int does not?

The question is "why does C# not have this feature?" The answer to that question is always the same. Features are unimplemented by default; C# does not have that feature because no one designed, implemented and shipped the feature to customers.

The absence of a feature does not need justification. Rather, all features must be justified by showing that their benefits outweigh their costs. As the person proposing the feature, the onus is on you to describe why you think the feature is valuable; the onus is not on me to explain why it is not.

Probably there is a strong reason to provide literal modifiers for some types, but not for all. What is it?

Now that is a more answerable question. Now the question is "what justifies the literal suffix on long, and why is that not also a justification for a similar literal suffix on short?"

Integers can be used for a variety of purposes. You can use them as arithmetic numbers. You can use them as collections of bit flags. You can use them as indices into arrays. And there are lots of more special-purpose usages. But I think it is fair to say that most of the time, integers are used as arithmetical numbers.

The vast majority of calculations performed in integers by normal programs involve numbers that are far, far smaller than the range of a 32 bit signed integer -- roughly +/- two billion. And lots of modern hardware is extremely efficient when dealing solely with 32 bit integers. It therefore makes sense to make the default representation of numbers to be signed 32 bit integers. C# is therefore designed to make calculations involving 32 bit signed integers look perfectly normal; when you say "x = x + 1" that "1" is understood to be a signed 32 bit integer, and odds are good that x is too, and the result of the sum is too.

What if the calculation is integral but does not fit into the range of a 32 bit integer? "long" 64 bit integers are a sensible next step up; they are also efficient on a lot of hardware and longs have a range that should satisfy the needs of pretty much anyone who isn't doing heavy-duty combinatorics that involve extremely large numbers. It therefore makes sense to have some way to specify clearly and concisely in source code that this literal here is to be treated as a long integer.

Interop scenarios, or scenarios in which integers are used as bit fields, often require the use of unsigned integers. Again, it makes sense to have a way to clearly and concisely specify that this literal is intended to be treated as an unsigned integer.

So, summing up, when you see "1", odds are good that the vast majority of the time the user intends it to be used as a 32 bit signed integer. The next most likely cases are that the user intends it to be a long integer or an unsigned int or unsigned long. Therefore there are concise suffixes for each of those cases.

Thus, the feature is justified.

Why is that not a justification for shorts?

Because first, in every context in which a short is legal, it is already legal to use an integer literal. "short x = 1;" is perfectly legal; the compiler realizes that the integer fits into a short and lets you use it.

Second, arithmetic is never done in shorts in C#. Arithmetic can be done in ints, uints, longs and ulongs, but arithmetic is never done in shorts. Shorts promote to int and the arithmetic is done in ints, because like I said before, the vast majority of arithmetic calculations fit into an int. The vast majority do not fit into a short. Short arithmetic is possibly slower on modern hardware which is optimized for ints, and short arithmetic does not take up any less space; it's going to be done in ints or longs on the chip.

You want a "long" suffix to tell the compiler "this arithmetic needs to be done in longs" but a "short" suffix doesn't tell the compiler "this arithmetic needs to be done in shorts" because that's simply not a feature of the C# language to begin with.

The reasons for providing a long suffix and an unsigned syntax do not apply to shorts. If you think there is a compelling benefit to the feature, state what the benefit is. Without a benefit to justify its costs, the feature will not be implemented in C#.

Why should we use literals in C#?

The "f" above is a type suffix. This tells the compiler the exact type of the literal provided. This is used so the compiler can allocate the appropriate amount of storage (precision) for the literal. By default, floating point literals are given storage for a "double." If you add "f" as a suffix, the literal will only get the storage for a float, which will have less accuracy.

double d = 50.1234; // given double storage
double d = 50.1234f; // given float storage, which might lose precision compared to double

Literal suffix for small integers

The two code samples are identical.

This is a widening conversion, meaning that it cannot possibly cause data loss, and therefore the compiler can handle it automatically. All widening conversions are implicit conversions. You can read more about data type conversions in the .NET Framework here.

The following would also be a widening conversion, and therefore done implicitly:

int  value = 16;
long other = value; // the int is implicitly converted to a long

All of these widening conversions are possible, and done implicitly:

 byte          => short, short, int, uint, ulong, float, double, decimal
sbyte => short, int, long, float, double, decimal
short => int, long, float, double, decimal
ushort => int, uint, long, ulong, float, double, decimal
int => long, float, double, decimal
uint => long, ulong, float, double, decimal
long => float, double, decimal
ulong => float, double, decimal
float => double

You mention casting in the question. Casting is explicit conversion (as opposed to implicit conversion). In this case, there is no casting, as the conversion is implicit. But even if there were casting, there would be no cost in this case, since these are numeric literals. All of the casting would be done at compile time. This code would be functionally equivalent, too:

for (uint div = (uint)0; div < (uint)100; div += (uint)2)
{
}

but not something you should write, since the pointless casting adds unnecessary clutter that obscures the meaning of the code.

C# compiler does not limit the number of digits of fractional part of a floating-point literal

  1. Yes, but they aren't decimal digits
  2. Specification of fractional parts beyond the ability to exactly represent them is easy, when the specification is decimal and the representation is binary. 0.3 already requires approximation.

C# Number Literals

I just tried it. C# 3.0 generates the following IL for your first call:

ldc.r8 1.
call instance void ConsoleApplication1.Program::Method(float64)

So, no runtime conversion.

How are integer literals stored?

Does a short integer literal in C# ...

You cannot declare a literal of type short so the question is iffy. In practice, the C# compiler will treat it like an int (or long if large enough) and readily convert it to byte or short where appropriate without a cast. And complain when such a conversion causes an overflow.

It will end up getting encoded in a processor instruction like MOV or PUSH, depending on how you use the literal. A practical example, use the Debug > Windows > Disasssembly window to see it:

    static void Main(string[] args) {
Console.WriteLine(12);
}

Generates:

  005B2DB0 B9 0C 00 00 00       mov         ecx,0Ch  
005B2DB5 E8 7E 38 BE 72 call 73196638
005B2DBA C3 ret

Note the MOV instruction and the instruction bytes it generates. B9 is the "move 32-bit immediate" instruction, the next 4 bytes are the value in little-endian order. Otherwise selected because the C# compiler used the WriteLine(Int32) overload, it does not have overloads for byte or short. Use the same technique to see what happens with your specific code.

Strange compiler behavior with float literals vs float variables

Your question can be simplified to asking why these two results are different:

float f = 2.0499999f;
var a = f * 100f;
var b = (int)(f * 100f);
var d = (int)a;
Console.WriteLine(b);
Console.WriteLine(d);

If you look at the code in .NET Reflector you can see that the above code is actually compiled as if it were the following code:

float f = 2.05f;
float a = f * 100f;
int b = (int) (f * 100f);
int d = (int) a;
Console.WriteLine(b);
Console.WriteLine(d);

Floating point calculations cannot always be made exactly. The result of 2.05 * 100f is not exactly equal to 205, but just a little less due to rounding errors. When this intermediate result is converted to an integer is truncated. When stored as a float it is rounded to the nearest representable form. These two methods of rounding give different results.


Regarding your comment to my answer when you write this:

Console.WriteLine((int) (2.0499999f * 100f));
Console.WriteLine((int)(float)(2.0499999f * 100f));

The calculations are done entirely in the compiler. The above code is equivalent to this:

Console.WriteLine(204);
Console.WriteLine(205);


Related Topics



Leave a reply



Submit