Why is a cast required for byte subtraction in C#? [duplicate] - c#

This question already has answers here:
byte + byte = int... why?
(16 answers)
Closed 9 years ago.
I have to following code in VS2008 .net 3.5 using WinForms:
byte percent = 70;
byte zero = 0;
Bitmap copy = (Bitmap)image1.Clone();
...
Color oColor = copy.GetPixel(x, y);
byte oR = (byte)(oColor.R - percent < zero ? zero : oColor.R - percent);
When I leave the "(byte)" off the last line of code, I get a compiler error saying it "Cannot implicitly convert type 'int' to 'byte'." If everything is of type byte and byte is an integer type... then why do I need to have the cast?

Because subtraction is coercing up to an integer. As I recall, byte is an unsigned type in C#, so subtraction can take you out of the domain of bytes.

That's because the result of a byte subtraction doesn't fit in a byte:
byte - byte = (0..255) - (0..255) = -255..255

Arithmetic on bytes results in an int value by default.

Because arithmetic on bytes returns integers by default so of the two possible assignments the narrower type of zero (byte) is promoted to int (that of oColor.r - percent). Thus the type of the operation is an int. The compiler will not, without a cast, allow you to assign a wider type to a narrower type, because it's a lossy operation. Hence you get the error, unless you explicitly say "I know I'm losing some data, it's fine" with the cast.

This is because byte subtraction returns an int. Actually any binary arithmetic operations on bytes will return an int, so the cast is required.

Because arithmetic operations on sbyte, byte, ushort, and short are automatically converted to int. The most plausible reason for this is that such operations are likely to overflow or underflow.
Thus, in your ternary operation, the final oColor.R - percent, actually results in an int, not a byte. So the return type of the operation is int.

Because the arithmetic expression on the right-hand side of the assignment operator evaluates to int by default. In your example percent defaults to int. You can read more about it on the MSDN page.

Try to run this C# code:
object o = (byte)(1);
o = (int)o;
What do you expect? Now try :)
I think this is right:
Eric Lippert says, "I don't think of bytes as "numbers"; I think of them as patterns of bits that could be interpreted as numbers, or characters, or colors or whatever. If you're going to be doing math on them and treating them as numbers, then it makes sense to move the result into a data type that is more commonly interpreted as a number."
Perhaps byte is closer to char than to int.

FWIW, java also promotes bytes to int.

Related

Why is Convert.ToDouble(char) not supported?

From the msdn page :
public static double ToDouble(
char value
)
Parameters
value
Type: System.Char
The Unicode character to convert.
Return Value
Type: System.Double
This conversion is not supported. No value is returned.
If it is not supported, why is it implemented in the first place ?
It is not the only one. Convert.ToBoolean(char), ToDateTime, ToDecimal and ToSingle are also not supported, they all throw InvalidCastException like ToDouble does.
This is just .NET design trying to keep you out of trouble. Converting a char to an integral type is reasonable, you can look at the Unicode mapping tables and count the codepoints. But what would a conversion to Boolean mean? What Unicode code point is True? ToDateTime requires no explanation. How could a character ever be a fractional value? There are no half or quarter codepoints.
You can make it work, convert to Int32 first and then convert to Double. But by all means, check your code and make sure that it is a senseful thing to do. The .NET designers thought it wasn't. They were right.
As per MSDN, this function is keep reserved for future use in .Net 2.0 and it's kept till 4.5 for supporting previous version of .net frameworks.
They will implement this if future OS will support this type of conversion. Currently, OS stores the char as int, so not providing the way to cast char to double due to lots of inner conversions.
Due to internal storage format, same limitation is with Convert.ToDouble(DateTime).
Each character has a corresponding integer. For example:
Convert.ToInt16('a') -> returns 97.
I guess the main reason why the Convert doesn't support to convert from a char the other types is that the second nature of a character is the integer type.
Maybe a more clear example is the following code:
char a = 'a';
int aVal = (int)a;
Which actually Convert.ToInt32 does ( but also raises the overflow exception)
A char can be implicitly converted to ushort, int, uint, long, ulong, float, double, or decimal.

C# int byte conversion [duplicate]

