Why write putc(s.i16 & 0xff, fp);

J

Jorge Peixoto

In the answer to question 12.42 of the C FAQ, we have this code:

putc((unsigned)((s.i32 >> 24) & 0xff), fp);
putc((unsigned)((s.i32 >> 16) & 0xff), fp);
putc((unsigned)((s.i32 >> 8) & 0xff), fp);
putc((unsigned)(s.i32 & 0xff), fp);


putc((s.i16 >> 8) & 0xff, fp);
putc(s.i16 & 0xff, fp);

Why the & 0xff ? The putc function casts its argument to an unsigned
char, so anything but the 8 lower bits is automatically discarded. And
the code already assumes the a char is 8 bits.
 
C

CBFalconer

Jorge said:
In the answer to question 12.42 of the C FAQ, we have this code:

putc((unsigned)((s.i32 >> 24) & 0xff), fp);
putc((unsigned)((s.i32 >> 16) & 0xff), fp);
putc((unsigned)((s.i32 >> 8) & 0xff), fp);
putc((unsigned)(s.i32 & 0xff), fp);

putc((s.i16 >> 8) & 0xff, fp);
putc(s.i16 & 0xff, fp);

Why the & 0xff ? The putc function casts its argument to an unsigned
char, so anything but the 8 lower bits is automatically discarded. And
the code already assumes the a char is 8 bits.

Where did you get the magic number 8 there? Bytes and chars can be
any size greater or equal to 8, given by CHAR_BIT in limits.h.
 
J

Jorge Peixoto

Where did you get the magic number 8 there? Bytes and chars can be
any size greater or equal to 8, given by CHAR_BIT in limits.h

Yes, but this code (which is from the C FAQ) already assumes that char
is 8 bits. Read the answer to the question 12.42 of the FAQ.
 
P

pete

Jorge said:
In the answer to question 12.42 of the C FAQ, we have this code:

putc((unsigned)((s.i32 >> 24) & 0xff), fp);
putc((unsigned)((s.i32 >> 16) & 0xff), fp);
putc((unsigned)((s.i32 >> 8) & 0xff), fp);
putc((unsigned)(s.i32 & 0xff), fp);

putc((s.i16 >> 8) & 0xff, fp);
putc(s.i16 & 0xff, fp);

Why the & 0xff ? The putc function casts its argument to an unsigned
char, so anything but the 8 lower bits is automatically discarded. And
the code already assumes the a char is 8 bits.

If the representation of negative integers isn't two's complement,
then converting to unsigned char
is different from discarding bits when s.i16 is negative.
 
J

Jorge Peixoto

If the representation of negative integers isn't two's complement,
then converting to unsigned char
is different from discarding bits when s.i16 is negative.

It doesn't matter.

