bitwise NOT on const byte fields in C# - c#

I realized that if I have a field or variable of type 'byte', I can apply bitwise NOT(~) on it and cast it to byte. However, if the field is 'const byte', I can still apply bitwise NOT(~), but I cannot cast it to byte. For example,
This compiles:
class Program
{
byte b = 7;
void Method()
{
byte bb = (byte) ~b;
}
}
But this has a compile error ("Constant value '-8' cannot be converted to a 'byte' "):
class Program
{
const byte b = 7;
void Method()
{
byte bb = (byte) ~b;
}
}
I wonder why?

Because the ~ operator is only predefined for int, uint, long, and ulong. Your first sample implicitly casts b to an int, performs the negation, then explicitly casts back to a byte.
In the second example, b is a constant, so the compiler is also inlining the negation, effectively making a constant int with a value of -8 (the signed twos-complement of 7). And since a constant negative value can't be cast to a byte (without adding an unchecked context), you get a compilation error.
To avoid the error just store the result in a non-constant int variable:
const byte b = 7;
void Main()
{
int i = ~b;
byte bb = (byte)i;
}

There is no ~ operator defined for byte. It's defined for int. The byte is implicitly converted to an int, and that int is NOT-ed. That resulting int is not in the range of a byte (0 - 255, inclusive), so it can only be converted to a byte at compile-time via an unchecked cast:
byte bb = unchecked((byte)~b);
The second program doesn't compile because, due to the use of compile time constants, it's able to validate the improper conversion at compile time. The compiler cannot make this assertion with non-compile time constant values.

I can't explain the difference, but the simple solution for your second program is to mark it unchecked like so:
byte bb = unchecked((byte)~b);

Related

Casting a hex number under unchecked scope [duplicate]

I want to use HEX number to assign a value to an int:
int i = 0xFFFFFFFF; // effectively, set i to -1
Understandably, compiler complains.
Question, how do I make above work?
Here is why I need this.
WritableBitmap class exposes pixels array as int[]. So if I want to set pixel to Blue, I would say: 0xFF0000FF (ARGB) (-16776961)
Plus I am curious if there is an elegant, compile time solution.
I know there is a:
int i = BitConverter.ToInt32(new byte[] { 0xFF, 0x00, 0x00, 0xFF }, 0);
but it is neither elegant, nor compile time.
Give someone a fish and you feed them for a day. Teach them to pay attention to compiler error messages and they don't have to ask questions on the internet that are answered by the error message.
int i = 0xFFFFFFFF;
produces:
Cannot implicitly convert type 'uint' to 'int'. An explicit conversion exists
(are you missing a cast?)
Pay attention to the error message and try adding a cast:
int i = (int)0xFFFFFFFF;
Now the error is:
Constant value '4294967295' cannot be converted to a 'int'
(use 'unchecked' syntax to override)
Again, pay attention to the error message. Use the unchecked syntax.
int i = unchecked((int)0xFFFFFFFF);
Or
unchecked
{
int i = (int)0xFFFFFFFF;
}
And now, no error.
As an alternative to using the unchecked syntax, you could specify /checked- on the compiler switches, if you like to live dangerously.
Bonus question:
What makes the literal a uint in the first place?
The type of an integer literal does not depend on whether it is hex or decimal. Rather:
If a decimal literal has the U or L suffixes then it is uint, long or ulong, depending on what combination of suffixes you choose.
If it does not have a suffix then we take the value of the literal and see if it fits into the range of an int, uint, long or ulong. Whichever one matches first on that list is the type of the expression.
In this case the hex literal has a value that is outside the range of int but inside the range of uint, so it is treated as a uint.
You just need an unchecked cast:
unchecked
{
int i = (int)0xFFFFFFFF;
Console.WriteLine("here it is: {0}", i);
}
The unchecked syntax seems a bit gar'ish (!) when compared to the various single-letter numerical suffixes available.
So I tried for a shellfish:
static public class IntExtMethods
{
public static int ui(this uint a)
{
return unchecked((int)a);
}
}
Then
int i = 0xFFFFFFFF.ui();
Because the lake has more fish.
Note: it's not a constant expression, so it can't be used to initialize an enum field for instance.

Why 'BitConverter.GetBytes()' accept argument of type 'byte' and returns always a 2-bytes array?

