Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c > #91346 > unrolled thread
| Started by | sinbad <sinbad.sinbad@gmail.com> |
|---|---|
| First post | 2016-07-30 02:38 -0700 |
| Last post | 2016-07-30 10:25 -0700 |
| Articles | 17 — 10 participants |
Back to article view | Back to comp.lang.c
bit value of a negative number sinbad <sinbad.sinbad@gmail.com> - 2016-07-30 02:38 -0700
Re: bit value of a negative number Malcolm McLean <malcolm.mclean5@btinternet.com> - 2016-07-30 02:50 -0700
Re: bit value of a negative number Ben Bacarisse <ben.usenet@bsb.me.uk> - 2016-07-30 11:42 +0100
Re: bit value of a negative number James Kuyper <jameskuyper@verizon.net> - 2016-07-30 09:38 -0400
Re: bit value of a negative number "James R. Kuyper" <jameskuyper@verizon.net> - 2016-07-30 11:39 -0400
Re: bit value of a negative number Keith Thompson <kst-u@mib.org> - 2016-07-30 13:59 -0700
Re: bit value of a negative number Ben Bacarisse <ben.usenet@bsb.me.uk> - 2016-07-30 15:16 +0100
Re: bit value of a negative number Tim Rentsch <txr@alumni.caltech.edu> - 2016-07-31 06:31 -0700
Re: bit value of a negative number Ben Bacarisse <ben.usenet@bsb.me.uk> - 2016-07-31 17:28 +0100
Re: bit value of a negative number supercat@casperkitty.com - 2016-07-31 13:19 -0700
Re: bit value of a negative number Ike Naar <ike@iceland.freeshell.org> - 2016-07-31 20:46 +0000
Re: bit value of a negative number Tim Rentsch <txr@alumni.caltech.edu> - 2016-07-30 14:21 -0700
Re: bit value of a negative number supercat@casperkitty.com - 2016-07-31 13:37 -0700
Re: bit value of a negative number Tim Rentsch <txr@alumni.caltech.edu> - 2016-07-31 16:59 -0700
Re: bit value of a negative number supercat@casperkitty.com - 2016-07-31 12:56 -0700
Re: bit value of a negative number Barry Schwarz <schwarzb@dqel.com> - 2016-07-30 10:15 -0700
Re: bit value of a negative number Tim Rentsch <txr@alumni.caltech.edu> - 2016-07-30 10:25 -0700
| From | sinbad <sinbad.sinbad@gmail.com> |
|---|---|
| Date | 2016-07-30 02:38 -0700 |
| Subject | bit value of a negative number |
| Message-ID | <159d5df7-d640-40b1-9866-2bb16d7769ee@googlegroups.com> |
why does the following program prints a garbage value for the last bit.
I was expecting a '1' instead of 4294967295.
0 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 4294967295
1 #include"stdio.h"
2
3 int bit_mask(int x)
4 {
5 int i = 0;
6 int j = 0;
7
8 for (i = 0;i < 4 ;i++) {
9 for (j = 0;j < 8;j++) {
10 printf("%u ", (x & (1 << ((i*8) + j))) >> ((i*8) + j));
11 }
12 printf("\n");
13 }
14
15 return(0);
16 }
17
18 int main()
19 {
20 int x = -2;
21
22 bit_mask(x);
23
24 return(0);
25 }
[toc] | [next] | [standalone]
| From | Malcolm McLean <malcolm.mclean5@btinternet.com> |
|---|---|
| Date | 2016-07-30 02:50 -0700 |
| Message-ID | <061cf719-b820-4b43-b9a9-e7657e1f7598@googlegroups.com> |
| In reply to | #91346 |
On Saturday, July 30, 2016 at 10:38:40 AM UTC+1, sinbad wrote:
> why does the following program prints a garbage value for the last bit.
> I was expecting a '1' instead of 4294967295.
>
> 0 1 1 1 1 1 1 1
> 1 1 1 1 1 1 1 1
> 1 1 1 1 1 1 1 1
> 1 1 1 1 1 1 1 4294967295
>
> 1 #include"stdio.h"
> 2
> 3 int bit_mask(int x)
> 4 {
> 5 int i = 0;
> 6 int j = 0;
> 7
> 8 for (i = 0;i < 4 ;i++) {
> 9 for (j = 0;j < 8;j++) {
> 10 printf("%u ", (x & (1 << ((i*8) + j))) >> ((i*8) + j));
> 11 }
> 12 printf("\n");
> 13 }
> 14
> 15 return(0);
> 16 }
> 17
> 18 int main()
> 19 {
> 20 int x = -2;
> 21
> 22 bit_mask(x);
> 23
> 24 return(0);
> 25 }
>
sign extension.
if we right-shift (16 bit for ease of writing)
01000000 00000000
we get
00100000 00000000
But if we right-shift
10000000 00000000
we could get
11000000 00000000
or
01000000 00000000
depending on whether the compiler think we want a bit mask operation
or a cheap and cheerful divide of a signed value by two
when you shift all the way, you get
11111111 11111111
or -1, which is -32768 / (pow(2,15)
[toc] | [prev] | [next] | [standalone]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2016-07-30 11:42 +0100 |
| Message-ID | <87a8gzxv8b.fsf@bsb.me.uk> |
| In reply to | #91346 |
sinbad <sinbad.sinbad@gmail.com> writes:
> why does the following program prints a garbage value for the last bit.
> I was expecting a '1' instead of 4294967295.
>
> 0 1 1 1 1 1 1 1
> 1 1 1 1 1 1 1 1
> 1 1 1 1 1 1 1 1
> 1 1 1 1 1 1 1 4294967295
>
> 1 #include"stdio.h"
> 2
> 3 int bit_mask(int x)
> 4 {
> 5 int i = 0;
> 6 int j = 0;
> 7
> 8 for (i = 0;i < 4 ;i++) {
> 9 for (j = 0;j < 8;j++) {
> 10 printf("%u ", (x & (1 << ((i*8) + j))) >> ((i*8) + j));
> 11 }
> 12 printf("\n");
> 13 }
> 14
> 15 return(0);
> 16 }
> 17
> 18 int main()
> 19 {
> 20 int x = -2;
> 21
> 22 bit_mask(x);
> 23
> 24 return(0);
> 25 }
I think most people would prefer source to shown without line numbers.
It makes it simpler to test.
Anyway, getting to your question...
In order to allow fast code to be generated, C imposes only very loose
conditions on what its operators should do. Over the years, hardware
designers have implemented lots of slightly different kinds of shift
instruction that do slightly different things in the various odd cases
like shifting a bit up into the sign position, or shifting all the bit
out of a number (shifting by the width or more). Anything outside a
very narrow common core of behaviour, C defined as "undefined". This
means that a << or a >> can be replaced by a single shift instruction
because C only guarantees the minimum that almost all hardware complies
with.
When you do 1 << 31 (that's when i == 3 and j == 7) your program is
undefined by the C standard. At this point anything goes. On an x86
the most likely outcome is you get -2147483648 (the value of 1 in the
sign bit and zeros elsewhere). Shifting that value right is, again,
undefined by C but, in this case, happens to propagate the sign bit
giving you -1 (the value when all the bit are 1). Now you do a third
undefined operation, or at least one that is dodgy, because you try to
print a signed int with the conversion used for unsigned int. This has
causes printf to treat the all-bits 1 pattern as an unsigned int whose
value is 2**32-1 or 4294967295.
Wherever possibly you should avoid writing programs with undefined
behaviour. There is really no alternative but to learn exactly what C
defines for the shift operations, but very often all you need to do is
to use unsigned arithmetic rather than signed arithmetic. Your example
can be fixed by adding one character: change the 1 to 1u.
Often the best way to examine the representation of an object is to
treat it as a sequence of unsigned char which you can do with a union.
For any complete object type T you can write
union { T value; unsigned char byte[sizeof (T)]; } u;
and, after setting u.value, you can look at u.byte[0] and upwards to see
the bits that make up the value (including any padding bits).
--
Ben.
[toc] | [prev] | [next] | [standalone]
| From | James Kuyper <jameskuyper@verizon.net> |
|---|---|
| Date | 2016-07-30 09:38 -0400 |
| Message-ID | <nniald$65o$1@dont-email.me> |
| In reply to | #91351 |
On 07/30/2016 07:31 AM, Stefan Ram wrote: ... > I think the old recommendation for pointer type punning Pointer type punning can violate the anti-aliasing rules; using unions is safer, not less safe. > stems from a misunderstanding of a phrase in the standard: > > 6.2.6.1p7 in 2016: > > �When a value is stored in a member of an object of > union type, the bytes of the object representation that > do not correspond to that member but do correspond to > other members take unspecified values.� > > It seems that in my program above, there are no bytes that > correspond to one member but not to the other, so that the > conclusion of this sentence does not apply. > > But some people read the sentence quoted above superficially > and take it to mean that the other members take undefined > values. Possibly that was the reason they recommended me to > prefer pointer type punning 15 years ago. As I remember, the fact that it was technically problematic was due to an explicit statement to that effect in the C90 standard: reading the value of a member of a union that was not the same as the one last written to was problematic in some way - but I don't have a copy of C90 that I can reference to confirm that. The relevant clause should be in the semantics section of the description of the member selection operator. > Still, I have not yet read a sentence in the standard that > says which values the other members /do/ take, when a value > is stored in one member (assuming - for simplicity that all > members have the same size). Since this can differ between > implementations, it might be /unspecified/ too. It is. > Of, course, the naive assumption is that there is a mapping > from values of a type to bit sequences of the length > sizeof( T )*CHAR_BIT. When I call this mapping B( T ) and its > inverse V( T ), then we assume that type punning from type T > to type S give > > V( S )( B( T )( v )) Since C99, the standard now describes the behavior in terms of reinterpretation of the bits, which is basically what you've just described. However, it does so in a footnote rather than in the normative text. That's only supposed to happen if that is a conclusion which can be rigorously derived from the normative text, but I'm not sure of that derivation. In any event, it's also what essentially every real implementation did even before the wording was changed.
[toc] | [prev] | [next] | [standalone]
| From | "James R. Kuyper" <jameskuyper@verizon.net> |
|---|---|
| Date | 2016-07-30 11:39 -0400 |
| Message-ID | <f4f991da-b4e8-b84d-5139-e48b025c9fa8@verizon.net> |
| In reply to | #91355 |
On 07/30/2016 09:59 AM, Stefan Ram wrote: > James Kuyper <jameskuyper@verizon.net> writes: >> As I remember, the fact that it was technically problematic was due to >> an explicit statement to that effect in the C90 standard: reading the >> value of a member of a union that was not the same as the one last >> written to was problematic in some way - but I don't have a copy of C90 >> that I can reference to confirm that. The relevant clause should be in >> the semantics section of the description of the member selection operator. > > Quoting from an old draft, from possibly 1988 or 1989 > (3.3.2.3, Semantics, p3): > > »if a member of a union object is accessed after a value > has been stored in a different member of the object, the > behavior is implementation-defined.«. > > »Implementation-defined«, however, might be the best one can > hope for anyways, even today. Not quite. The C standard says "Except for bit-fields, objects are composed of contiguous sequences of one or more bytes, the number, order, and encoding of which are either explicitly specified or implementation-defined." Assuming that footnote 95 correctly describes something that's guaranteed by the normative text, it's possible to use the implementation-defined encoding to determine precisely what the result of reinterpreting the bits would be. Under C90, the clause cited above gave the implementation the freedom to define it as having a result different from that which could be inferred from a knowledge of the encoding. I can't imagine any good reason for doing so, and I doubt that any implementation actually did so (which is why the C99 wording could be changed in this fashion without causing problems) but it was permitted. >> reinterpretation of the bits, which is basically what you've just >> described. However, it does so in a footnote rather than in the >> normative text. > > Yes, »6.5.2.3 Structure and union members« in p3 has: > > »A postfix expression followed by the . operator and an > identifier designates a member of a structure or union > object. The value is that of the named member,95)«, and > > »95) If the member used to read the contents of a union > object is not the same as the member last used to store > a value in the object, the appropriate part of the > object representation of the value is reinterpreted as > an object representation in the new type as described in > 6.2.6 (a process sometimes called "type punning"). This > might be a trap representation.«. > > I have not yet found a part in 6.2.6 that gives more > semantics to union punning, though. I agree - but at least one C committee member has claimed that it can be derived from other words somewhere in the standard (a claim that he failed to back up with explicit citations).
[toc] | [prev] | [next] | [standalone]
| From | Keith Thompson <kst-u@mib.org> |
|---|---|
| Date | 2016-07-30 13:59 -0700 |
| Message-ID | <lntwf66dwh.fsf@kst-u.example.com> |
| In reply to | #91355 |
James Kuyper <jameskuyper@verizon.net> writes:
> On 07/30/2016 07:31 AM, Stefan Ram wrote:
[...]
> As I remember, the fact that it was technically problematic was due to
> an explicit statement to that effect in the C90 standard: reading the
> value of a member of a union that was not the same as the one last
> written to was problematic in some way - but I don't have a copy of C90
> that I can reference to confirm that. The relevant clause should be in
> the semantics section of the description of the member selection operator.
C90 6.3.2.3 says:
With one exception, if a member of a union object is accessed after
a value has been stored in a different member of the object, the
behavior is implementation-defined.
(The exception is the common initial sequence rule.) A footnote on that
sentence says:
The "byte orders" for scalar types are invisible to isolated
programs that do not indulge in type punning (for example, by
assigning to one member of a union and inspecting the storage by
accessing another member that is an appropriately sized array
of character type), but must be accounted for when conforming
to externally imposed storage layouts.
[...]
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
[toc] | [prev] | [next] | [standalone]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2016-07-30 15:16 +0100 |
| Message-ID | <8737mrxlbg.fsf@bsb.me.uk> |
| In reply to | #91351 |
ram@zedat.fu-berlin.de (Stefan Ram) writes:
> Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
<snip>
>>Often the best way to examine the representation of an object is to
>>treat it as a sequence of unsigned char which you can do with a union.
>>For any complete object type T you can write
>>union { T value; unsigned char byte[sizeof (T)]; } u;
>>and, after setting u.value, you can look at u.byte[0] and upwards to see
>>the bits that make up the value (including any padding bits).
>
> One might say that in C there are attempts to do type
> punning with unions or with pointers. And it seems to me
> that today it is recommended to do it with unions. When I
> read about this in the German C language newsgroup one or
> two decades ago, they still recommended pointers,
It's a good way too. For some people learning C, the union way might be
more natural.
> so the
> following program I wrote at this time (15 years ago?) is
> using pointers:
>
> bitprint.c
>
> #include <stdio.h> /* putchar */
> #include <limits.h> /* CHAR_BIT */
>
> #define DUMP(T,w) { const T const v = w; \
> dump( &v, sizeof( v )); putchar( '\n' ); }
Personally, I'd include a prototype for the dump function in a macro
like this since, without it, the conversion of &v might go wrong. Of
course you expect DUMP to be used only in places where a prototype for
dump is in scope, but I like to be cautious.
> static void dump( void const * const buffer, int j )
> { char const * const b = buffer; while( j-- )
> { register char const c = b[ j ];
> for( register int i = CHAR_BIT; i--; )
> putchar( '0' +( c >> i & 1 )); }}
>
> int main( void ){ DUMP(double,1.0) DUMP(int,1) }
>
> stdout
> 0011111111110000000000000000000000000000000000000000000000000000
> 00000000000000000000000000000001
>
> stdout
> 0000000000000000000000000000000000000000000000001111000000111111
> 00000001000000000000000000000000
>
> The outputs are shown for two different implementations.
>
> Now, in 2016, I try to rewrite it to union type punning:
>
> #include <stdio.h> /* putchar */
> #include <limits.h> /* CHAR_BIT */
>
> #define DUMP(T,v) \
> { union { T value; unsigned char buffer[ sizeof( T )]; } u; \
> u.value = v; \
> dump( u.buffer, sizeof( T )); putchar( '\n' ); }
Or, in >= C99:
(dump((union { T value; unsigned char buffer[sizeof (T)]}){
.value = (v) }.buffer, sizeof (T)), putchar('\n'))
and it's syntactically an expression just like a normal function call.
> static void dump( unsigned char const * const b, int j )
> { while( j-- )
> { unsigned char const c = b[ j ];
> for( int i = CHAR_BIT; i--; )
> putchar( '0' +( c >> i & 1 )); }}
>
> int main( void ){ DUMP(double,1.0) DUMP(int,1) }
>
> stdout
> 0011111111110000000000000000000000000000000000000000000000000000
> 00000000000000000000000000000001
>
> I think the old recommendation for pointer type punning
> stems from a misunderstanding of a phrase in the standard:
>
> 6.2.6.1p7 in 2016:
>
> »When a value is stored in a member of an object of
> union type, the bytes of the object representation that
> do not correspond to that member but do correspond to
> other members take unspecified values.«
>
> It seems that in my program above, there are no bytes that
> correspond to one member but not to the other, so that the
> conclusion of this sentence does not apply.
I think each member corresponds to the other and the conclusion does
apply.
> But some people read the sentence quoted above superficially
> and take it to mean that the other members take undefined
> values. Possibly that was the reason they recommended me to
> prefer pointer type punning 15 years ago.
They take unspecified values. Some people have, I think, mistakenly
taken this to mean the action is always undefined. I don't think it is,
though it might be in some cases when the result is a trap
representation.
> Still, I have not yet read a sentence in the standard that
> says which values the other members /do/ take, when a value
> is stored in one member (assuming - for simplicity that all
> members have the same size). Since this can differ between
> implementations, it might be /unspecified/ too.
Quite. The standard can't really say without going into all sort of
troublesome detail, but a helpful footnote explains that the intent is
for the natural meaning to prevail:
"If the member used to read the contents of a union object is not the
same as the member last used to store a value in the object, the
appropriate part of the object representation of the value is
reinterpreted as an object representation in the new type as described
in 6.2.6 (a process sometimes called 'type punning'). This might be a
trap representation."
> Of, course, the naive assumption is that there is a mapping
> from values of a type to bit sequences of the length
> sizeof( T )*CHAR_BIT. When I call this mapping B( T )
I don't think it's naive. For complete object types T there is such a
mapping. This mapping is not a function, though, because a value may
have multiple representations.
> and its inverse V( T ),
B(T) has no inverse in the usual sense because it is not a function,
but the mapping from representations to values *is* a function, albeit a
partial one.
> then we assume that type punning from type T
> to type S give
>
> V( S )( B( T )( v ))
>
> for a value v of type T. But I am not sure whether the
> C standard actually says that something like this happens
> when doing union type punning.
>
> In reality the simplicity of this mapping might be
> distorted by trap representations or by being a relation
> instead of a mapping (i.e., by not being 1-to-1). For
> example, it is conceivable that for values of certain
> types the values of some bits of the underlying bit
> representation are ignored.
Yes, it not quite as simple as it may appear because B(T) is a
multi-valued map and V(S) is a partial function.
--
Ben.
[toc] | [prev] | [next] | [standalone]
| From | Tim Rentsch <txr@alumni.caltech.edu> |
|---|---|
| Date | 2016-07-31 06:31 -0700 |
| Message-ID | <kfnzioylyrb.fsf@x-alumni2.alumni.caltech.edu> |
| In reply to | #91358 |
Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
> ram@zedat.fu-berlin.de (Stefan Ram) writes:
>
>> Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
>
> <snip>
>
>>> Often the best way to examine the representation of an object is to
>>> treat it as a sequence of unsigned char which you can do with a union.
>>> For any complete object type T you can write
>>> union { T value; unsigned char byte[sizeof (T)]; } u;
>>> and, after setting u.value, you can look at u.byte[0] and upwards to see
>>> the bits that make up the value (including any padding bits).
[...]
>> I think the old recommendation for pointer type punning
>> stems from a misunderstanding of a phrase in the standard:
>>
>> 6.2.6.1p7 in 2016:
>>
>> >>When a value is stored in a member of an object of
>> union type, the bytes of the object representation that
>> do not correspond to that member but do correspond to
>> other members take unspecified values.<<
>>
>> It seems that in my program above, there are no bytes that
>> correspond to one member but not to the other, so that the
>> conclusion of this sentence does not apply.
>
> I think each member corresponds to the other and the conclusion does
> apply.
I don't know what you mean to say here. Do you agree that there
are no bytes that correspond to one member but do not correspond
to any other member? So the prescription that certain bytes take
unspecified values applies to no bytes? If it applies to no
bytes, how is that different from saying it doesn't apply?
>> But some people read the sentence quoted above superficially
>> and take it to mean that the other members take undefined
>> values. Possibly that was the reason they recommended me to
>> prefer pointer type punning 15 years ago.
>
> They take unspecified values. Some people have, I think, mistakenly
> taken this to mean the action is always undefined. I don't think it is,
> though it might be in some cases when the result is a trap
> representation.
Why do you say other members take unspecified values? If we have
a variable 'foo' of union type with members R and W, and their
sizes obey sizeof foo.R <= sizeof foo.W, and if the mappings
between values and object representations of the two types are
both 1-1 and onto, then after 'foo.W = something;', how can the
value of foo.R be unspecified? I believe the Standard requires
the value of foo.R to be completely determined, dependent of
course on the encodings of the two types involved, but those
encodings are implementation-defined, under 6.2.6.1 p2. Is
there some part of the last sentence with which you do not
agree? If so can you say which part and why?
>> Still, I have not yet read a sentence in the standard that
>> says which values the other members /do/ take, when a value
>> is stored in one member (assuming - for simplicity that all
>> members have the same size). Since this can differ between
>> implementations, it might be /unspecified/ too.
>
> Quite. The standard can't really say without going into all sort of
> troublesome detail, but a helpful footnote explains that the intent is
> for the natural meaning to prevail:
>
> "If the member used to read the contents of a union object is not the
> same as the member last used to store a value in the object, the
> appropriate part of the object representation of the value is
> reinterpreted as an object representation in the new type as described
> in 6.2.6 (a process sometimes called 'type punning'). This might be a
> trap representation."
After your previous statement about other members taking on
unspecified values this response is confusing. If there is rule
for what value is produced, doesn't that mean that the values are
/not/ unspecified? Of course the result may depend on bits whose
values are (partially) unspecified, but that is different than
saying the result is an unspecified value. Can you clear up this
confusion?
[toc] | [prev] | [next] | [standalone]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2016-07-31 17:28 +0100 |
| Message-ID | <87bn1dwz4r.fsf@bsb.me.uk> |
| In reply to | #91388 |
Tim Rentsch <txr@alumni.caltech.edu> writes:
> Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
>
>> ram@zedat.fu-berlin.de (Stefan Ram) writes:
>>
>>> Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
>>
>> <snip>
>>
>>>> Often the best way to examine the representation of an object is to
>>>> treat it as a sequence of unsigned char which you can do with a union.
>>>> For any complete object type T you can write
>>>> union { T value; unsigned char byte[sizeof (T)]; } u;
>>>> and, after setting u.value, you can look at u.byte[0] and upwards to see
>>>> the bits that make up the value (including any padding bits).
>
> [...]
>
>>> I think the old recommendation for pointer type punning
>>> stems from a misunderstanding of a phrase in the standard:
>>>
>>> 6.2.6.1p7 in 2016:
>>>
>>> >>When a value is stored in a member of an object of
>>> union type, the bytes of the object representation that
>>> do not correspond to that member but do correspond to
>>> other members take unspecified values.<<
>>>
>>> It seems that in my program above, there are no bytes that
>>> correspond to one member but not to the other, so that the
>>> conclusion of this sentence does not apply.
>>
>> I think each member corresponds to the other and the conclusion does
>> apply.
>
> I don't know what you mean to say here.
Neither do I. I was misreading it or, to be more accurate, not reading
it in that I think I just assumed I knew what it was saying.
<snip>
--
Ben.
[toc] | [prev] | [next] | [standalone]
| From | supercat@casperkitty.com |
|---|---|
| Date | 2016-07-31 13:19 -0700 |
| Message-ID | <eea0f63d-7206-406d-a73f-b3f15be88dda@googlegroups.com> |
| In reply to | #91358 |
On Saturday, July 30, 2016 at 9:16:59 AM UTC-5, Ben Bacarisse wrote:
> They take unspecified values. Some people have, I think, mistakenly
> taken this to mean the action is always undefined. I don't think it is,
> though it might be in some cases when the result is a trap
> representation.
The Standard lacks the terminology to describe anything beyond
Implementation-Defined behavior and Undefined Behavior. The term
"Unspecified Value" allows for Undefined Behavior only in cases
where the type in question has defined trap representations.
"Indeterminate Value", however, is a bit trickier. There is some
usefulness to allowing a compiler to replace something like:
int foo;
void foo(void)
{
int a=foo;
int b=a;
... code that uses neither a, nor b, nor foo ...
if (rare_condition1) { ... do something with a }
if (rare_condition2) { ... do something with b }
}
with
int foo;
void foo(void)
{
... code that doesn't use foo ...
if (rare_condition1) { ... do something with foo }
if (rare_condition2) { ... do something with foo }
}
even though if something were to affect "foo" without the compiler's
knowledge that could cause the values of "a" and "b" to appear
different. Since the Standard defines no means by which the values
of "a" and "b" could differ, anything which could cause the apparent
values to differ must be branded as Undefined Behavior, even if
the results of all computations involving a and b end up getting
ignored.
Given that it's often easier to unconditionally perform computations
whose outcome which may or may not end up being relevant than to
check whether computations will be relevant before performing them,
an ability to safely perform operations on Indeterminate values, with
the consequence that the results would be Indeterminate absent some
action to turn them into Unspecified values, would be very useful.
Usefully describing what implementations would or would not be required
to guarantee about the behavioral consequences, however, would require
a level of detail beyond what the authors of the Standard wanted to
employ.
[toc] | [prev] | [next] | [standalone]
| From | Ike Naar <ike@iceland.freeshell.org> |
|---|---|
| Date | 2016-07-31 20:46 +0000 |
| Message-ID | <slrn3vfsnpsotb.m4v.ike@iceland.freeshell.org> |
| In reply to | #91425 |
On 2016-07-31, supercat@casperkitty.com <supercat@casperkitty.com> wrote: > On Saturday, July 30, 2016 at 9:16:59 AM UTC-5, Ben Bacarisse wrote: >> They take unspecified values. Some people have, I think, mistakenly >> taken this to mean the action is always undefined. I don't think it is, >> though it might be in some cases when the result is a trap >> representation. > > The Standard lacks the terminology to describe anything beyond > Implementation-Defined behavior and Undefined Behavior. The term > "Unspecified Value" allows for Undefined Behavior only in cases > where the type in question has defined trap representations. > "Indeterminate Value", however, is a bit trickier. There is some > usefulness to allowing a compiler to replace something like: > > int foo; > void foo(void) Those declarations contradict.
[toc] | [prev] | [next] | [standalone]
| From | Tim Rentsch <txr@alumni.caltech.edu> |
|---|---|
| Date | 2016-07-30 14:21 -0700 |
| Message-ID | <kfn4m76om89.fsf@x-alumni2.alumni.caltech.edu> |
| In reply to | #91351 |
ram@zedat.fu-berlin.de (Stefan Ram) writes:
> Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
> [...]
>
>> Often the best way to examine the representation of an object is to
>> treat it as a sequence of unsigned char which you can do with a union.
>> For any complete object type T you can write
>> union { T value; unsigned char byte[sizeof (T)]; } u;
>> and, after setting u.value, you can look at u.byte[0] and upwards to see
>> the bits that make up the value (including any padding bits).
>
> One might say that in C there are attempts to do type
> punning with unions or with pointers. And it seems to me
> that today it is recommended to do it with unions. When I
> read about this in the German C language newsgroup one or
> two decades ago, they still recommended pointers, so the
> following program I wrote at this time (15 years ago?) is
> using pointers: [... program and sample outputs ...]
>
> Now, in 2016, I try to rewrite it to union type punning:
>
> #include <stdio.h> /* putchar */
> #include <limits.h> /* CHAR_BIT */
>
> #define DUMP(T,v) \
> { union { T value; unsigned char buffer[ sizeof( T )]; } u; \
> u.value = v; \
> dump( u.buffer, sizeof( T )); putchar( '\n' ); }
>
> static void dump( unsigned char const * const b, int j )
> { while( j-- )
> { unsigned char const c = b[ j ];
> for( int i = CHAR_BIT; i--; )
> putchar( '0' +( c >> i & 1 )); }}
>
> int main( void ){ DUMP(double,1.0) DUMP(int,1) }
>
> stdout
> 0011111111110000000000000000000000000000000000000000000000000000
> 00000000000000000000000000000001
>
> I think the old recommendation for pointer type punning
> stems from a misunderstanding of a phrase in the standard:
>
> 6.2.6.1p7 in 2016:
>
> >>When a value is stored in a member of an object of
> union type, the bytes of the object representation that
> do not correspond to that member but do correspond to
> other members take unspecified values.<<
>
> It seems that in my program above, there are no bytes that
> correspond to one member but not to the other, so that the
> conclusion of this sentence does not apply.
Right.
> But some people read the sentence quoted above superficially
> and take it to mean that the other members take undefined
> values. Possibly that was the reason they recommended me to
> prefer pointer type punning 15 years ago.
Another possible reason is that C99 didn't make it clear what was
supposed to happen. If you look in N1256 (dated September 2007),
section 6.5.2.3 paragraph 3 has a clarifying footnote:
If the member used to access the contents of a union object
is not the same as the member last used to store a value in
the object, the appropriate part of the object representation
of the value is reinterpreted as an object representation in
the new type as described in 6.2.6 (a process sometimes
called "type punning"). This might be a trap representation.
However, this footnote does not appear in N1124 (dated May 2005).
So at the time being considered, 15 years ago, it wasn't obvious
what meaning was intended.
> Still, I have not yet read a sentence in the standard that
> says which values the other members /do/ take, when a value
> is stored in one member (assuming - for simplicity that all
> members have the same size). Since this can differ between
> implementations, it might be /unspecified/ too.
The Standard does a poor job of expressing what the semantics
are here. Reading through the notes of various WG14 meetings
(and maybe also some Defect Reports), it is clear that what
was intended has been the same all along - ie, that the bits of
representation are re-interpreted as a representation of the
type of the member being read. The statement in C89/C90 is
a little different from that, but that version of the Standard
did not discuss the notion of /object representation/, so it
didn't really have the right constructs available to be able
to describe it. In any case the footnote in N1256, and still
present in C11, clearly states what semantics are expected,
and ought to suffice for anyone who only wants to know what
the Standard stipulates, and isn't interested in bickering
about the technicalities of how the semantics are expressed.
> Of, course, the naive assumption is that there is a mapping
> from values of a type to bit sequences of the length
> sizeof( T )*CHAR_BIT. When I call this mapping B( T ) and its
> inverse V( T ), then we assume that type punning from type T
> to type S give
>
> V( S )( B( T )( v ))
>
> for a value v of type T. But I am not sure whether the
> C standard actually says that something like this happens
> when doing union type punning.
Based on the footnote, it seems fair to conclude that WG14 would
say that this result is implied by normative text. My own view
is similar to that. IMO this result does follow from how the
Standard describes the semantics of union types, represenatations
of objects, assignment, and lvalue conversion. The description
falls short (IMO) in that it could (and should) be more explicit
about the transformations that occur between abstract values and
object representations. Clearly the Standard expects that these
transformations occur, but doesn't say so in so many words. But
that is a language-lawyer type of quibble. In practical terms
there isn't any question but that this meaning applies.
> In reality the simplicity of this mapping might be
> distorted by trap representations or by being a relation
> instead of a mapping (i.e., by not being 1-to-1). For
> example, it is conceivable that for values of certain
> types the values of some bits of the underlying bit
> representation are ignored.
Right. What happens is implementation-defined, except that some
aspects of the implementation-defined values may be partially
unspecified. If the types involved both have 1-1 and onto
mappings, then the result is completely determined. Similarly,
if the type used for reading has no dependencies on any
unspecified or partially unspecified bits of the type used for
writing, then the result is completely determined (allowing a
"trap representation" to be a possible result). The last case,
where the type used for reading does have dependencies on
unspecified or partially unspecified bits of the type used for
writing, produces a result that is unspecified, or at least
partially unspecified. Of course, these shadings of unspecified
value are not defined in the Standard, so it doesn't have a good
way to talk about the different cases here. That observation may
partly explain why the Standard doesn't describe the semantics of
union member access more explicitly and exactly.
[toc] | [prev] | [next] | [standalone]
| From | supercat@casperkitty.com |
|---|---|
| Date | 2016-07-31 13:37 -0700 |
| Message-ID | <313ca936-553b-421b-ab60-371db69b97ab@googlegroups.com> |
| In reply to | #91379 |
On Saturday, July 30, 2016 at 4:22:07 PM UTC-5, Tim Rentsch wrote: > The Standard does a poor job of expressing what the semantics > are here. Reading through the notes of various WG14 meetings > (and maybe also some Defect Reports), it is clear that what > was intended has been the same all along - ie, that the bits of > representation are re-interpreted as a representation of the > type of the member being read. What is supposed to be required when code takes the address of a union member and makes use of the resulting pointer? It might make sense to say that the act of taking the address of a union member shall be deemed as invalidating all existing pointers to other members of that union, but I don't think anything in the Standard *says* that.
[toc] | [prev] | [next] | [standalone]
| From | Tim Rentsch <txr@alumni.caltech.edu> |
|---|---|
| Date | 2016-07-31 16:59 -0700 |
| Message-ID | <kfnk2g1l5pz.fsf@x-alumni2.alumni.caltech.edu> |
| In reply to | #91426 |
supercat@casperkitty.com writes: > On Saturday, July 30, 2016 at 4:22:07 PM UTC-5, Tim Rentsch wrote: >> The Standard does a poor job of expressing what the semantics >> are here. Reading through the notes of various WG14 meetings >> (and maybe also some Defect Reports), it is clear that what >> was intended has been the same all along - ie, that the bits of >> representation are re-interpreted as a representation of the >> type of the member being read. > > What is supposed to be required when code takes the address of a > union member and makes use of the resulting pointer? [...] That question has nothing to do with my comments, which are only about accessing a union member using directly an expression of union type.
[toc] | [prev] | [next] | [standalone]
| From | supercat@casperkitty.com |
|---|---|
| Date | 2016-07-31 12:56 -0700 |
| Message-ID | <b904fa21-ce0b-4511-9ef5-7237f7ee52b3@googlegroups.com> |
| In reply to | #91351 |
On Saturday, July 30, 2016 at 5:42:53 AM UTC-5, Ben Bacarisse wrote: > In order to allow fast code to be generated, C imposes only very loose > conditions on what its operators should do. Over the years, hardware > designers have implemented lots of slightly different kinds of shift > instruction that do slightly different things in the various odd cases > like shifting a bit up into the sign position, or shifting all the bit > out of a number (shifting by the width or more). Anything outside a > very narrow common core of behaviour, C defined as "undefined". This > means that a << or a >> can be replaced by a single shift instruction > because C only guarantees the minimum that almost all hardware complies > with. The issue isn't merely one of performance. A bigger problem is that there were already many C implementations in wide use before the C89 Standard was written, and the authors of the Standard were very loath to forbid any behavior that some code somewhere might be relying upon. What would have been helpful would have been to recognize a concept of "commonplace" and "alternative" implementations, and recognize that an implementation can be classified as a fully-compliant Alternative C Implementation even if some operators' behaviors differ from 99.9% of all other C implementations, but a C implementation could only be classified as commonplace if things behaved in a fashion typical of other compilers. Unfortunately, it seems the Committee was unwilling to classify any existing behaviors--or even potentially-existing behaviors--as obscure, thus leading to a split between the minimalist standard defined by the Standard (which is too weak to guarantee that *any* particular program will run without UB on all compliant implementations), or a language which used to be vastly more powerful but would only run predictably on 90%+ of C implementations. With regard to integer maths, the right approach (which should have been adopted if the Standard had predated contrary implementations) would be to observe that expanding the series (1+2+4+8+16...) to any arbitrary length will yield a value which is congruent to -1 mod any value in the series, and the only integer which is congruent to -1 mod all possible powers of two is -1. In binary, if one subtracts ...0001 from ...0000, the result will be ...1111. No matter how many zeroes one pads the first two numbers with, the result will have the corresponding number of ones. If one performs integer arithmetic using infinite precision and there are no overflows, all of the upper bits will match the highest bit of the represented result; the upper bit in an 8-bit signed number doesn't represent a single bit with significance -128, but instead represents an infinite number of ones extended to the left. Incidentally, one's-complement math makes sense for real-number quantities, using the identity that in binary, 0.1111... infinitely far to the right is equivalent to 1.0000... and thus -0001.0000 would become ...1110.1111... (which would be equivalent to ...1111.0000). One's complement thus works out mathematically if the value of the leftmost bit is replicated on both sides of the number, but for many integer operations it's easier to say that all numbers should be zero-padded past the radix point than to say that positive numbers are zero-padded and negative numbers are ones-padded.
[toc] | [prev] | [next] | [standalone]
| From | Barry Schwarz <schwarzb@dqel.com> |
|---|---|
| Date | 2016-07-30 10:15 -0700 |
| Message-ID | <pomppblkbtfheh1muhuehc2i8kniret6q2@4ax.com> |
| In reply to | #91346 |
On Sat, 30 Jul 2016 02:38:29 -0700 (PDT), sinbad
<sinbad.sinbad@gmail.com> wrote:
>why does the following program prints a garbage value for the last bit.
>I was expecting a '1' instead of 4294967295.
>
>0 1 1 1 1 1 1 1
>1 1 1 1 1 1 1 1
>1 1 1 1 1 1 1 1
>1 1 1 1 1 1 1 4294967295
>
> 1 #include"stdio.h"
> 2
> 3 int bit_mask(int x)
> 4 {
> 5 int i = 0;
> 6 int j = 0;
> 7
> 8 for (i = 0;i < 4 ;i++) {
> 9 for (j = 0;j < 8;j++) {
> 10 printf("%u ", (x & (1 << ((i*8) + j))) >> ((i*8) + j));
%u promises that the corresponding argument is an unsigned int. Your
expression is not. Technically this leads to undefined behavior.
> 11 }
> 12 printf("\n");
> 13 }
> 14
> 15 return(0);
> 16 }
> 17
> 18 int main()
> 19 {
> 20 int x = -2;
> 21
> 22 bit_mask(x);
> 23
> 24 return(0);
> 25 }
Others have explained why this occurred. One way to get the results
you expect is to copy the bit pattern into an unsigned variable. You
could probably use an unsigned int on most systems and change the
first two lines of bit_mask to
int bit_mask(int y)
{
unsigned int x;
memcpy(&x, &y, sizeof y);
However, on some systems this could lead to a trap representation. The
bullet proof approach is to copy the bit pattern to an array of
unsigned char, as in
int bit_mask(int y)
{
unsigned char x[sizeof y];
memcpy(x &y, sizeof y);
and then the value to print is computed by the expression
(unsigned int)((x[i] & (1 << j)) >> j)
Also note that an int need not be four bytes and a byte need not be
eight bits.
--
Remove del for email
[toc] | [prev] | [next] | [standalone]
| From | Tim Rentsch <txr@alumni.caltech.edu> |
|---|---|
| Date | 2016-07-30 10:25 -0700 |
| Message-ID | <kfnh9b7nim8.fsf@x-alumni2.alumni.caltech.edu> |
| In reply to | #91346 |
sinbad <sinbad.sinbad@gmail.com> writes:
> why does the following program prints a garbage value for the last bit.
> I was expecting a '1' instead of 4294967295.
>
> 0 1 1 1 1 1 1 1
> 1 1 1 1 1 1 1 1
> 1 1 1 1 1 1 1 1
> 1 1 1 1 1 1 1 4294967295
>
> 1 #include"stdio.h"
> 2
> 3 int bit_mask(int x)
> 4 {
> 5 int i = 0;
> 6 int j = 0;
> 7
> 8 for (i = 0;i < 4 ;i++) {
> 9 for (j = 0;j < 8;j++) {
> 10 printf("%u ", (x & (1 << ((i*8) + j))) >> ((i*8) + j));
> 11 }
> 12 printf("\n");
> 13 }
> 14
> 15 return(0);
> 16 }
> 17
> 18 int main()
> 19 {
> 20 int x = -2;
> 21
> 22 bit_mask(x);
> 23
> 24 return(0);
> 25 }
Others have explained why you aren't getting the result you
expect. I won't elaborate on those but here are some
suggestions.
Suggestion one: whenever there is shifting involved, use an
unsigned type at least as large as unsigned int.
Suggestion two: for loops of this kind, where segments are broken
up into multiple lines, in many cases it is simpler to code a
single loop, with a conditional printing of an extra newline
inside the loop.
Taking these two suggestions together, here is a re-writing of
your bit_mask() function (with minor layout changes):
int
bit_mask( int x ){
unsigned printed;
unsigned mask;
for( printed = 0, mask = 1; mask != 0; mask <<= 1 ){
printf( "%d ", x & mask ? 1 : 0 );
printed += 1;
if( printed %8 == 0 ) printf( "\n" );
}
return 0;
}
Do you see how this is working? Do you see how it avoids the
problem of shifting a signed integer type? How about the logic
to break into multiple lines every 8 printed digits?
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.c
csiph-web