The standard guarantees (according to http://c-faq.com/decl/inttypes.html)
that an unsigned char can hold any integer from 0 to 255. Since we are
already assuming that char is 8 bits, it can hold at most 256 numbers;
there are already 256 numbers between 0 and 255, so 0-255 is exactly
the range of numbers that an unsigned char can hold.

As far as I know (if I am wrong here please correct me), when you
convert from an integer to an unsigned, shorter one, the result is the
nonnegative remander in the division by a number that is 1 bigger
than the maximum number that can be represented by the smaller type.
In our case, this number is 256.

Taking the remainder modulo 256 is equivalent to bitwise and with
0xff, isn't it?
 
P

pete

Jorge said:
It doesn't matter.

The standard guarantees (according to http://c-faq.com/decl/inttypes.html)
that an unsigned char can hold any integer from 0 to 255. Since we are
already assuming that char is 8 bits, it can hold at most 256 numbers;
there are already 256 numbers between 0 and 255, so 0-255 is exactly
the range of numbers that an unsigned char can hold.

As far as I know (if I am wrong here please correct me), when you
convert from an integer to an unsigned, shorter one, the result is the
nonnegative remander in the division by a number that is 1 bigger
than the maximum number that can be represented by the smaller type.
In our case, this number is 256.
Taking the remainder modulo 256 is equivalent to bitwise and with
0xff, isn't it?

No.

There are three allowable ways to represent (-1) in 16 bits:
1111 1111 1111 1111
1111 1111 1111 1110
1000 0000 0000 0001

If s.i16 has a value of (-1),
then (s.i16 & 0xff) can have a value of either
255, or 254, or 1.

((unsigned char)-1) is always equal to UCHAR_MAX.
 
C

CBFalconer

Jorge said:
It doesn't matter.

The standard guarantees (according to http://c-faq.com/decl/inttypes.html)
that an unsigned char can hold any integer from 0 to 255. Since we
are already assuming that char is 8 bits, it can hold at most 256
numbers; there are already 256 numbers between 0 and 255, so 0-255
is exactly the range of numbers that an unsigned char can hold.

IMO the fact that the FAQ makes (and documents) assumptions about
CHAR_BIT is no excuse for doing the same in this newsgroup,
especially without clearly so documenting. In other words, it does
matter, and omitting it will lead all sorts of innocent newbies
into dingy dark despondent passages. Look with especial suspicion
on any constant '8' in your code.
 
J

Jorge Peixoto

If s.i16 has a value of (-1),
then (s.i16 & 0xff) can have a value of either
255, or 254, or 1.

((unsigned char)-1) is always equal to UCHAR_MAX.
I didn't know that. So the bitwise operators are machine-dependent?
Are you telling me that the following simple code

#include <stdio.h>

int main (void) {
unsigned t = 256;
t &= 0xff;
printf ("%u\n", t);
return 0;
}

is machine dependent*? I thought that the bitwise operators would
behave as if the number is in two's complement, in any machine. It is
amazing if the simple code above is machine dependent.

* Even positive numbers may be represented in a crazy way. On the
Deathstation, positive numbers are represented in base 1, that is, the
number is the number of 1s in the variable. So 0 is all bits 0, 1 is
1, two is 11, three is 111, etc.
 
J

James Kuyper

Jorge Peixoto wrote:
....
I didn't know that. So the bitwise operators are machine-dependent?

There are three permitted representations for negative values of signed
integer types; which one is actually used is implementation-defined.

The binary | and & operators are defined by the standard only in terms
of their operations on value and sign bits; the consequences of that
definition when applied to signed types are therefore tied to which of
those three representations is used for negative values.
Are you telling me that the following simple code

#include <stdio.h>

int main (void) {
unsigned t = 256;
t &= 0xff;
printf ("%u\n", t);
return 0;
}

is machine dependent*?

No. You'd have to use a signed type with a negative value in order to
get behavior that is implementation-defined.
 
C

CBFalconer

Jorge said:
.... snip ...

* Even positive numbers may be represented in a crazy way. On the
Deathstation, positive numbers are represented in base 1, that is,
the number is the number of 1s in the variable. So 0 is all bits
0, 1 is 1, two is 11, three is 111, etc.

Not so. The fundamental binary representation of positive integers
is specified in the standard.
 
W

Willem

Jorge wrote:
) In the answer to question 12.42 of the C FAQ, we have this code:
)
) putc((unsigned)((s.i32 >> 24) & 0xff), fp);
) putc((unsigned)((s.i32 >> 16) & 0xff), fp);
) putc((unsigned)((s.i32 >> 8) & 0xff), fp);
) putc((unsigned)(s.i32 & 0xff), fp);
)
)
) putc((s.i16 >> 8) & 0xff, fp);
) putc(s.i16 & 0xff, fp);
)
) Why the & 0xff ? The putc function casts its argument to an unsigned
) char, so anything but the 8 lower bits is automatically discarded. And
) the code already assumes the a char is 8 bits.

Think about what would happen with this bit of code on a system where
a char is 9 bits. What would the result be ? What exactly would putc
be outputting ?


SaSW, Willem
--
Disclaimer: I am in no way responsible for any of the statements
made in the above text. For all I know I might be
drugged or something..
No I'm not paranoid. You all think I'm paranoid, don't you !
#EOT
 
J

James Kuyper

Willem said:
Jorge wrote:
) In the answer to question 12.42 of the C FAQ, we have this code:
)
) putc((unsigned)((s.i32 >> 24) & 0xff), fp);
) putc((unsigned)((s.i32 >> 16) & 0xff), fp);
) putc((unsigned)((s.i32 >> 8) & 0xff), fp);
) putc((unsigned)(s.i32 & 0xff), fp);
)
)
) putc((s.i16 >> 8) & 0xff, fp);
) putc(s.i16 & 0xff, fp);
)
) Why the & 0xff ? The putc function casts its argument to an unsigned
) char, so anything but the 8 lower bits is automatically discarded. And
) the code already assumes the a char is 8 bits.

Think about what would happen with this bit of code on a system where
a char is 9 bits. What would the result be ? What exactly would putc
be outputting ?

That char should be assumed to be 8 bits is part of the design
specifications for this code, so that can't be the justification for the
&0xff.
 
