bitwise operators

R

Rahul

Hi Everyone,

It is known that bitwise operators are independent of the
endianess of the machine, as each and every compiler is developed for
a particular target micro-processor. Hence it is the compiler's
responsibility to convert the bit-wise instructions into proper
assembly instructions.

But how is the programmer supposed to look at the logic? Say if i'm
working on a 4 byte datatype, how should i assume the value to be
stored in the four bytes? is it little or big?

Thanks in advance ! ! !
 
C

Chris Dollin

Rahul said:
Hi Everyone,

It is known that bitwise operators are independent of the
endianess of the machine, as each and every compiler is developed for
a particular target micro-processor.

(fx:nit) Not all targets are micro-processors.
Hence it is the compiler's
responsibility to convert the bit-wise instructions into proper
assembly instructions.

(fx:nit) /machine/ instructions. Compilers don't have to go via
assembly.
But how is the programmer supposed to look at the logic? Say if i'm
working on a 4 byte datatype, how should i assume the value to be
stored in the four bytes? is it little or big?

If you create the bits in C and work with them in C, and don't play
games with the position of eg chars in ints, and don't try and read in
values from a file that were written by a different implementation,
it shouldn't matter.

[If you're communicating with a non-C environment, it may matter,
but then you're anyway outside the scope of the Standard and you'll
have to use what that environment demands and pay the portability
penalty. Promptly, purposefully and politely.]
 
E

Eric Sosman

Rahul said:
Hi Everyone,

It is known that bitwise operators are independent of the
endianess of the machine, as each and every compiler is developed for
a particular target micro-processor. Hence it is the compiler's
responsibility to convert the bit-wise instructions into proper
assembly instructions.

... exactly as it is the compiler's responsibility to
generate the right instructions for the addition operator,
or for the division operator, or for the equality operator.
All these operators work with the values of their operands,
not with the representations of the values. (This may not
be entirely true of the generated code, but is true of the
C language operator.)

The confusion surrounding bitwise and shift operators
is caused, I think, by the fact that it is convenient to
describe them by referring to a bit-by-bit representation
of the operand and result values. It's easier to say that
`x |= 8' "sets the eight's bit" than to say that it "adds
eight to x if x%16 is less than eight." `x |= 9' would be
even harder to describe without appealing to the binary
representation. Even so, these are just notions that help
in explaining; the operators still operate on values and
not on representations.
But how is the programmer supposed to look at the logic? Say if i'm
working on a 4 byte datatype, how should i assume the value to be
stored in the four bytes? is it little or big?

This is a different matter. Look up Chris Torek's
posting of Dec 18, 2006; it's the most intuitive explanation
of "endianness" I've run across.
 
B

Ben Pfaff

Rahul said:
It is known that bitwise operators are independent of the
endianess of the machine, as each and every compiler is developed for
a particular target micro-processor. Hence it is the compiler's
responsibility to convert the bit-wise instructions into proper
assembly instructions.

But how is the programmer supposed to look at the logic? Say if i'm
working on a 4 byte datatype, how should i assume the value to be
stored in the four bytes? is it little or big?

You can (should, in my opinion) look at it in terms of values,
e.g. if the bit with value 1 is set either in x or in y, then it
will be set in x | y.
 
P

Philip Potter

Eric said:
The confusion surrounding bitwise and shift operators
is caused, I think, by the fact that it is convenient to
describe them by referring to a bit-by-bit representation
of the operand and result values. It's easier to say that
`x |= 8' "sets the eight's bit" than to say that it "adds
eight to x if x%16 is less than eight." `x |= 9' would be
even harder to describe without appealing to the binary
representation. Even so, these are just notions that help
in explaining; the operators still operate on values and
not on representations.

From n1256, 6.5.12:
"The result of the | operator is the bitwise inclusive OR of the
operands (that is, each bit in the result is set if and only if at least
one of the corresponding bits in the converted operands is set)."

