I came across this funny behavior of the compiler:
If I have
public int GetInt()
{
Random rnd = new Random();
double d = rnd.NextDouble();
int i = d % 1000;
return i;
}
I get an error of:
Cannot implicitly convert type 'double' to 'int'. An explicit conversion exists (are you missing a cast?)
which actually makes sense as 1000 can be a double, and the result of the modulo operator might be a double as well.
But after changing the code to:
public int GetInt()
{
Random rnd = new Random();
double d = rnd.NextDouble();
int i = d % (int)1000;
return i;
}
The error persists.
As far as I can tell, the compiler has all of the information in order to determine that the output of the modulo operator will be an int, so why doesn't it compile?
if d is == to 1500.72546 then the result of the calculation int d % (int)1000 would be 500.72546 so then implicitly casting to an int would result in a loss of data.
This is by design. See C# language specification:
7.3.6.2 Binary numeric promotions
Binary numeric promotion occurs for the operands of the predefined +,
–, *, /, %, &, |, ^, ==, !=, >, <, >=, and <= binary operators. Binary
numeric promotion implicitly converts both operands to a common type
which, in case of the non-relational operators, also becomes the
result type of the operation. Binary numeric promotion consists of
applying the following rules, in the order they appear here:
...
Otherwise, if either operand is of type double, the other operand is converted to type double.
The compiler can tell the result is an integer here, but is being your friend.
Sure, it can automatically convert it for you and not tell you about it and in your trivial example this would be just fine. However, given that converting a double to an int means a LOSS of data, it might well have not been your intention.
If it had not been your intention and the compiler had just gone ahead and done the conversion for you, you could have ended up in a marathon debugging session, devoid of sanity - trying to figure out why a rather esoteric bug as been reported.
This way, the compiler is forcing you to say, as a programmer, "I know I will lose data, it's fine".
Compiler will assume you don't want to loose precision of the operation and implicitly use double.
http://msdn.microsoft.com/en-us/library/0w4e0fzs.aspx
From the documentation:
http://msdn.microsoft.com/en-us/library/0w4e0fzs.aspx
The result of a modulo of a double will be a double. If you need an integer result, then you must:
int i = (int)(d % 1000);
But bear in mind that you are liable to lose data here.
As I slowly engage my brain here - your code doesn't make any sense. NextDouble() will return a value between 0 and 1. There are some logical issues with what you are doing, the result will always be zero, e.g.:
0.15 % 1000 = 0.15
Cast 0.15 to int (always rounds towards zero) -> 0
double d = rnd.NextDouble();
int i = d % (int)1000;
This code doesn't makes sense. (int)1000 says 1000 is int, but d % (int)1000 says that oh d is double, so compiler has to convert both into a common type Binary numeric promotions mentioned in another answer to make it work.
One thing to understand is that you can't apply any operations with different types, so compiler will convert implicitly for you if there is no loss of data. So (int)1000 will be still converted to double by the compiler before applying operation. so the result will be of type double not int.
Related
If you put the following code in a .NET 4.5 application:
public const long MAXIMUM_RANGE_MAGNITUDE = int.MaxValue + 1;
A compiler error is generated stating "The operation overflows at compile time in checked mode". I know that I could put this in an "unchecked" block and be fine, but my question is why does the error appear in the first place? Clearly a long can hold a int's max value plus one.
Note that using Int32 and Int64 instead of long and int does not seem to help.
It is because the calculations on the right hand side of assignment is being done in integer type. And it is overflowing integer
You can fix that with:
public const long MAXIMUM_RANGE_MAGNITUDE = int.MaxValue + (long)1; // or 1L
By casting at least one of the operand to long
The reason you get the error is specified in C# specifications.
See C# Specification Section 4.1.5 (Integral types)
For the binary +, –, *, /, %, &, ^, |, ==, !=, >, <, >=, and <=
operators, the operands are converted to type T, where T is the first
of int, uint, long, and ulong that can fully represent all possible
values of both operands. The operation is then performed using the
precision of type T, and the type of the result is T (or bool for the
relational operators). It is not permitted for one operand to be of
type long and the other to be of type ulong with the binary operators.
In your case since both operands of addition can be represented in int therefore the calculation is done in integer type. Explicitly casting one of the operand to long would result in long result and thus no overflow error.
Your code in fact looks like this:
(long)(int.MaxValue + 1)
But because .Net framework has an inbuilt implicit conversion between int and long you do not have to explicitly put a cast to long in your code.
So firstly this part of the code is executed:
int.MaxValue + 1
and the result of this operation is an int value which causes and overflow exception.
So your code does not even have a chance to start the conversion from int to long.
I think this has to do with the value of int.MaxValue + 1 being calculated before the cast to long is made. Certainly a long can hold the value, but because you are doing integer addition, there is no way to store the integer value of int.MaxValue + 1 in an int until the cast is made.
try
public const long MAXIMUM_RANGE_MAGNITUDE = (long)int.MaxValue + 1L;
Cast the constant value to a long.
public const long MAXIMUM_RANGE_MAGNITUDE = (long) int.MaxValue + 1;
Could someone point me, why here:
Byte b = 100;
b = (Byte)(b+200);
I have to use explicit type conversion. But here
Byte b = 100;
b += 200;
I don't need to do this?
Does compiler generate different IL code for this two cases? And which case is better?
Because the standard permits it (see the second case below):
14.14.2 Compound assignment
An operation of the form x op= y is processed by applying binary operator overload resolution (§14.2.4) as if the operation was written x op y. Then,
If the return type of the selected operator is implicitly convertible to the type of x, the operation is evaluated as x = x op y, except that x is evaluated only once.
Otherwise, if the selected operator is a predefined operator, if the return type of the selected operator is explicitly convertible to the type of x, and if y is implicitly convertible to the type of x or the operator is a shift operator, then the operation is evaluated as x = (T)(x op y), where T is the type of x, except that x is evaluated only once.
Otherwise, the compound assignment is invalid, and a compile-time error occurs.
The IL code should be essentially identical in this case. Of course, if evaluating b has side effects, it will be evaluated twice in the b = (byte)b + 200 case and only once when using compound assignment.
This is a FAQ in the C# tag, hard to find the duplicate. The need for the cast is relevant first. The underlying reason is that the CLI only specifies a limited number of valid types for the Opcodes.Add IL instruction. Only operands of type Int32, Int64, Single, Double and IntPtr are supported. IntPtr is special as well, the C# language forbids using that one.
So the C# compiler has to use an implicit conversion to uplift the byte to a type that the operator supports. It will pick Int32 as the closest compatible type. The result of the addition is Int32. Which does not fit back into a byte without truncating the result, throwing away the extra bits. An obvious example is 255 + 1, the result is 256 in Int32 but doesn't fit a Byte and yields 0 when stored.
That's a problem, the language designers didn't like that truncation to happen without you explicitly acknowledging that you are aware of the consequences. A cast is required to convince the compiler that you're aware. Bit of a cop-out of course, you tend to produce the cast mechanically without thinking much about the consequences. But that makes it your problem, not Microsoft's :)
The rub was the += operator, a very nice operator to write condense code. Resembles the brevity of the var keyword. Rock and a hard place however, where do you put the cast? It just doesn't work so they punted the problem and allowed truncation without a cast.
Notable is the way VB.NET works, it doesn't require a cast. But it gives a guarantee that C# doesn't provide by default, it will generate an OverflowException when the result doesn't fit. Pretty nice, but that check doesn't come for free.
Designing clean languages is a very hard problem. The C# team did an excellent job, warts not withstanding. Otherwise the kind of warts brought on by processor design. IL has these type restrictions because that's what real 32-bit processors have too, particularly the RISC designs that were popular in the 90s. Their internal registers can only handle 32-bit integers and IEEE-754 floating point. And only permit smaller types in loads and stores. The Intel x86 core is very popular and actually permits basic operations on smaller types. But that's mostly a historical accident due to Intel keeping the design compatible through the 8-bit 8080 and 16-bit 8086 generations. It doesn't come for free, 16-bit operations costs an extra cpu cycle. To be avoided.
The spec calls this out as a specific case of the compound assignment operator:
http://msdn.microsoft.com/en-us/library/aa691316%28v=vs.71%29.aspx
Specifically, bullet 2:
Otherwise, if the selected operator is a predefined operator, if the
return type of the selected operator is explicitly convertible to the
type of x, and if y is implicitly convertible to the type of x, then
the operation is evaluated as x = (T)(x op y), where T is the type of
x, except that x is evaluated only once.
The second rule above permits x op= y to be evaluated as x = (T)(x op
y) in certain contexts. The rule exists such that the predefined
operators can be used as compound operators when the left operand is
of type sbyte, byte, short, ushort, or char. Even when both arguments
are of one of those types, the predefined operators produce a result
of type int, as described in Section 7.2.6.2. Thus, without a cast it
would not be possible to assign the result to the left operand.
This because of the implicit conversion rules.
When you have a binary + operator the result is converted to the larger of the two types.
The literal 200 is of type int and therefore the type of the expression b+200 is int.
The assignment operator = doesn't do implicit conversion, but rather throws error. As in
int x = 10;
Byte b = x; //Error
In the second case the += operator expects byte, so 200 (which is of type int, but fits into byte) is implicitly converted to byte, because the compiler knows it can. The following won't compile, because the compile doesn't know if x will fit in a byte or not.
Byte b = 100;
int x = 200;
b += x; //Error
If you make x a const then it compiles:
Byte b = 100;
const int x = 200;
b += x; //OK
is
if(float > int)
really just
if(float > (float)int)
I was doing so research and it seems like it costs a lot to do float to int and int to float casts.
I have a lot of float/int comparisons.
Just a quick question
Yes!
They're the same thing.
There's no instruction to directly compare a floating-point to an integer, so it first casts the integer to float.
However:
Be careful: That does not mean that the int-to-float conversion is lossless. It still can lose some information, so this code:
(int)(float)integer == integer
doesn't always evaluate to true! (Try it with int.MaxValue to see. Ditto with double/long.)
Yes. There's no >(float, int) operator - just >(int, int) and >(float, float). So the compiler calls the latter operator by converting the second operand to float. See section 7.3.6.2 of the C# spec for more details:
Binary numeric promotion occurs for the operands of the predefined +, -, *, / %, &, |, ^, ==, !=, >, <, >= and <= binary operators. Binary numeric promotion implicitly converts both operands to a common type which, in case of the nonrelational operators, also becomes the result type of the operation.
(It then lists the steps involved.)
Are you sure that the int to float conversion is taking a lot of time though? It should be pretty cheap.
1) If one operand is of type ulong, while the other operand is of type sbyte/short/int/long, then compile-time error occurs. I fail to see the logic in this. Thus, why would it be bad idea for both operands to instead be promoted to type double or float?
long L = 100;
ulong UL = 1000;
double d = L + UL; // error saying + operator can't be applied
to operands of type ulong and long
b) Compiler implicitly converts int literal into byte type and assigns resulting value to b:
byte b = 1;
But if we try to assign a literal of type ulong to type long(or to types int, byte etc), then compiler reports an error:
long L = 1000UL;
I would think compiler would be able to figure out whether result of constant expression could fit into variable of type long?!
thank you
To answer the question marked (1) -- adding signed and unsigned longs is probably a mistake. If the intention of the developer is to overflow into inexact arithmetic in this scenario then that's something they should do explicitly, by casting both arguments to double. Doing so implicitly is hiding mistakes more often than it is doing the right thing.
To answer the question marked (b) -- of course the compiler could figure that out. Obviously it can because it does so for integer literals. But again, this is almost certainly an error. If your intention was to make that a signed long then why did you mark it as unsigned? This looks like a mistake. C# has been carefully designed so that it looks for weird patterns like this and calls your attention to them, rather than making a guess that you meant to say this weird thing and blazing on ahead as if everything were normal. The compiler is trying to encourage you to write sensible code; sensible code does not mix signed and unsigned types.
Why should it?
Generally, the 2 types are incompatible because long is signed. You are only describing a special case.
For byte b = 1; 1 has no implicit type as such and can be coerced into byte
For long L = 1000UL; "1000UL" does have an explicit type and is incompatible and see my general case above.
Example from "ulong" on MSDN:
When an integer literal has no suffix,
its type is the first of these types
in which its value can be represented:
int, uint, long, ulong.
and then
There is no implicit conversion from
ulong to any integral type
On "long" in MSDN (my bold)
When an integer literal has no suffix,
its type is the first of these types
in which its value can be represented:
int, uint, long, ulong.
It's quite common and logical and utterly predictable
long l = 100;
ulong ul = 1000;
double d = l + ul; // error
Why would it be bad idea for both operands to instead be promoted to type double or float?
Which one? Floats? Or doubles? Or maybe decimals? Or longs? There's no way for the compiler to know what you are thinking. Also type information generally flows out of expressions not into them, so it can't use the target of the assignment to choose either.
The fix is to simply specify which type you want by casting one or both of the arguments to that type.
The compiler doesn't consider what you do with the result when it determines the result type of an expression. The rules for how types are promoted in an expression only consider the values in the expression itself, not what you do with the value later on.
In the case where you assign the result to a variable, it could be possible to use that information, but consider a statement like this:
Console.Write(L + UL);
The Write method has overloads that take several different data types, which would make it rather complicated to decide how to use that information.
For example, there is an overload that takes a string, so one possible way to promote the types (and a good candidate as it doesn't lose any precision) would be to first convert both values to strings and then concatenate them, which is probably not the result that you were after.
Simple answer is that's just the way the language spec is written:
http://msdn.microsoft.com/en-us/library/y5b434w4(v=VS.80).aspx
You can argue over whether the rules of implicit conversions are logical in each case, but at the end of the day these are just the rules the design committee decided on.
Any implicit conversion has a downside in that it's doing something the programmer may not expect. The general principal with c# seems to be to error in these cases rather then try to guess what the programmer meant.
Suppose one variable was equal to 9223372036854775807 and the other was equal to -9223372036854775806? What should the result of the addition be? Converting the two values to double would round them to 9223372036854775808 and -9223372036854775808, respectively; performing the subtraction would then yield 0.0 (exactly). By contrast, if both values were signed, the result would be 1.0 (also exact). It would be possible to convert both operands to type Decimal and do the math exactly. Conversion to Double after the fact would require an explicit cast, however.
This question already has answers here:
byte + byte = int... why?
(16 answers)
Closed 5 years ago.
Why does the following raise a compile time error: 'Cannot implicitly convert type 'int' to 'byte':
byte a = 25;
byte b = 60;
byte c = a ^ b;
This would make sense if I were using an arithmentic operator because the result of a + b could be larger than can be stored in a single byte.
However applying this to the XOR operator is pointless. XOR here it a bitwise operation that can never overflow a byte.
using a cast around both operands works:
byte c = (byte)(a ^ b);
I can't give you the rationale, but I can tell why the compiler has that behavior from the stand point of the rules the compiler has to follow (which might not really be what you're interesting in knowing).
From an old copy of the C# spec (I should probably download a newer version), emphasis added:
14.2.6.2 Binary numeric promotions This clause is informative.
Binary numeric promotion occurs for
the operands of the predefined +, ?,
*, /, %, &, |, ^, ==, !=, >, <, >=, and <= binary operators. Binary
numeric promotion implicitly converts
both operands to a common type which,
in case of the non-relational
operators, also becomes the result
type of the operation. Binary numeric
promotion consists of applying the
following rules, in the order they
appear here:
If either operand is of type decimal, the other operand is
converted to type decimal, or a
compile-time error occurs if the other
operand is of type float or double.
Otherwise, if either operand is of type double, the other operand is
converted to type double.
Otherwise, if either operand is of type float, the other operand is
converted to type float.
Otherwise, if either operand is of type ulong, the other operand is
converted to type ulong, or a
compile-time error occurs if the other
operand is of type sbyte, short, int,
or long.
Otherwise, if either operand is of type long, the other operand is
converted to type long.
Otherwise, if either operand is of type uint and the other operand is of
type sbyte, short, or int, both
operands are converted to type long.
Otherwise, if either operand is of type uint, the other operand is
converted to type uint.
Otherwise, both operands are converted to type int.
So, basically operands smaller than an int will be converted to int for these operators (and the result will be an int for the non-relational ops).
I said that I couldn't give you a rationale; however, I will make a guess at one - I think that the designers of C# wanted to make sure that operations that might lose information if narrowed would need to have that narrowing operation made explicit by the programmer in the form of a cast. For example:
byte a = 200;
byte b = 100;
byte c = a + b; // value would be truncated
While this kind of truncation wouldn't happen when performing an xor operation between two byte operands, I think that the language designers probably didn't want to have a more complex set of rules where some operations would need explicit casts and other not.
Just a small note: the above quote is 'informational' not 'normative', but it covers all the cases in an easy to read form. Strictly speaking (in a normative sense), the reason the ^ operator behaves this way is because the closest overload for that operator when dealing with byte operands is (from 14.10.1 "Integer logical operators"):
int operator ^(int x, int y);
Therefore, as the informative text explains, the operands are promoted to int and an int result is produced.
FWIW
byte a = 25;
byte b = 60;
a = a ^ b;
does not work. However
byte a = 25;
byte b = 60;
a ^= b;
does work.
The demigod programmer from Microsoft has an answer: Link
And maybe it's more about compiler design. They make the compiler simpler by generalizing the compiling process, it doesn't have to look at operator of operands, so it lumped bitwise operations in the same category as arithmetic operators. Thereby, subjected to type widening
Link dead, archive here:
https://web.archive.org/web/20140118171646/http://blogs.msdn.com/b/oldnewthing/archive/2004/03/10/87247.aspx
I guess its because the operator XOR is defined for booleans and integers.
And a cast of the result from the integer result to a byte is an information-losing conversion ; hence needs an explicit cast (nod from the programmer).
It seems to be because in C# language specifications, it is defined for integer and long
http://msdn.microsoft.com/en-us/library/aa691307%28v=VS.71%29.aspx
So, what actually happens is that compiler casts byte operands to int implicitly because there is no loss of data that way. But the result (which is int) can not be down-cast-ed without loss of data (implicitly). So, you need to tell the compiler explicitly that you know what you are doing!
As to why the two bytes have to be converted to ints to do the XOR?
If you want to dig into it, 12.1.2 of the CLI Spec (Partition I) describes the fact that, on the evaluation stack, only int or long can exist. All shorter integral types have to be expanded during evaluation.
Unfortunately, I can't find a suitable link directly to the CLI Spec - I've got a local copy as PDF, but can't remember where I got it from.
This has more to do with the rules surrounding implicit and explicit casting in the CLI specification. An integer (int = System.Int32 = 4 bytes) is wider than a byte (1 byte, obviously!). Therefore any cast from int to byte is potentially a narrowing cast. Therefore, the compiler wants you to make this explicit.