C

Charlie Gordon

CBFalconer said:
IMO the fact that the FAQ makes (and documents) assumptions about
CHAR_BIT is no excuse for doing the same in this newsgroup,
especially without clearly so documenting. In other words, it does
matter, and omitting it will lead all sorts of innocent newbies
into dingy dark despondent passages. Look with especial suspicion
on any constant '8' in your code.

You are being excessively picky here: the OP writes "the code already
assumes the char is 8 bits". Innocent newbies should be shielded from these
arcane considerations until they become proficient enough to master them,
and versatile enough to actually care about them. Bashing beginners with
the intricacies of non twos-complement representations is stupid and
counter-productive. What would you think of a school teacher that would
concentrate on esoteric typographical details and abtruse considerations on
ancient hieroglyphs instead of first polishing the basic reading skills of
her pupils ?

These constant recriminations are just a form of pedantry, far too common on
this newsgroup, stretched to the extreme in a form a pointless sickening
competition.
 
J

Jorge Peixoto

* Even positive numbers may be represented in a crazy way. On the
Not so. The fundamental binary representation of positive integers
is specified in the standard.

So a machine that uses BCD would have to do emulation to be the target
of a C implementation?

In any event, I think this issue should be in the answer to the
question. People will be thinking why is that &0xff needed, and the
FAQ should be didactic.
 
C

Charlie Gordon

pete said:
No.

There are three allowable ways to represent (-1) in 16 bits:
1111 1111 1111 1111
1111 1111 1111 1110
1000 0000 0000 0001

If s.i16 has a value of (-1),
then (s.i16 & 0xff) can have a value of either
255, or 254, or 1.

((unsigned char)-1) is always equal to UCHAR_MAX.

The OP's purpose is to serialize the integer to a stream. For maximum
portability, it is obviously be preferable that the resulting stream be
independant of the particular representation of integers on the stream
writer and reader. Therefore the method used is inappropriate, s.i32 should
first be converted to an unsigned type, and then shifted around and masked
off in chunks of fixed numbers of bits (8 is an obvious choice), streamed
out in a defined order (big endian here).

putc(((unsigned long)s.i32 >> 24) & 0xff, fp);
putc(((unsigned long)s.i32 >> 16) & 0xff, fp);
putc(((unsigned long)s.i32 >> 8) & 0xff, fp);
putc(((unsigned long)s.i32 >> 0) & 0xff, fp);

putc(((unsigned int)s.i16 >> 8) & 0xff, fp);
putc(((unsigned int)s.i16 >> 0) & 0xff, fp);

This code can be simplified, extra operations are here for reasons of
symmetry.
 
J

James Kuyper

Jorge Peixoto wrote:
....
So a machine that uses BCD would have to do emulation to be the target
of a C implementation?

Yes. Bitwise operators must be implemented as if operating on a binary
representation of the value, which would presumably not be a simple
operation on such a machine. Trinary machines (which have some
interesting advantages in certain contexts compared to binary machines)
would also be problematic.
 
C

CBFalconer

Charlie said:
"CBFalconer" <[email protected]> a écrit:
.... snip ...


You are being excessively picky here: the OP writes "the code
already assumes the char is 8 bits". Innocent newbies should be
shielded from these arcane considerations until they become
proficient enough to master them, and versatile enough to
actually care about them. Bashing beginners with the intricacies
of non twos-complement representations is stupid and counter-
productive. What would you think of a school teacher that would
concentrate on esoteric typographical details and abtruse
considerations on ancient hieroglyphs instead of first polishing
the basic reading skills of her pupils ?

These constant recriminations are just a form of pedantry, far too
common on this newsgroup, stretched to the extreme in a form a
pointless sickening competition.

You mistake the criticism. It is not about 1's vs 2's complement
vs sign-magnitude (which is easily handled by dealing with values,
rather than bit pattern), but about the size of CHAR_BIT,
specifying the size of a byte on that machine.
 
C

Chad

No.

There are three allowable ways to represent (-1) in 16 bits:
1111 1111 1111 1111
1111 1111 1111 1110
1000 0000 0000 0001

If s.i16 has a value of (-1),
then (s.i16 & 0xff) can have a value of either
255, or 254, or 1.

((unsigned char)-1) is always equal to UCHAR_MAX.


Would 0xff on a 16 bit machine be represented as
0000 0000 1111 1111

In other words, would there be leading zero's before 1111 1111 ?
 

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

Similar Threads


Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top