public inbox for gcc-help@gcc.gnu.org
 help / color / mirror / Atom feed
* Re: Re: Integral conversions in C/C++
@ 2008-04-23  1:34 Tom St Denis
  0 siblings, 0 replies; 5+ messages in thread
From: Tom St Denis @ 2008-04-23  1:34 UTC (permalink / raw)
  To: gcc-help

On Tue 22/04/08  4:46 PM , Christian Böhme monodhs@gmx.de sent:
> > "The correct result"?  The correct result is whatever
> the standard
> > says it is.
> 
> However counter-intuitive to anyone knowing what linear algebra is it
> may possibly seem.  That's also olds.

No, as I pointed out there are counter-examples to your interpretation of "the
way things should be."  If you apply the conversions out of order of precedence
you get undefined behaviour plain and simple.  You're refusal to acknowledge that
notwithstanding, that's the way things are.

And C is not a linear algebra language.  It's also not French.  So are you next
going to complain about the lack of accented keywords?  It's also not Pascal, so
are you gonna complain about the lack of unit support?

> > Right, so it sounds as though you do understand the
> standard.
> 
> I do actually understand a lot more than that.  What gave it away ?
> Is it my pointing to the actual standards text and the insinstence on
> doing so for the responders to prove their arguments ?

The problem is I could read you the entire ISO C spec, and you'd continue to
refuse to acknowledge it.  So it's not worth digging things up.  The way
expressions are parsed in C is well known.  Counter-examples to your
interpretation are easy to produce.  What more do you want?

> > What, then, is your point?
> 
> All in the above.

No, you're just a troll.  Even when it's plain as day that your interpretation is
wrong, you continue to press on the point.  This is not a C standards mailing
list.  If you want a hand in the next ISO C draft, then contact them, not us.

I don't know what you had hoped to accomplish here.  GCC is not wrong in the code
it produces [in this case], your code is buggy in that you interpreted the
standard wrong.

Tom

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Re: Integral conversions in C/C++
@ 2008-04-23  8:42 Tom St Denis
  0 siblings, 0 replies; 5+ messages in thread
From: Tom St Denis @ 2008-04-23  8:42 UTC (permalink / raw)
  To: gcc-help

On Tue 22/04/08  5:35 PM , Christian Böhme monodhs@gmx.de sent:
> Optimizers aren't part of the standard.  If the standard(s) were to
> allow for much more explicit expressions, many optimizers would do a
> much better job.  Writing optimizers around standards is what since
> the 90ies characterizes ``good'' compilers.  Here's where the kewl
> stuff happens.

I think you're not giving the compiler enough credit, for example:

.L13:
        movl    (%ebx), %eax
        mull    (%ecx)
        addl    %eax, 16(%esp)
        adcl    %edx, 20(%esp)
        addl    $1, %esi
        addl    $4, %ebx
        subl    $4, %ecx
        cmpl    %edi, %esi
        jne     .L13

is produced from (omitting the loop entirely...):

     _W += ((mp_word)*tmpx++)*((mp_word)*tmpy--);

Where mp_word in this case is a "unsigned long long" and so is _W.  What I'm
asking for is a 64-bit MULADD and the compiler does it with a 32x32=>64 mul and
an add/adc chain.  Not too bad for a "standards comforming" compiler if you ask
me.  It realizes that both operands are actually 32-bits but that I want the
product to be 64-bit.  So it does the fast 32x32 mult AND keeps the upper product
[EDX in this case].

BTW that's a snippet from my LibTomMath library.  In case you're wondering ... I
write performance portable C cryptographic code for a living, so I know a bit
about what I'm talking...

The trick is to comply with the standard (such that the compiler is worth beans)
but to then really take a hard look at what the user is actually doing.  If you
interpret the standard naively the code above "requires" a 64x64 mult.  However,
the spec only says that the code generated must emulate the standard, not follow
it naively.  So as long as the correct result occurs the compiler is correct.  In
this case, GCC is smart about the operands and simplifies the operation to a
32x32 mult.  