I'm using BitConverter.GetBytes() to convert various variables (of different types) to a byte array, to pass it to a custom method where I need to check the value of each byte.
I've noticed that I can pass a variable of type byte to BitConverter.GetBytes() (even if it is not listed in the Overload list: see related MSDN page) and in this case I always have a 2-bytes array as return value.
Shouldn't I have a single-byte array as return value? How does .NET interpret the byte argument?
Sample:
byte arg = 0x00;
byte[] byteArr = BitConverter.GetBytes(arg);
// Result: byteArr is a 2-bytes array where byte[0] = 0 and byte[ 1] = 0
When you look up GetBytes() you will find that there is no overload that takes a byte parameter.
You are looking at the results of the closest match, GetBytes(Int16) and that of course produces a byte[2].
In other words, your code:
byte arg = 0x00;
byte[] byteArr = BitConverter.GetBytes(arg);
is equivalent to:
byte arg = 0x00;
short _temp = arg;
byte[] byteArr = BitConverter.GetBytes(_temp);
As the other answers have pointed out, there is no GetBytes overload that takes a byte parameter. The next question is why does it choose the overload that takes a short. It could pick any of these for example:
GetBytes(short)
GetBytes(int)
GetBytes(long)
GetBytes(float)
...
The reasoning for why it chooses short is not just because short is the next closest thing. There is better reasoning behind it. The C# language specification explains:
"Given an implicit conversion C1 that converts from a type S to a type T1, and an implicit conversion C2 that converts from a type S to a type T2, the better conversion of the two conversions is determined as follows" [1]
Here are two possible conversions from S to either T1 or T2:
S
C1 byte short (T1)
C2 byte int (T2)
The rule that works here is:
"If an implicit conversion from T1 to T2 exists, and no implicit conversion from T2 to T1 exists, C1 is the better conversion."
There's is an implicit conversion from short to int, but not from int to short, so the conversion from byte to short is chosen.
[1] http://msdn.microsoft.com/en-us/library/aa691339(v=vs.71).aspx (old copy)
It's actually using the overload for short instead of byte, which means it's up-casting your byte to a short, which is 2 bytes long.
There is no overload for GetBytes which accepts a byte.
However, Section 6.1.2 of the C# language spec says that there is an implicit numeric conversion
• From byte to short, ushort, int, uint, long, ulong, float, double, or decimal.
This causes the compiler to convert the byte to short (which is 2 bytes), which causes the method to return a 2 byte array.

Operator ">" cannot be applied to type 'ulong' and 'int'