This question already has answers here:
Integer summing blues, short += short problem
(5 answers)
Closed 6 years ago.
Why is
byte someVar;
someVar -= 3;
valid but
byte someVar;
someVar = someVar - 3;
isnt?
Surprisingly, when you perform operations on bytes the computations will be done using int values, with the bytes implicitly cast to (int) first. This is true for shorts as well, and similarly floats are up-converted to double when doing floating-point arithmetic.
The second snippet is equivalent to:
byte someVar;
someVar = (int) someVar - 3;
Because of this you must cast the result back to (byte) to get the compiler to accept the assignment.
someVar = (byte) (someVar - 3);
Here's a copy of a table in the CLI specification (Ecma 335) that specifies which operands are valid on the binary numeric operators of the type A op B, where A and B are the operands and "op" is the operator, like Opcodes.Sub that you are using in your snippet:
Some annotations are required with this:
"native int" is IntPtr in a C# program
F represents a floating point type, double or float in C#
& represents a pointer value, the boxes are shaded because they are unsafe operations
O represents an object reference
x is an operation that's not permitted.
Note the row and column for F, both operands must be floating point, you cannot directly add, say, an int to a double. The C# compiler deals with that limitation by automatically converting the int operand to double so that the operator is valid.
Relevant to your question: also note that the byte, sbyte, char, short and ushort types are not present. Same approach, the compiler converts the operands to the smallest type that can represent the value so that the operator can be used. Which will be int32. According to the table, the result of the operation will be int32.
Now here's the rub: the result is int32 but assigning that back to a byte value requires a narrowing conversion. From 32 bits to 8 bits. That's trouble because it loses significant bits. The C# compiler requires you to make that explicit. You essentially acknowledge that you know what you're doing and that you are aware of the potentially surprising result. Like this one:
byte v = 255;
v = (byte)(v + 1);
The -= operator is a problem because there is no effective way to apply that required cast. It isn't expressible in the language syntax. Using (byte)3 doesn't make sense, the literal gets converted to int32 anyway to make the operator work.
They punted the problem, the compiler automatically emits the cast without your help.

Instead of error, why don't both operands get promoted to float or double?

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.

C# XOR on two byte variables will not compile without a cast [duplicate]

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.

C# numeric constants

I have the following C# code:
byte rule = 0;
...
rule = rule | 0x80;
which produces the error:
Cannot implicitly convert type 'int' to 'byte'. An explicit conversion exists (are you missing a cast?)
[Update: first version of the question was wrong ... I misread the compiler output]
Adding the cast doesn't fix the problem:
rule = rule | (byte) 0x80;
I need to write it as:
rule |= 0x80;
Which just seems weird. Why is the |= operator any different to the | operator?
Is there any other way of telling the compiler to treat the constant as a byte?
# Giovanni Galbo : yes and no. The code is dealing with the programming of the flash memory in an external device, and logically represents a single byte of memory. I could cast it later, but this seemed more obvious. I guess my C heritage is showing through too much!
# Jonathon Holland : the 'as' syntax looks neater but unfortunately doesn't appear to work ... it produces:
The as operator must be used with a reference type or nullable type ('byte' is a non-nullable value type)
C# does not have a literal suffix for byte. u = uint, l = long, ul = ulong, f = float, m = decimal, but no byte. You have to cast it.
This works:
rule = (byte)(rule | 0x80);
Apparently the expression 'rule | 0x80' returns an int even if you define 0x80 as 'const byte 0x80'.
The term you are looking for is "Literal" and unfortunately C# does not have a byte literal.
Here's a list of all C# literals.
int rule = 0;
rule |= 0x80;
http://msdn.microsoft.com/en-us/library/kxszd0kx.aspx The | operator is defined for all value types. I think this will produced the intended result. The "|=" operator is an or then assign operator, which is simply shorthand for rule = rule | 0x80.
One of the niftier things about C# is that it lets you do crazy things like abuse value types simply based on their size. An 'int' is exactly the same as a byte, except the compiler will throw warnings if you try and use them as both at the same time. Simply sticking with one (in this case, int) works well. If you're concerned about 64bit readiness, you can specify int32, but all ints are int32s, even running in x64 mode.
According to the ECMA Specification, pg 72 there is no byte literal. Only integer literals for the types: int, uint, long, and ulong.
Almost five years on and nobody has actually answered the question.
A couple of answers claim that the problem is the lack of a byte literal, but this is irrelevant. If you calculate (byte1 | byte2) the result is of type int. Even if "b" were a literal suffix for byte the type of (23b | 32b) would still be int.
The accepted answer links to an MSDN article claiming that operator| is defined for all integral types, but this isn't true either.
operator| is not defined on byte so the compiler uses its usual overload resolution rules to pick the version that's defined on int. Hence, if you want to assign the result to a byte you need to cast it:
rule = (byte)(rule | 0x80);
The question remains, why does rule |= 0x80; work?
Because the C# specification has a special rule for compound assignment that allows you to omit the explicit conversion. In the compound assignment x op= y the rule is:
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.
Looks like you may just have to do it the ugly way: http://msdn.microsoft.com/en-us/library/5bdb6693.aspx.
Unfortunately, your only recourse is to do it just the way you have. There is no suffix to mark the literal as a byte. The | operator does not provide for implicit conversion as an assignment (i.e. initialization) would.
Apparently the expression 'rule |
0x80' returns an int even if you
define 0x80 as 'const byte 0x80'.
I think the rule is numbers like 0x80 defaults to int unless you include a literal suffix. So for the expression rule | 0x80, the result will be an int since 0x80 is an int and rule (which is a byte) can safely be converted to int.
According to the C standard, bytes ALWAYS promote to int in expressions, even constants. However, as long as both values are UNSIGNED, the high-order bits will be discarded so the operation should return the correct value.
Similarly, floats promote to double, etc.
Pull out of copy of K&R. It's all in there.

Categories