Again, you're inability to understand the way things are doesn't mean you're
correct.  GCC does a very good job at optimizing C code all while following the
specs fairly well in my opinion (have yet to really bump into a gcc-bug of my own
in a long time).  

I think once you figure out that both

(unsigned)-1

and 

unsigned a = 1; -(a)

Result in the same expression type, the better off you'll be. 

The C spec is actually very strict about the type of expressions (I mean data
type).  so when I say 

unsigned char = (unsigned long)((int) + (unsigned)) * (double)

I can follow the precedence tree and know what the data types are at each stage.
 If we allowed the types to be assigned out of order the expressions and the
statement as a whole become undefined and meaningless.  

Assuming you're not a complete troll just trying to cause noise on the list ... I
suggest you read a good book about compiler theory.  When I say statement or
"expression," it might help if you understand what I'm talking about.  That way
you'll fully get the value of my emails, ideally acknowledge that I'm right, go
away, lead a productive life, marry, have kids, and tell them the story about how
some guy who was bored between guitar and piano practice at home after a hard
days of work decided to drop a message and help clear things up.

just saying ...

Tom

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Re: Integral conversions in C/C++
@ 2008-04-21  9:29 Tom St Denis
  0 siblings, 0 replies; 5+ messages in thread
From: Tom St Denis @ 2008-04-21  9:29 UTC (permalink / raw)
  To: gcc-help

On Sun 20/04/08  7:05 PM , Christian Böhme monodhs@gmx.de sent:
> Actually, the notion of an ``unsigned integer'' as it is known in
> the C/C++ world is already an oxymoron.  And it grows from there.
> Don't get me started on the float/double nightmare.

Actually, no it's not.  unsigned just means a subset.  You could just as easily
stated prime integers as a subset of the integers (well I guess natural numbers,
which is itself a subset of the integers).

You're lack of understanding notwithstanding, the concepts of signed and unsigned
are well understood by most experienced developers.

> > And the promotion applies to the operator
> 
> Reiterating a statement without proof does not make said statement
> valid.  Show me the section(s) in the standard(s) that limit integral
> conversions to assignment statements as you claim.

It's not my job to quote you chapter and verse.  It's quite easy to prove this is
how promotions work (hint, my previous example with the four integer types shows
it quite well).

There are those who have problems with data types (you), and those who do not
(me, the rest of the list apparently).  

> > The promotions occur (in time) with respect to the
> order of precedence. 
> > If they occur out of order you end up with nastyness
> (like the float example I
> > sent earlier).
> 
> At which point in the parsing process of the original example is
> there actually an operator precedence resolution involved ?  Care
> to elaborate a bit on that ?

the statement

a = -(b * 10U); 

Can be read as 

- 10U
- b
  - multiply (both are unsigned so no promotion required)
  - negate
  - promote the unsigned result to 64-bit signed (via zero extension)
  - store

That's the order of precedence (I'd do a nice ASCII diagram, but you're not worth
the effort).

> >> given the original expression, a = 4294967280UL is a
> colossal
> >> screw-up which I actually expected the compiler to
> warn me about.
> > 
> 
> > Why?
> 
> The missing warning, the screw-up or both ?

It's not invalid, it's a design fault. 

Again, with CHAR_BIT = 8, what should

unsigned char b = 255;
int a = 3;
a += b;

this produce?  By your logic, it should promote b to an int via sign extension. 
Hint, how is 255 any diff from (unsigned char)-1 ?

> >     You negated an unsigned expression.
> Nope.  I multiplied a natural number by -1 expecting to produce
> an integer lange enough to hold the value of a natural number
> which itself should be large enough to hold the result of a
> multiplication of one natural number variable of limited range
> and a relatively small natural number constant.  Clean a priori
> information that is available to the compiler.  The C/C++
> backwardness, however, maimes it into something that probably
> was the norm in the 70ies but is unacceptable in the 2000s.
> At least in my book.  But then again, it's only PeeCees in the
> computing world even on the MPAs nowadays so why bother.

Your problem is you think the compiler does type promotion out of the order of
precedence.  This breaks so much logic.

For example, consider

unsigned a = 60000;
unsigned char b = 255, c;
c = a / b;