I'm curious to know why the C# compiler only gives me an error message for the second if statement.
enum Permissions : ulong
{
ViewListItems = 1L,
}
public void Method()
{
int mask = 138612833;
int compare = 32;
if (mask > 0 & (ulong)Permissions.ViewListItems > 32)
{
//Works
}
if (mask > 0 & (ulong)Permissions.ViewListItems > compare)
{
//Operator '>' cannot be applied to operands of type 'ulong' and 'int'
}
}
I've been experimenting with this, using ILSpy to examine the output, and this is what I've discovered.
Obviously in your second case this is an error - you can't compare a ulong and an int because there isn't a type you can coerce both to. A ulong might be too big for a long, and an int might be negative.
In your first case, however, the compiler is being clever. It realises that const 1 > const 32 is never true, and doesn't include your if statement in the compiled output at all. (It should give a warning for unreachable code.) It's the same if you define and use a const int rather than a literal, or even if you cast the literal explicitly (i.e. (int)32).
But then isn't the compiler successfully comparing a ulong with an int, which we just said was impossible?
Apparently not. So what is going on?
Try instead to do something along the following lines. (Taking input and writing output so the compiler doesn't compile anything away.)
const int thirtytwo = 32;
static void Main(string[] args)
{
ulong x = ulong.Parse(Console.ReadLine());
bool gt = x > thirtytwo;
Console.WriteLine(gt);
}
This will compile, even though the ulong is a variable, and even though the result isn't known at compile time. Take a look at the output in ILSpy:
private static void Main(string[] args)
{
ulong x = ulong.Parse(Console.ReadLine());
bool gt = x > 32uL; /* Oh look, a ulong. */
Console.WriteLine(gt);
}
So, the compiler is in fact treating your const int as a ulong. If you make thirtytwo = -1, the code fails to compile, even though we then know that gt will always be true. The compiler itself can't compare a ulong to an int.
Also note that if you make x a long instead of a ulong, the compiler generates 32L rather than 32 as an integer, even though it doesn't have to. (You can compare an int and a long at runtime.)
This points to the compiler not treating 32 as a ulong in the first case because it has to, merely because it can match the type of x. It's saving the runtime from having to coerce the constant, and this is just a bonus when the coercion should by rights not be possible.
It's not the CLR giving this error message it's the compiler.
In your first example the compiler treats 32 as ulong (or a type that's implicitly convertible to ulong eg uint) whereas in your second example you've explicitly declared the type as an int. There is no overload of the > operator that accepts an ulong and an int and hence you get a compiler error.
rich.okelly and rawling's answers are correct as to why you cannot compare them directly. You can use the Convert class's ToUInt64 method to promote the int.
if (mask > 0 & (ulong)Permissions.ViewListItems > Convert.ToUInt64(compare))
{
}

What's wrong with this expression? Cannot implicitly convert type 'int' to 'byte'

I am getting the error "Cannot implicitly convert type 'int' to 'byte'. An explicit conversion exists (are you missing a cast?)". Doesn't byte + byte = byte? Also I notice when I remove the +rgb.Green it works
// rgb.Red, rgb.Green, rgb.Blue are byte types
// h, delta are double
rgb.Red = Convert.ToByte(Math.Round((h - 4) * delta)) + rgb.Green;
public struct RGBColor
{
public byte Red { get; set; }
public byte Green { get; set; }
public byte Blue { get; set; }
}
Adding two bytes produces an integer in C#. Convert the entire thing back to a byte.
rgb.Red = (byte)(Convert.ToByte(Math.Round((h - 4) * delta)) + rgb.Green);
See byte + byte = int... why? for more information.
Doesn't byte + byte = byte?
Nope, because it may overflow (> 255), that's why this operation returns an Int32. You could cast the result back to byte at your own risk.
http://msdn.microsoft.com/en-us/library/5bdb6693(VS.71).aspx
byte + byte = int
More accurately framework doesn't define operator + on byte, but there is an implicit conversion from byte to int, to
byte + byte = int + int = int
I don't quite agree with the justification for this being that it may overflow, since so may int + int. But obviously byte arithmetic is far more 'dangerous' in this respect - and this behaviour forces you to take a close look at what you are doing.
C# widens all operands to int before doing arithmetic on them. So you'll need to cast it back to byte explicitly.
http://msdn.microsoft.com/en-us/library/5bdb6693(VS.80).aspx
For built-in numeric types, an implicit conversion can be made when the value to be stored can fit into the variable without being truncated or rounded off. For example, a variable of type long (8 byte integer) can store any value that an int (4 bytes on a 32-bit computer) can store.
Refer Implicit Conversion section in this https://msdn.microsoft.com/en-us/library/ms173105.aspx
Now coming to your example obviously byte + byte need not necessarily be a byte. So byte+byte may be int. In that case "Implicit Conversion" will not fit because Upward casting is possible not vice versa, that is int can be converted to long, byte can be converted to int.
So in your case you need explicit conversion. Compiler needs you to perform this.
However, if a conversion cannot be made without a risk of losing information, the compiler requires that you perform an explicit conversion, which is called a cast. A cast is a way of explicitly informing the compiler that you intend to make the conversion and that you are aware that data loss might occur.
Refer explicit conversion in the same page.
so for your example
rgb.Red = (byte)(Convert.ToByte(Math.Round((h - 4) * delta)) + rgb.Green);
This will convert int to byte explicitly.

If compiler can perform implicit narrowing conversion on an integer literal, then it should also

If compiler is able to implicitly convert integer literal into byte type and assign the result to b ( b = 100; ), why can’t it also implicitly assign the result of an expression a+100 ( result is of type integer ) to b?
byte a = 10;
byte b = a; //ok
b = 100; //ok
b = a + 100;//error - explicit cast needed
b = (byte)(a + 100); // ok
thanx
It's all about static type safety - whether, at compile time, we can safety know the type of an expression. With a literal, the compiler can correctly tell that if it can be converted to a byte. In byte a = 20, 20 is convertible, so it all goes through fine. byte a = 257 won't work (257 can't be converted).
In the case byte b = a, then we already know a is a byte, so type safety is assured. b = 100 is again fine (it's statically known that 100 is convertible).
In b = a + 100, it is not statically known if a + 100 is a byte. a could contain 200, so a + 100 is not representable as a byte. Hence the compiler forces you to tell it "Yes, a + 100 is always a byte" via a cast, by appealing to your higher level programmer knowledge.
Some types of more advanced type systems don't suffer from this problem, but come with their own problems that most programmers won't like.
The compiler allows you to implicitly convert an integer literal into a byte, since it can, at compile time, check the value of the literal to make sure that it's a byte, and treat it as a byte literal.
You can see this if you try the following:
byte a = 10; // Works, since 10 is valid as byte
byte b = 239832; // Gives error!
The error you get if you put an arbitrary int is:
Error 1 Constant value '239832' cannot be converted to a 'byte'
When you're adding a literal to a byte:
b = a + 100
There's the potential for overflowing, so it's not implicitly allowed. You need to tell the compiler that you explicitly want this to happen, via a cast.
If you use the assigning version of the operator (+=) then it will perform the narrowing conversion on the result without reporting an error:
byte a = 10;
byte b = a; //ok
b = 100; //ok
b = a;
b += 100;//ok
Because treating literals specially is easy and useful; having the compiler distinguish all expressions consisting of compile-time constants and treat them specially would be far more work and far less useful.

Categories

Resources