ridiculous

J

Jon Slaughter

I was using ints for all my code which involved a lot of bitwise logic.
Since in actuality I was just working with bytes I decided to change all the
ints to bytes. Now none of my code works... I have to explicitly cast any
time I use a bitwise operator just because C# thinks it has to be an int.

like if I do

byte x;

x >> 3 then it must be an int, right?

or x | 5 then it must be an int and I have to explicity cast back to a byte.

for example,

byte x = 5;

byte y = x | 3;



gives



Error 1 Cannot implicitly convert type 'int' to 'byte'. An explicit
conversion exists (are you missing a cast?) C:\Documents and Settings\Jon\My
Documents\Visual Studio 2005\Projects\ThreadTest5\ThreadTest5\Program.cs 31
22 ThreadTest5




This is really ridiculous!!!!



I know one can use unchecked but its a mess and I would have about 20% of my
code being the work unchecked(because I have a lot of short properties that
just extract bits from data.



Is there any way to turn off this "feature" completely? Non of my bit
juggling produces any ints so I don't need to implicitly or explicitly cast
from an int.



Jon
 
J

Jon Skeet [C# MVP]

Is there any way to turn off this "feature" completely? Non of my bit
juggling produces any ints so I don't need to implicitly or explicitly cast
from an int.

It's not a feature to be turned off - it's just a lack of overloads on
byte for addition etc. I appreciate it can be annoying though.

Jon
 
M

Matt Brunell

I believe that any time you are doing a bitwise operation ( And, Or, XOR,
Shift ) you are operating on 32 bits. There are no x86 instructions that
bitwise operate on less than 32 bits ( for 32 bit processors ).

So even if you cast to a byte, and then operate, the processor will still
operate on 32 bits. ( The upper 24 bits being 0 )

int b;
byte answer = (byte) ((byte)b << 4) ;

Here, b is being explicitly casted to a byte ( which masks off the upper 24
bits ), and then implicitly casted back to an int, the shift left
instruction executes, and then the result is being explicitly cast to a
byte.
This has the feeling of a language 'feature' to me. I would bet that this
is intentional.
 
J

Jon Slaughter

Matt Brunell said:
I believe that any time you are doing a bitwise operation ( And, Or, XOR,
Shift ) you are operating on 32 bits. There are no x86 instructions that
bitwise operate on less than 32 bits ( for 32 bit processors ).

So even if you cast to a byte, and then operate, the processor will still
operate on 32 bits. ( The upper 24 bits being 0 )

This is simply not true.

The x86 and all compatiable processors can easily do bitwise operations at
the byte level using its byte registers.

The line

and al, 3 works just fine in x86 assembly and if I did

byte c = 10;
byte q = c & 3;

then it would work jsut fine if translated into the equivalent assembly
code.

Now maybe the IL doesn't have this ability in some way but thats not my
fault....

I guess I'll go write it in C++ or something...
 
J

Jon Slaughter

Jon Skeet said:
It's not a feature to be turned off - it's just a lack of overloads on
byte for addition etc. I appreciate it can be annoying though.

Well, I guess I'll have to go write my code in managed and unmanaged C++...
what a mess though just for that ;/

Thanks,
Jon
 
J

Jon Skeet [C# MVP]

Jon Slaughter said:
Well, I guess I'll have to go write my code in managed and unmanaged C++...
what a mess though just for that ;/

You're willing to change programming language just to avoid casts? That
sounds a bit extreme to me.
 
J

Jon Slaughter

Jon Skeet said:
You're willing to change programming language just to avoid casts? That
sounds a bit extreme to me.

Passing all those ints around is waste of time and who knows what they do
when they cast. I suppose its actually not that big of a performance problem
but since I am going to have to write some unmanaged critical code I might
as well move all that into it too.
 
J

Jon Skeet [C# MVP]

Jon Slaughter said:
Passing all those ints around is waste of time and who knows what they do
when they cast.

Don't pass them, and just cast them. As for "who knows what they do" -
what exactly do you mean?
I suppose its actually not that big of a performance problem
but since I am going to have to write some unmanaged critical code I might
as well move all that into it too.

If you can isolate it sensibly, that sounds reasonable.
 
M

Matt Brunell

Jon Slaughter said:
This is simply not true.

The x86 and all compatiable processors can easily do bitwise operations at
the byte level using its byte registers.

The line

and al, 3 works just fine in x86 assembly and if I did

byte c = 10;
byte q = c & 3;

then it would work jsut fine if translated into the equivalent assembly
code.

I stand corrected. An assumption I had made earlier was wrong.

It makes sense that the above code wouldn't work in C# because a numeric
literal (without a suffix) in an expression is parsed as an int. The byte
variable initializer appears to either parse as a byte, or convert to byte.
For the variable initializer, the type can be inferred, but for the
expression it is assumed to be an int.

I tried using two byte variables like this:

byte b = 44;
byte c = 2;
byte ans = b & c;

This doesn't compile either.
From a language perspective, this should be possible. ( operator
overloading is supported elsewhere )
 
J

Jon Slaughter

Jon Skeet said:
Don't pass them, and just cast them. As for "who knows what they do" -
what exactly do you mean?

When you cast what actually goes on behind the scenes? Hell, for all I know
they could box them and then unbox them or convert them all to ints then to
bytes.

if it just used 32-bit registers then it wouldn't be a problem...

something like

mov eax, [some_byte]
and eax, 3
mov [some_byte], al

then it would be ok, but I have no idea what its trying to do.

I guess the problem is I'm wondering why it requires an explicit cast from a
byte to an int?
If you can isolate it sensibly, that sounds reasonable.

Well, its mainly for some hardware communications stuff and I did some tests
in unmanaged code and its 3x faster(basically the exact same code that
communicates with an unmanaged dll that hooks communicates with a kernel
mode driver).
 
J

Jon Slaughter

Matt Brunell said:
I stand corrected. An assumption I had made earlier was wrong.

It makes sense that the above code wouldn't work in C# because a numeric
literal (without a suffix) in an expression is parsed as an int. The byte
variable initializer appears to either parse as a byte, or convert to
byte.
For the variable initializer, the type can be inferred, but for the
expression it is assumed to be an int.

I tried using two byte variables like this:

byte b = 44;
byte c = 2;
byte ans = b & c;

This doesn't compile either.
From a language perspective, this should be possible. ( operator
overloading is supported elsewhere )


I'm just either going to move all the code into managed or unmanaged C++. I
just don't want to end up having to write a wrapper around the unmanaged
code but I have no choice I suppose. Might not be all that difficult though
compared to writing a kernel mode driver ;/

Thanks,
Jon
 
J

Jon Skeet [C# MVP]

Jon Slaughter said:
When you cast what actually goes on behind the scenes? Hell, for all I know
they could box them and then unbox them or convert them all to ints then to
bytes.

No, there's no boxing going on.
if it just used 32-bit registers then it wouldn't be a problem...

something like

mov eax, [some_byte]
and eax, 3
mov [some_byte], al

then it would be ok, but I have no idea what its trying to do.

Well, we don't know what the JITted code will do - but you can see what
the IL will do.
I guess the problem is I'm wondering why it requires an explicit cast from a
byte to an int?

It doesn't. It requries an explicit cast from an int to a byte. The
result of an operation involving two bytes is an int (in fact, the
individual bytes are promoted and then the operation is done on the
ints) and so you need to cast back down to byte.
Well, its mainly for some hardware communications stuff and I did some tests
in unmanaged code and its 3x faster(basically the exact same code that
communicates with an unmanaged dll that hooks communicates with a kernel
mode driver).

In some cases 3x faster is worth it - in others it isn't. (That sounds
flippant, but if performance is already okay, it's often not worth
going unmanaged for the boost.) I assume in this case that it's worth
it though.
 
J

Jon Slaughter

Jon Skeet said:
No, there's no boxing going on.

Yeah, I know.. I'm just saying that I don't know what there doing. I suppose
its not all that critical now but I guess I'm just kinda peaved that I spend
5 mins changing all those ints to bytes just to find it wouldn't work ;/
lol...
if it just used 32-bit registers then it wouldn't be a problem...

something like

mov eax, [some_byte]
and eax, 3
mov [some_byte], al

then it would be ok, but I have no idea what its trying to do.

Well, we don't know what the JITted code will do - but you can see what
the IL will do.

Yeah, I might take a look at that later if I need to... its not big deal
now.
It doesn't. It requries an explicit cast from an int to a byte. The
result of an operation involving two bytes is an int (in fact, the
individual bytes are promoted and then the operation is done on the
ints) and so you need to cast back down to byte.

(I meant int to byte)

But the result of the operations I'm using do not result in an int. Flitting
or changing bits in a byte don't result in an int. Right shifting also do
not result in an int... so there is absolutely no need to cast to an int in
anything I'm doing.

My code basically involves combining two bytes through a mask.

public void SyncSend(ParallelPortPins DataLines, int[] DataBits)
{
int m = (((int)DataLines) | (int)ClockPin) ^ ((int)InvertedPins);
int cp = ((int)ClockPin) ^ ((int)InvertedPins);
int clock = cp;


for (int k = 0; k < DataBits.Length; k++)
{
// Toggle ClockPin
clock = clock ^ cp;

DataBits[k] = (DataBits[k] & ~cp) ^ clock;

// Send data before clock
if ((((int)ClockPin) >> 16) > 0)
{
Data = (Data & ~m) | (DataBits[k] & m);
Control = ((Control & ((m >> 16) & 0xFF)) | ((DataBits[k] & m)} else
{
Control = ((Control & ((m >> 16) & 0xFF)) | ((DataBits[k] & m)Data = (Data & ~m) | (DataBits[k] & m);
}

Thread.SpinWait(DataRate);
}
}


Every bitwise operation involved never produces anything more than a byte.
(thats only one function, I have about 20 properties like

public int nSelectPrinter { get { control = Control; return (((control & 8)Control & (8 ^ 255); } }

and this is where I have problems when I use byte instead of int. But again,
its a waste to use an int yet if I use byte I get errors any time I use
bitwise operations and shifts.

But its just going to be easier for me to implement all this stuff in
unmanaged code because its much faster. But I still want to use C# for the
gui so I'll have to wrap it in managed C++ and that might offset the
benefit.

In some cases 3x faster is worth it - in others it isn't. (That sounds
flippant, but if performance is already okay, it's often not worth
going unmanaged for the boost.) I assume in this case that it's worth
it though.


Well. I think in this case it will. I might also be able to improve it more
by working with a larger data path(actually could improve it by another
factor of 3). The parallel port itself cannot really write all that fast but
the faster I can get it less I have to worry about timing issues. (thats not
completely true but it just gives me more leeway)

It well might not be worth it though but I guess its a good experience to
play around with unmanaged and managed code.

Thanks,
Jon
 
W

Willy Denoyette [MVP]

Jon Skeet said:
Jon Slaughter said:
When you cast what actually goes on behind the scenes? Hell, for all I
know
they could box them and then unbox them or convert them all to ints then
to
bytes.

No, there's no boxing going on.
if it just used 32-bit registers then it wouldn't be a problem...

something like

mov eax, [some_byte]
and eax, 3
mov [some_byte], al

then it would be ok, but I have no idea what its trying to do.

Well, we don't know what the JITted code will do - but you can see what
the IL will do.
I guess the problem is I'm wondering why it requires an explicit cast
from a
byte to an int?

It doesn't. It requries an explicit cast from an int to a byte. The
result of an operation involving two bytes is an int (in fact, the
individual bytes are promoted and then the operation is done on the
ints) and so you need to cast back down to byte.

Note that in C++, you are also operating on to int promoted 'bytes'
(unsigned char) or shorts, the only difference is that in C++ you don't need
to be explicit, the compiler performs an implicit cast.

unsigned char b1 = 255;
unsigned char b2 = 5;
int i = b1 + b2; // i = 300 no casts needed
unsigned char b3 = b1 + b2; // b3 = 4, implicit cast - overflow is dropped

See no casts needed, however , consider following small C++ code sample:

unsigned char x, y;
....
y = 5;
....
x = ~y;
this returns x = 251, but was it 251 that you expected here, or did you
expect -5 ?
if it's -5 you will have to declare x as being of type int, like this...

unsigned char y;
int x;
....
y = 5;
....
x = ~y;
this returns -5, but what if here you expected ~ to be operating on a byte
(well... an unsigned char) and return 251?
Well, in this case you need a cast like:
x = (unsigned char)~y;

The explicit casts in C# aren't that bad, they force you to think (a bit)
about what you are expecting, so they might help you in writing correct
code. Of course, the compiler could treat the operation on bytes that can't
overflow as not requiring a cast, but this leads to inconsistency.

Willy.
 
L

Luc E. Mistiaen

doesn't ~5 give -6 (the difference between 1-complement vs 2-complement?)

/LM


Willy Denoyette said:
Jon Skeet said:
Jon Slaughter said:
Don't pass them, and just cast them. As for "who knows what they do" -
what exactly do you mean?

When you cast what actually goes on behind the scenes? Hell, for all I
know
they could box them and then unbox them or convert them all to ints then
to
bytes.

No, there's no boxing going on.
if it just used 32-bit registers then it wouldn't be a problem...

something like

mov eax, [some_byte]
and eax, 3
mov [some_byte], al

then it would be ok, but I have no idea what its trying to do.

Well, we don't know what the JITted code will do - but you can see what
the IL will do.
I guess the problem is I'm wondering why it requires an explicit cast
from a
byte to an int?

It doesn't. It requries an explicit cast from an int to a byte. The
result of an operation involving two bytes is an int (in fact, the
individual bytes are promoted and then the operation is done on the
ints) and so you need to cast back down to byte.

Note that in C++, you are also operating on to int promoted 'bytes'
(unsigned char) or shorts, the only difference is that in C++ you don't
need to be explicit, the compiler performs an implicit cast.

unsigned char b1 = 255;
unsigned char b2 = 5;
int i = b1 + b2; // i = 300 no casts needed
unsigned char b3 = b1 + b2; // b3 = 4, implicit cast - overflow is
dropped

See no casts needed, however , consider following small C++ code sample:

unsigned char x, y;
...
y = 5;
...
x = ~y;
this returns x = 251, but was it 251 that you expected here, or did you
expect -5 ?
if it's -5 you will have to declare x as being of type int, like this...

unsigned char y;
int x;
...
y = 5;
...
x = ~y;
this returns -5, but what if here you expected ~ to be operating on a byte
(well... an unsigned char) and return 251?
Well, in this case you need a cast like:
x = (unsigned char)~y;

The explicit casts in C# aren't that bad, they force you to think (a bit)
about what you are expecting, so they might help you in writing correct
code. Of course, the compiler could treat the operation on bytes that
can't overflow as not requiring a cast, but this leads to inconsistency.

Willy.
 
W

Willy Denoyette [MVP]

Luc E. Mistiaen said:
doesn't ~5 give -6 (the difference between 1-complement vs 2-complement?)

/LM

My goodness, sure 250 and -6.

Thanks for correcting,
Willy.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top