By your logic, we should convert everything to unsigned char first?  Or should we
promote b to unsigned, then divide, then convert to unsigned char to store it?

The conversion to the lvalue type occurs when we reach that operator in the tree
of expressions that make up the statement.

> > unsigned char b = 255;
> > int a = 4;
> > a += b;
> > Should the result be 3?
> 
> Define the ranges of ``unsigned char'' and ``int'' in your example
> and I can give you a reasonable answer.

Stop trolling, you're just evading my clear cut counter-example of your problem
statement.  

> int64_t a;
> uint32_t b = 0xdeadbeef;
> 
> a = -(((int64_t )b) * 10u);
> 
> then on a 32 bit machine 10u is promoted to int64_t (which is large
> enough to hold the value) and a 64
> performed.  Or isn't it ?  Now, if there was a machine that actually
> implemented a 64
> would the result in a of the original example look like if the code
> was to be translated according to the standard for this particular
> machine ?  Could this multiplication _ever_ be used if the standards
> were followed blindly ?

Well given that the type of the expression (int64_t)b is a signed 64-bit integer,
the 10u is promoted to it as well [via zero extension], then negated.  So you
will get a negative result equal to -0x8B2C97556ULL.  

> Wrong on so many levels.  -1 is neither the value after a promotion
> nor conversion.  If you indeed understood my logic, then you would
> have souped up a conversion example which the above is not.

Um, if you stored 255 into a signed char where CHAR_BIT == 8, then you'd end up
with a negative value of 1 (on a twos compliment machine).  If you then added
this signed char to another signed type, you'd be subtracting one.

> I was probably being too optimistic and should have been more
> specific: If you stick to C you never will.  C++ is sloooowly
> beginning to move into the right direction.  Too many Cisms and
> inconsistencies in it that make coding a tedious task but still
> preferable over C and Fortran.  We'll see how far they will have
> come within five years from now.

I've been a professional developer for about 8 years now.  I've been a hobbyist
for several years more.  Out of all the problems I find myself in, figuring out
the C language is not it.  Not that I don't have bugs in my code from time to
time, but not due to any misunderstanding of the language.  If anything, I
wrestle more with inconsistent APIs (re: Linux kernel changing from each point
release...) than anything else.

You're problem seems to be there is the way you want things to be, and the way
things are, and you just can't get here from there.  As another poster pointed
out, avoid C.

> Right.  Reals, integers, rational and natural numbers.  It's all the
> same thing, really.  And because it's all the same thing, really, there
> are actually _separate_ clauses for floating point and integer
> conversion cases in the C++ standard.  I am with you so far.

The same rules of type promotion and conversion apply w.r.t. order at least.  

char = float / int;

Will result in the int being promoted to float, the floating point division
occurring, and the result being stored as an integral type in the char.

That is no different than

char = long / short;

or 

int = -(unsigned);

> Define ``similar''.  And, please, prove it with pointers to the
> standards.

Do.  Your.  Own.  Homework.

> Since I have only access to various GCC incarnations nowadays,
> it's impossible for me to (dis-)prove that point.  Can you ?

In my spare time I wrote libraries which were used on everything from UNIX boxes
like HPUX, IRIX, etc, to every type of Linux and BSD, to MS-DOS, to windows, and
to all sorts of proprietary embedded systems.  Nowhere.  Not even remotely close
to anything like this has crept up in my code.  Not to say there haven't been
porting issues [where I skirt non-portability and what not], but basic C syntax
has not been one of my faults in a really long time.

> > And just because you're resilient to new information
> 
> Once again: There's been no news so far.

I've shown you several counter-examples where your logic proves illogical. 
Accept it.

> There is no mess as far as my code is concerned as I adhere to the
> standards as backward/retarded/perverted the rules may be.  Any well
> trained mathematician would, however, think otherwise.  That is the
> view I was expressing and the kind of out-of-the-box thinking that
> appears to baffle you.  Can't help you with that.