Seems to be defined in terms of representation to me. (In fact, it seems
to also operate on padding bits, though I'm not sure). Did you mean the
This is a different matter. Look up Chris Torek's
posting of Dec 18, 2006; it's the most intuitive explanation
of "endianness" I've run across.

An excellent article indeed. The message id is
(e-mail address removed) for those looking.
 
B

Ben Bacarisse

Philip Potter said:
From n1256, 6.5.12:
"The result of the | operator is the bitwise inclusive OR of the
operands (that is, each bit in the result is set if and only if at
least one of the corresponding bits in the converted operands is
set)."

Seems to be defined in terms of representation to me.

That is over-stating the case since the bits themselves are, earlier,
defined in terms of the way in which they contribute to the value ("if
there are N value bits, each bit shall represent a different power of
2 between 1 and 2**(N −1)"). The term "corresponding" does not need
to be read as meaning "corresponding by position" -- the bits
correspond by contributing the same power of two to the value.

The representation does play a role in the result of bit operations on
signed types because the value produced by changing the sign bit does
depend on whether the system uses sign+magnitude, 1s complement or 2s
complement for negative numbers -- but that is only one bit out of
N+1.
(In fact, it
seems to also operate on padding bits, though I'm not sure).

It doesn't matter, since the padding bits don't contribute to the
value, and no valid operation can produce a combination of padding
bits that constitute a trap representation.
 
E

Eric Sosman

Philip said:
From n1256, 6.5.12:
"The result of the | operator is the bitwise inclusive OR of the
operands (that is, each bit in the result is set if and only if at least
one of the corresponding bits in the converted operands is set)."

Seems to be defined in terms of representation to me. (In fact, it seems
to also operate on padding bits, though I'm not sure). Did you mean the
shift operators (<< and >>), which are most certainly defined in terms
of value?

This may be a little like debating whether the angels on
the pinhead would prefer to dance the foxtrot or the frug, but
it still seems to me that operators[*] work on values. As I
said, the descriptions of some operators' actions is greatly
facilitated by referring to binary representations of values,
but it seems to me wrong-headed to read too much into that.
For one thing, a literal interpretation of something like
"shift left" gets into trouble if the electronic components
storing the bits are arranged vertically!

[*] "Ordinary" operators, that is. C regards some things
like -> and sizeof as "operators," but the operand of sizeof
is not a value and the r.h.s. of -> is not a value. But for
those operators that have value operands and/or value results,
it seems clearer to me to think about the values and not the
way they are represented.

Here's a final thought, and then I'll turn off the awful
disco music and let the angels strum their harps for a while.
What is the representation of the constant one in `x += 1', if
the generated code is something like `inc x'? In such a case
the constant does not appear at all, which makes it rather
difficult to make a case for any particular representation!
Even so, the += operator makes use of the value of its right-
hand operand, whether it has a representation or not.
 
C

CBFalconer

Rahul said:
It is known that bitwise operators are independent of the
endianess of the machine, as each and every compiler is developed
for a particular target micro-processor. Hence it is the
compiler's responsibility to convert the bit-wise instructions
into proper assembly instructions.

But how is the programmer supposed to look at the logic? Say if
i'm working on a 4 byte datatype, how should i assume the value
to be stored in the four bytes? is it little or big?

You don't. If you have a 4 byte datatype, say T, and items defined
as being of type T, you can shift them (as long as they are
positive, or unsigned) and store them back in objects of type T.
The shifts are defined in terms of multiplication and division by
2.

These things are independent because they operate on values.
 
P

Philip Potter

Ben said:
That is over-stating the case since the bits themselves are, earlier,
defined in terms of the way in which they contribute to the value ("if
there are N value bits, each bit shall represent a different power of
2 between 1 and 2**(N −1)"). The term "corresponding" does not need
to be read as meaning "corresponding by position" -- the bits
correspond by contributing the same power of two to the value.

Since the two operands are of the same type, "corresponding by position"
is equivalent to "corresponding by value" for all bits except padding
bits (which have no value).
The representation does play a role in the result of bit operations on
signed types because the value produced by changing the sign bit does
depend on whether the system uses sign+magnitude, 1s complement or 2s
complement for negative numbers -- but that is only one bit out of
N+1.

If you're going to allow sign+magnitude, 1s complement and 2s complement
as different kinds of representation, then don't you also accept
"binary" as an unsigned representation? The fact that it's the only one
allowed doesn't preclude the fact that it specifies how integers are
represented. Therefore the representation plays a key role in the result
of bit operations - they are defined in terms of bits, which are a
representational artefact.

I guess this is simply a difference of opinion in what "representation"
means.
It doesn't matter, since the padding bits don't contribute to the
value, and no valid operation can produce a combination of padding
bits that constitute a trap representation.

Define "valid operation". Looking at n1256, I guess you mean this
(6.2.6.1p8):

"Where an operator is applied to a value that has more than one object
representation, which object representation is used shall not affect the
value of the result.[43] Where a value is stored in an object using a
type that has more than one object representation for that value, it is
unspecified which representation is used, but a trap representation
shall not be generated."

I think this shows that padding bits are not necessarily involved in a
bitwise-OR operation and that it therefore it isn't defined in terms of
representation as defined in 6.2.6.1p4. Thanks for the correction. As
Eric Sosman says elsethread, this means the rest of the argument is
angels-on-pinheads territory.

n1256 certainly seems impossible to read unless you assume that
operators work on values (statements such as the fact that ~myuint is
equivalent to UINT_MAX - uint in the definition of the ~ operator
preclude the bitwise operators seeing padding bits) but I can't seem to
see this explicitly stated anywhere.
 
P

pete

Ben said:
You can (should, in my opinion) look at it in terms of values,
Mostly.

e.g. if the bit with value 1 is set either in x or in y, then it
will be set in x | y.

But one aspect of representation has to be considered
in order to evaluate (-1 & 3),
which is why you can use the value of that expression
to determine which system is being
used to represent negative int values
in a particular C implementation,
if you care for that sort of thing,
though I've never really needed it.
 
C

CBFalconer

pete said:
Ben Pfaff wrote:
.... snip ...

But one aspect of representation has to be considered in order
to evaluate (-1 & 3),

That is easily handled. One operand is negative, and can't be
handled at all. You are writing, at best, implementation defined
code.
 
K

Keith Thompson

CBFalconer said:
That is easily handled. One operand is negative, and can't be
handled at all. You are writing, at best, implementation defined
code.

What do you mean "can't be handled at all"? C99 6.5.12, which defines
the bitwise inclusive OR operator, places no restriction on the
signedness of the operands.

Yes, the code's behavior is at best implementation-defined.
 
P

Peter Nilsson

Keith Thompson said:
Yes, the code's behavior is at best implementation-
defined.

You say 'at best', but realise that implementation defined
doesn't preclude use in strictly conforming code, e.g. the
use of bit sets [cf FAQ 20.8.]
 
P

pete

CBFalconer said:
That is easily handled. One operand is negative, and can't be
handled at all.

This is a correct program for anyone else that wants to see
how (-1 & 3) can be used to determine whether
a system uses sign and magnitude or one's complement
or two's complement to represent negative integers:

/* BEGIN new.c */

#include <stdio.h>

int main(void)
{
switch (-1 & 3) {
case 1:
puts("sign and magnitude");
break;
case 2:
puts("one's complement");
break;
case 3:
puts("two's complement");
break;
default:
puts("Hmmm...");
break;
}
return 0;
}

/* END new.c */
 
P

pete

Peter said:
Keith Thompson said:
Yes, the code's behavior is at best implementation-
defined.

You say 'at best', but realise that implementation defined
doesn't preclude use in strictly conforming code, e.g. the
use of bit sets [cf FAQ 20.8.]

The value of (-1 & 3) tells whether the system uses two's complement
or one's complement or sign and magnitude to represent
negative integers.

An implementation defined value is appropriate
for that purpose.

And yes,
implementation defined code like that
can be to make a strictly conforming program.

/* BEGIN new.c */

#include <stdio.h>

#define NMEMB(A) (sizeof (A) / sizeof *(A))

int main(void)
{
int number[] = {1, 0, -1, -2, -3, -4};
unsigned count;

for (count = 0; count != NMEMB(number); ++count) {
if (number[count] >= 0) {
printf("%2d is %s\n", number[count],
number[count] & 1 ? "odd" : "even");
} else {
printf("%2d is %s\n", number[count],
number[count] & 1 ^ (-1 & 1) ? "even" : "odd");
}
}
return 0;
}

/* END new.c */
 

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

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top