Well, I'd hardly say C compares to something PARI, MAPLE, SAGE or whatever.  C is
a low level programming language meant to do bit twiddling and byte shuffling. 
If you want to script mathematical algorithms, proofs, or whatever, stick to one
of those languages.

As far as C is concerned, it has a well documented and understood [by most] way
of interpreting expressions that yields logical and consistent results.

You're just upset because your code has bugs, and you feel the need to vent.  And
instead of being the big man and admitting you're not correct, you'll just drag
this on because SOMEONE IS WRONG ON THE INTERNET AND I MUST PROVE THEM SO!!!

Get a grip.

Again, those who can write software without type errors (me, and the rest of the
gang), and people who can't (you).

Tom

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Re: Integral conversions in C/C++
@ 2008-04-21  2:14 Tom St Denis
  0 siblings, 0 replies; 5+ messages in thread
From: Tom St Denis @ 2008-04-21  2:14 UTC (permalink / raw)
  To: gcc-help

On Sun 20/04/08  3:43 PM , Christian Böhme monodhs@gmx.de sent:
> Tom St Denis wrote:
> > But it isn't.
> That's no news.  I am interested in _why_ it is not.  I have yet to
> see a plausible explanation for the original algebraic statement why
> a does not contain a _negative_ value although a's type allows it and
> the compiler is given enough freedom to allocate a temporary large
> enough and actually technically available (which is both the case)
> to hold such a result.

Because there is no such thing as a negative unsigned number.  And the promotion
applies to the operator, you do get that = is an operator just like + and /
right?  The promotions occur (in time) with respect to the order of precedence. 
If they occur out of order you end up with nastyness (like the float example I
sent earlier).

> > Numerically, that's what it does.
> This is what is done _technically_.  In pure algebraic terms and
> 
> given the original expression, a = 4294967280UL is a colossal
> screw-up which I actually expected the compiler to warn me about.

Why?  You negated an unsigned expression.  Then assigned that unsigned value to a
larger integer type.  an unsigned promotion to a signed type [regardless of size]
does NOT include a sign extension.  Never has, never will.

If you think about that, it makes sense.  For example,

unsigned char b = 255;
int a = 4;

a += b;

Should the result be 3?  Even though 255 is technically a -1 mod 256?

How is that any different than your example?

If we followed your logic, the above statement would read

1.  promote b to a signed int (-1)
2.  add to a and store

> > I ask again.  Why would that be sign extended?
> 
> You are missing the point.  The result of b * 2u is to be multiplied
> by -1 and stored in a.  How such a simple algebraic statement can
> possibly be interpreted as some sick type conversion operation was
> the point of the original post.  Bottom line: If I wanted to use
> modulo arithmetic I'd be _explicit_ about it.

Bottom line is, if you want to work with signed data, don't use unsigned types. 
This sort of bug is probably hidden most of the time from you since if you use a
32-bit int, you'll get the negative value you want (but just by mere coincidence
since they're both two's complement).

> > You're right that a conversion is applied before being
> stored, but the conversion
> > rule applies to the = operator, nothing else.
> 
> Interesting.  Can you point me to the section(s) in the standard(s)
> where temporary expressions are explicitly left out from integral
> conversions ?

They are converted, but to the type of the expression (multiple promotions may
occur).  For example:

signed char a;
short b;
int c;
long long d;

d = d + (c + (b + a));

a is an expression of type signed char.  b+a is of type short, c + ... is type
int, and so on.

The result of the expressions are promoted (converted) to the appropriate type. 
So if you had b = c = d = 0, and a = -1, then the result would be d = -1.  Now
change a to unsigned char and see what happens.  All of a sudden d is equal to
255.  Amazing!  Apparently if you add 255 to a larger data type it doesn't get
interpreted as negative!

> > I have no idea what you're talking about.
> 
> You will in a couple of years.

All those having problem understanding the C [and C++] standards raise your hand
(hint: this is embarrassing, but please raise your hand!!!).

> That's a completely different problem dealing with _floating point_
> conversions (C++ 4.8) and irrelevant to the one I was describing.

Actually, it's the exact same thing.  The rules for promoting/converting
regarding the = operator don't change just because you're using a float or heck
even a structure!!!

> It appears that you did not read the original post thoroughly as
> that contained explicit references to the C++ standard.

C++ and C are similar in regards to promoting data types for expressions.

> > This is also totally off-topic for this list, as you're
> not
> > discussing a GCC bug.
> 
> This is gcc-help and not gcc-bugs.

And this has nothing specifically to do with GCC.  Any conforming C or C++
compiler will generate the same damn output.

And just because you're resilient to new information doesn't mean your complaints
or observations are any more correct then when you first started posting.  If you
used the language properly in the first place, you wouldn't be in this mess.  

And while I'm on a rant, I should point out that type promotions is a very BASIC
element of both the C and C++ languages, and any competent software developer
should be familiar with them like they're 2nd nature.  It'd be like the pianist
having to look up where the middle C is every time they played... can't work that
way.

Tom

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Re: Integral conversions in C/C++
@ 2008-04-20 18:27 Tom St Denis
  0 siblings, 0 replies; 5+ messages in thread
From: Tom St Denis @ 2008-04-20 18:27 UTC (permalink / raw)
  To: gcc-help



On Sun 20/04/08 11:55 AM , Christian Böhme monodhs@gmx.de sent:
> Tom St Denis wrote:
> 
> > What type is the expression -(b * 2u)?
> 
> It _should_ be that of the destination type of the whole expression.
> 
> and thus integral conversion of an rvalue applied.

But it isn't.  The process works like this, you end up with 2^32 - 16, which is a
positive unsigned number.  This then gets zero extended (since it's unsigned) and
stored in a 64-bit integer.

How is what you wrote any different from 

a = 4294967280UL;

> 2^32 - 16 is not what -(b * 2u) expresses.  The result of x = b * 2u

Numerically, that's what it does.  you took 8, doubled it, then negated it as a
32-bit two's complement.  Then you took that unsigned expression and stored it in
a 64-bit type.

So effectively, you are doing 

a = 4294967280UL;

I ask again.  Why would that be sign extended?

You're right that a conversion is applied before being stored, but the conversion
rule applies to the = operator, nothing else.  So you have an expression who's
result is converted to a signed 64-bit int.  And the rule states that from a
32-bit unsigned value that it will be zero extended.  Otherwise, the above
statement ends with -16 in a which is not what you're doing.

> > Why does this work when your example does not?
> 
> For once, it does not give exact sizes for the objects in use.
> 
> Secondly, the type cast is acually redundant to the original version.
> 
> Finally, the example may work for b = 8 but not for b = 0xffffffff
> 
> and hence is _wrong_ with b being a _variable_ whose value no
> 
> compiler can ever predict an therefore _should_ use a temporary
> 
> with an appropriate type which, again, is the point of the
> 
> whole discussion.
> 

I have no idea what you're talking about.

b = 0xFFFFFFFF * 2 would get you 0xFFFFFFFE which btw is congruent to -2 mod
2^32.  However, if you do 

long long a = 0xFFFFFFFEUL;

a == -2 will be false, since it's not sign extended.

Using your logic ...

float a = 3;
int b;

b = a / 1.5;

What is the value that should be in b?  Well if we convert first we get 

b = 3 / 1 [or 2]

so b is either 3 or 1.  How is that advantageous?  Instead, what ACTUALLY happens
is the division occurs first, resulting in a float of 2, then that is converted
before being stored in b.  

I suggest you read either the C standard or at least the K&R C book and learn
about conversion rules, variable promotions, etc...

In short, if you want signed arithmetic and expressions, use signed variables [or
cast them to a signed type].  Negating an unsigned variable or expression is not
usually a good idea [or particularly useful].  

This is also totally off-topic for this list, as you're not discussing a GCC bug.  

Tom

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2008-04-22 22:14 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-04-23  1:34 Re: Integral conversions in C/C++ Tom St Denis
  -- strict thread matches above, loose matches on Subject: below --
2008-04-23  8:42 Tom St Denis
2008-04-21  9:29 Tom St Denis
2008-04-21  2:14 Tom St Denis
2008-04-20 18:27 Tom St Denis

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).