Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c > #105182 > unrolled thread

"usual arithmetic conversions": please explain something...

Started byLowell Gilbert <lgusenet@be-well.ilk.org>
First post2017-02-28 18:33 -0500
Last post2017-03-01 10:58 -0500
Articles 20 on this page of 22 — 9 participants

Back to article view | Back to comp.lang.c


Contents

  "usual arithmetic conversions": please explain something... Lowell Gilbert <lgusenet@be-well.ilk.org> - 2017-02-28 18:33 -0500
    Re: "usual arithmetic conversions": please explain something... BartC <bc@freeuk.com> - 2017-02-28 23:45 +0000
      Re: "usual arithmetic conversions": please explain something... Keith Thompson <kst-u@mib.org> - 2017-02-28 16:59 -0800
        Re: "usual arithmetic conversions": please explain something... Ben Bacarisse <ben.usenet@bsb.me.uk> - 2017-03-01 02:20 +0000
          Re: "usual arithmetic conversions": please explain something... Keith Thompson <kst-u@mib.org> - 2017-02-28 18:51 -0800
        Re: "usual arithmetic conversions": please explain something... BartC <bc@freeuk.com> - 2017-03-01 12:04 +0000
          Re: "usual arithmetic conversions": please explain something... Keith Thompson <kst-u@mib.org> - 2017-03-01 08:25 -0800
            Re: "usual arithmetic conversions": please explain something... BartC <bc@freeuk.com> - 2017-03-01 16:47 +0000
              Re: "usual arithmetic conversions": please explain something... Keith Thompson <kst-u@mib.org> - 2017-03-01 09:43 -0800
                Re: "usual arithmetic conversions": please explain something... BartC <bc@freeuk.com> - 2017-03-01 18:00 +0000
                  Re: "usual arithmetic conversions": please explain something... Keith Thompson <kst-u@mib.org> - 2017-03-01 10:15 -0800
                  Re: "usual arithmetic conversions": please explain something... Ben Bacarisse <ben.usenet@bsb.me.uk> - 2017-03-01 19:08 +0000
            Re: "usual arithmetic conversions": please explain something... supercat@casperkitty.com - 2017-03-01 09:01 -0800
    Re: "usual arithmetic conversions": please explain something... jameskuyper@verizon.net - 2017-02-28 15:49 -0800
    Re: "usual arithmetic conversions": please explain something... Keith Thompson <kst-u@mib.org> - 2017-02-28 16:03 -0800
      Re: "usual arithmetic conversions": please explain something... Nick Bowler <nbowler@draconx.ca> - 2017-03-01 21:53 +0000
        Re: "usual arithmetic conversions": please explain something... Keith Thompson <kst-u@mib.org> - 2017-03-01 14:18 -0800
    Re: "usual arithmetic conversions": please explain something... Lowell Gilbert <lgusenet@be-well.ilk.org> - 2017-03-01 08:29 -0500
      Re: "usual arithmetic conversions": please explain something... Lowell Gilbert <lgusenet@be-well.ilk.org> - 2017-03-01 09:11 -0500
      Re: "usual arithmetic conversions": please explain something... Keith Thompson <kst-u@mib.org> - 2017-03-01 08:59 -0800
    Re: "usual arithmetic conversions": please explain something... scott@slp53.sl.home (Scott Lurndal) - 2017-03-01 14:00 +0000
      Re: "usual arithmetic conversions": please explain something... "James R. Kuyper" <jameskuyper@verizon.net> - 2017-03-01 10:58 -0500

Page 1 of 2  [1] 2  Next page →


#105182 — "usual arithmetic conversions": please explain something...

FromLowell Gilbert <lgusenet@be-well.ilk.org>
Date2017-02-28 18:33 -0500
Subject"usual arithmetic conversions": please explain something...
Message-ID<448topoppn.fsf@be-well.ilk.org>
Given the following program:

    #include <stdio.h>

    int main (void)
    {
            unsigned long a = 4000;
            signed long b = -10;
            int c;

            c = a * b;
            printf("%d\n", c);

            return 0;
    }

I am surprised that I get a negative answer. From reading the standard,
my expectation was that the multiplication would convert the signed
operand to unsigned, based on this bit of the standard:

    Otherwise, if the operand that has unsigned integer type has rank greater or
    equal to the rank of the type of the other operand, then the operand with
    signed integer type is converted to the type of the operand with unsigned
    integer type.

They've got the same rank, right?

What am I missing? [It must be something, because I don't even get
warnings on any of several compilers, with messages cranked up high.]

-- 
Lowell Gilbert, embedded/networking software engineer
	 http://be-well.ilk.org/~lowell/

[toc] | [next] | [standalone]


#105186

FromBartC <bc@freeuk.com>
Date2017-02-28 23:45 +0000
Message-ID<o951vq$ch6$2@dont-email.me>
In reply to#105182
On 28/02/2017 23:33, Lowell Gilbert wrote:
>
> Given the following program:
>
>     #include <stdio.h>
>
>     int main (void)
>     {
>             unsigned long a = 4000;
>             signed long b = -10;
>             int c;
>
>             c = a * b;
>             printf("%d\n", c);
>
>             return 0;
>     }
>
> I am surprised that I get a negative answer. From reading the standard,
> my expectation was that the multiplication would convert the signed
> operand to unsigned, based on this bit of the standard:
>
>     Otherwise, if the operand that has unsigned integer type has rank greater or
>     equal to the rank of the type of the other operand, then the operand with
>     signed integer type is converted to the type of the operand with unsigned
>     integer type.
>
> They've got the same rank, right?
>
> What am I missing? [It must be something, because I don't even get
> warnings on any of several compilers, with messages cranked up high.]

Try:

              printf("%u\n", c);


-- 
Bartc

[toc] | [prev] | [next] | [standalone]


#105195

FromKeith Thompson <kst-u@mib.org>
Date2017-02-28 16:59 -0800
Message-ID<ln1suhersb.fsf@kst-u.example.com>
In reply to#105186
BartC <bc@freeuk.com> writes:
> On 28/02/2017 23:33, Lowell Gilbert wrote:
>> Given the following program:
>>
>>     #include <stdio.h>
>>
>>     int main (void)
>>     {
>>             unsigned long a = 4000;
>>             signed long b = -10;
>>             int c;
>>
>>             c = a * b;
>>             printf("%d\n", c);
>>
>>             return 0;
>>     }
>>
>> I am surprised that I get a negative answer. From reading the standard,
>> my expectation was that the multiplication would convert the signed
>> operand to unsigned, based on this bit of the standard:
>>
>>     Otherwise, if the operand that has unsigned integer type has rank
>>     greater or equal to the rank of the type of the other operand,
>>     then the operand with signed integer type is converted to the
>>     type of the operand with unsigned integer type.
>>
>> They've got the same rank, right?
>>
>> What am I missing? [It must be something, because I don't even get
>> warnings on any of several compilers, with messages cranked up high.]
>
> Try:
>
>               printf("%u\n", c);

No, c is an int.

But do try:

    printf("%llu\n", a * b);

That will show you the actual value that's converted to int before being
stored in c.

On my system, with the obvious changes to the OP's program, I get:
    a * b = 18446744073709511616
    c = -40000
or, when I compile in 32-bit mode:
    a * b = 4294927296
    c = -40000

(Again, the conversion has undefined behavior, but these results is
likely to be typical.)

-- 
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]


#105204

FromBen Bacarisse <ben.usenet@bsb.me.uk>
Date2017-03-01 02:20 +0000
Message-ID<87fuixohzp.fsf@bsb.me.uk>
In reply to#105195
Keith Thompson <kst-u@mib.org> writes:
<snip>
>> On 28/02/2017 23:33, Lowell Gilbert wrote:
>>> Given the following program:
>>>
>>>     #include <stdio.h>
>>>
>>>     int main (void)
>>>     {
>>>             unsigned long a = 4000;
>>>             signed long b = -10;
>>>             int c;
>>>
>>>             c = a * b;
>>>             printf("%d\n", c);
>>>
>>>             return 0;
>>>     }
<snip>
> But do try:
>
>     printf("%llu\n", a * b);

A small point, but the product is of type unsigned long so %lu is the
right format.

> That will show you the actual value that's converted to int before being
> stored in c.

<snip>
-- 
Ben.

[toc] | [prev] | [next] | [standalone]


#105207

FromKeith Thompson <kst-u@mib.org>
Date2017-02-28 18:51 -0800
Message-ID<lnwpc9d814.fsf@kst-u.example.com>
In reply to#105204
Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
> Keith Thompson <kst-u@mib.org> writes:
> <snip>
>>> On 28/02/2017 23:33, Lowell Gilbert wrote:
>>>> Given the following program:
>>>>
>>>>     #include <stdio.h>
>>>>
>>>>     int main (void)
>>>>     {
>>>>             unsigned long a = 4000;
>>>>             signed long b = -10;
>>>>             int c;
>>>>
>>>>             c = a * b;
>>>>             printf("%d\n", c);
>>>>
>>>>             return 0;
>>>>     }
> <snip>
>> But do try:
>>
>>     printf("%llu\n", a * b);
>
> A small point, but the product is of type unsigned long so %lu is the
> right format.

You're right, of course.  Not sure why I added the extra "l".

>> That will show you the actual value that's converted to int before being
>> stored in c.
>
> <snip>

-- 
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]


#105231

FromBartC <bc@freeuk.com>
Date2017-03-01 12:04 +0000
Message-ID<o96d9b$p64$1@dont-email.me>
In reply to#105195
On 01/03/2017 00:59, Keith Thompson wrote:
> BartC <bc@freeuk.com> writes:
>> On 28/02/2017 23:33, Lowell Gilbert wrote:
>>> Given the following program:
>>>
>>>     #include <stdio.h>
>>>
>>>     int main (void)
>>>     {
>>>             unsigned long a = 4000;
>>>             signed long b = -10;
>>>             int c;
>>>
>>>             c = a * b;
>>>             printf("%d\n", c);
>>>
>>>             return 0;
>>>     }
>>>
>>> I am surprised that I get a negative answer. From reading the standard,
>>> my expectation was that the multiplication would convert the signed
>>> operand to unsigned, based on this bit of the standard:
>>>
>>>     Otherwise, if the operand that has unsigned integer type has rank
>>>     greater or equal to the rank of the type of the other operand,
>>>     then the operand with signed integer type is converted to the
>>>     type of the operand with unsigned integer type.
>>>
>>> They've got the same rank, right?
>>>
>>> What am I missing? [It must be something, because I don't even get
>>> warnings on any of several compilers, with messages cranked up high.]
>>
>> Try:
>>
>>               printf("%u\n", c);
>
> No, c is an int.
>
> But do try:
>
>     printf("%llu\n", a * b);


It doesn't make much difference. Trying this (on a machine where long is 
32 bits):

             unsigned long a = 4000;
             signed long b = -10;
             int c;

             c = a * b;
             printf("%d\n", c);
             printf("%u\n", c);
             printf("%X\n", c);

             printf("%d\n", a*b);
             printf("%u\n", a*b);
             printf("%X\n", a*b);

             printf("%ld\n", a*b);
             printf("%lu\n", a*b);
             printf("%lX\n", a*b);

the output is:

-40000
4294927296
FFFF63C0
-40000
4294927296
FFFF63C0
-40000
4294927296
FFFF63C0

That suggests that it's the same pattern which is the expected -40000 
when interpreted as a signed int, and the large positive value otherwise.

Changing a,b between both signed, both unsigned, unsigned/signed and 
signed/unsigned, all produce the same results.

The cases where signed and/or unsigned make a difference to the actual 
arithmetic performed can be tricky to find! I would experiment with 
operations such as ">>" and ">".

Or using 'double c' (or a cast to double) instead of int c. That might 
give more enlightening results, as then you are not imposing your own 
signedness on the answer using an implicit conversion with c=a*b, or an 
explicit one with "%lu":

   double c = a*b;
   printf("%f\n", c);

-- 
bartc

[toc] | [prev] | [next] | [standalone]


#105267

FromKeith Thompson <kst-u@mib.org>
Date2017-03-01 08:25 -0800
Message-ID<lnshmxc6bz.fsf@kst-u.example.com>
In reply to#105231
BartC <bc@freeuk.com> writes:
> On 01/03/2017 00:59, Keith Thompson wrote:
>> BartC <bc@freeuk.com> writes:
[...]
>>> Try:
>>>
>>>               printf("%u\n", c);
>>
>> No, c is an int.
>>
>> But do try:
>>
>>     printf("%llu\n", a * b);

My mistake, I meant "%lu\n", not "%llu\n".

> It doesn't make much difference. Trying this (on a machine where long is 
> 32 bits):

Sure, if you use an incorrect format you can sometimes learn
something about the value being passed to printf, or its
representation -- but what's the point?  c is of type int, and
happens to have a negative value; printing it as if it were an
unsigned int means you're lying to the compiler, and the behavior
is undefined.  Printing the value of a * b shows you the actual
value that was converted to int.

I showed how to print the actual values with their correct types.

[...]

-- 
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]


#105272

FromBartC <bc@freeuk.com>
Date2017-03-01 16:47 +0000
Message-ID<o96tr9$u0t$1@dont-email.me>
In reply to#105267
On 01/03/2017 16:25, Keith Thompson wrote:
> BartC <bc@freeuk.com> writes:
>> On 01/03/2017 00:59, Keith Thompson wrote:
>>> BartC <bc@freeuk.com> writes:
> [...]
>>>> Try:
>>>>
>>>>               printf("%u\n", c);
>>>
>>> No, c is an int.
>>>
>>> But do try:
>>>
>>>     printf("%llu\n", a * b);
>
> My mistake, I meant "%lu\n", not "%llu\n".
>
>> It doesn't make much difference. Trying this (on a machine where long is
>> 32 bits):
>
> Sure, if you use an incorrect format you can sometimes learn
> something about the value being passed to printf, or its
> representation -- but what's the point?  c is of type int, and
> happens to have a negative value; printing it as if it were an
> unsigned int means you're lying to the compiler, and the behavior
> is undefined.  Printing the value of a * b shows you the actual
> value that was converted to int.
>
> I showed how to print the actual values with their correct types.

I showed how to convert the result of a signed/signed, 
unsigned/unsigned, signed/unsigned or unsigned/signed to a float, which 
is a better idea.

Otherwise you're going always going to be forcing such a result into 
either a signed or unsigned int, so you will not know what it actually was.

In fact, the true result of the OP's calculation was 4294927296, with 
the operands being 4000 and 4294967286 (the -10).

Now, it is possible to understand better what happens when either of 
these values has to be coerced into a signed or unsigned int type.

(That's with 'long' as 32 bits. When it's 64 bits, then there are 
problems with representing the lower bits of an unsigned value which 
itself is trying to represent a small 'signed' value. So for 
experimenting, 32-bit ints and 64-bit floats are better.)

-- 
bartc

[toc] | [prev] | [next] | [standalone]


#105287

FromKeith Thompson <kst-u@mib.org>
Date2017-03-01 09:43 -0800
Message-ID<ln8toodhad.fsf@kst-u.example.com>
In reply to#105272
BartC <bc@freeuk.com> writes:
> On 01/03/2017 16:25, Keith Thompson wrote:
>> BartC <bc@freeuk.com> writes:
>>> On 01/03/2017 00:59, Keith Thompson wrote:
>>>> BartC <bc@freeuk.com> writes:
>> [...]
>>>>> Try:
>>>>>
>>>>>               printf("%u\n", c);
>>>>
>>>> No, c is an int.
>>>>
>>>> But do try:
>>>>
>>>>     printf("%llu\n", a * b);
>>
>> My mistake, I meant "%lu\n", not "%llu\n".
>>
>>> It doesn't make much difference. Trying this (on a machine where long is
>>> 32 bits):
>>
>> Sure, if you use an incorrect format you can sometimes learn
>> something about the value being passed to printf, or its
>> representation -- but what's the point?  c is of type int, and
>> happens to have a negative value; printing it as if it were an
>> unsigned int means you're lying to the compiler, and the behavior
>> is undefined.  Printing the value of a * b shows you the actual
>> value that was converted to int.
>>
>> I showed how to print the actual values with their correct types.
>
> I showed how to convert the result of a signed/signed, 
> unsigned/unsigned, signed/unsigned or unsigned/signed to a float, which 
> is a better idea.

I showed how to print the actual values without converting them.
Converting to floating-point is not helpful, and introduces
irrelevant issues.

> Otherwise you're going always going to be forcing such a result into 
> either a signed or unsigned int, so you will not know what it actually was.

No, the result is an unsigned long, which is what it actually is.

> In fact, the true result of the OP's calculation was 4294927296, with 
> the operands being 4000 and 4294967286 (the -10).

Which calculation?  There were several.

    unsigned long a = 4000;
    signed long b = -10;
    int c;

    c = a * b;

The result of the multiplication was a number of type unsigned long.
The result of the implicit conversion was a different number of type
int.  The OP's original code combined those two operations; mine split
them up so you could see the intermediate result.

[...]

-- 
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]


#105290

FromBartC <bc@freeuk.com>
Date2017-03-01 18:00 +0000
Message-ID<o9723r$ema$1@dont-email.me>
In reply to#105287
On 01/03/2017 17:43, Keith Thompson wrote:
> BartC <bc@freeuk.com> writes:

>> I showed how to convert the result of a signed/signed,
>> unsigned/unsigned, signed/unsigned or unsigned/signed to a float, which
>> is a better idea.
>
> I showed how to print the actual values without converting them.

Where did you do that?

I found it difficult to establish whether a*b was done in signed or 
unsigned mode, without examining the result.

And examining the result usually means stipulating whether it should be 
shown as signed or unsigned!

Showing it as float, means that you can at least pick up signed negative 
values, and deduce that the result was signed and therefore the 
operation. (Signed positive values can't be distinguished from unsigned 
ones, except the latter has a slightly bigger range.)

> Converting to floating-point is not helpful

Disagree. I should have done it long ago when such issues came up.

>> Otherwise you're going always going to be forcing such a result into
>> either a signed or unsigned int, so you will not know what it actually was.
>
> No, the result is an unsigned long, which is what it actually is.

Again, how do you know that? By sweating over the minutia in the 
Standard? That's no way to do things if you're just learning; suppose 
you get it wrong?

>> In fact, the true result of the OP's calculation was 4294927296, with
>> the operands being 4000 and 4294967286 (the -10).
>
> Which calculation?  There were several.
>
>     unsigned long a = 4000;
>     signed long b = -10;
>     int c;
>
>     c = a * b;

a*b in this example.

> The result of the multiplication was a number of type unsigned long.
> The result of the implicit conversion was a different number of type
> int.  The OP's original code combined those two operations; mine split
> them up so you could see the intermediate result.

And printed it using %lu? Obviously, the output is always going to be an 
unsigned value, so proves nothing.

-- 
Bartc

[toc] | [prev] | [next] | [standalone]


#105294

FromKeith Thompson <kst-u@mib.org>
Date2017-03-01 10:15 -0800
Message-ID<ln4lzcdfst.fsf@kst-u.example.com>
In reply to#105290
BartC <bc@freeuk.com> writes:
> On 01/03/2017 17:43, Keith Thompson wrote:
>> BartC <bc@freeuk.com> writes:
>
>>> I showed how to convert the result of a signed/signed,
>>> unsigned/unsigned, signed/unsigned or unsigned/signed to a float, which
>>> is a better idea.
>>
>> I showed how to print the actual values without converting them.
>
> Where did you do that?

    printf("%lu\n", a * b);

(I originally wrote "%llu\n", which was a mistake, one that my compiler
would have warned me about if I had compiled that line of code.)

> I found it difficult to establish whether a*b was done in signed or 
> unsigned mode, without examining the result.

The rules are in N1570 6.3.1.8, "Usual arithmetic conversions":

    Otherwise, if the operand that has unsigned integer type has rank
    greater or equal to the rank of the type of the other operand, then
    the operand with signed integer type is converted to the type of the
    operand with unsigned integer type.

The rules are complicated, but you need to understand them if you're
going to reason about arithmetic on operands of different types.

I suppose I can see the point of converting to a type like long double
whose range covers the expected range of values, such as long double --
but that's necessary only if you're unwilling to figure out what the
actual type is.

Here's a program that uses both your method and mine:

#include <stdio.h>
int main (void) {
    unsigned long a = 4000;
    signed long b = -10;
    int c;

    c = a * b;
    long double ld = a * b;
    printf("a * b = %lu\n", a * b);
    printf("ld    = %Lf\n", ld);
    printf("c     = %d\n", c);
}

and its output on my system (in 64-bit mode);

a * b = 18446744073709511616
ld    = 18446744073709511616.000000
c     = -40000

> And examining the result usually means stipulating whether it should be 
> shown as signed or unsigned!

The standard already stipulates that the result is unsigned long.

[...]

> Again, how do you know that? By sweating over the minutia in the 
> Standard? That's no way to do things if you're just learning; suppose 
> you get it wrong?

If you get it wrong, you get it wrong.  It happens.  Some compilers will
warn you if you print a value with the wrong format for its type.  (gcc,
at least by default, doesn't warn about using "%ld" with an unsigned
long argument.)

[...]

-- 
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]


#105300

FromBen Bacarisse <ben.usenet@bsb.me.uk>
Date2017-03-01 19:08 +0000
Message-ID<87r32gn7c4.fsf@bsb.me.uk>
In reply to#105290
BartC <bc@freeuk.com> writes:

> I found it difficult to establish whether a*b was done in signed or
> unsigned mode, without examining the result.
>
> And examining the result usually means stipulating whether it should
> be shown as signed or unsigned!

You can get C to tell you, though it's only useful while learning.  It's
the kind of thing you need to know about C:

  #include <stdio.h>

  int main(void)
  {
       unsigned long a = 4000;
       signed long b = -10;
       puts(_Generic(a * b,
                     unsigned long: "unsigned long",
                     default: "something else"));
  }

<snip>
-- 
Ben.

[toc] | [prev] | [next] | [standalone]


#105281

Fromsupercat@casperkitty.com
Date2017-03-01 09:01 -0800
Message-ID<c35ea81a-2cd4-41fc-9414-fba4bf789051@googlegroups.com>
In reply to#105267
On Wednesday, March 1, 2017 at 10:24:06 AM UTC-6, Keith Thompson wrote:
> Sure, if you use an incorrect format you can sometimes learn
> something about the value being passed to printf, or its
> representation -- but what's the point?  c is of type int, and
> happens to have a negative value; printing it as if it were an
> unsigned int means you're lying to the compiler, and the behavior
> is undefined.  Printing the value of a * b shows you the actual
> value that was converted to int.

The Standard explicitly specifies that an "unsigned*" may be used to access
an "int", and vice versa.  It also specifies that "unsigned" and "int" have
compatible representations for all positive values.  That suggests an
expectation that they be processed rather similarly.  Further, the rationale
for having short unsigned types promote to signed int makes no mention of
the possibility that it might break any code which outputs values of type
"unsigned char" in "%02X" format without first casting to "unsigned".

Even if the authors of the Standard would not have forbidden implementations
from doing something weird in the aforementioned case, their failure to even
mention such a possibility when discussing promotion rules suggests that
they would have expected programmers on normal implementations to give it
more consideration than the Committee had when considering the place where
such behavior could have the biggest impact.

[toc] | [prev] | [next] | [standalone]


#105187

Fromjameskuyper@verizon.net
Date2017-02-28 15:49 -0800
Message-ID<a915cf8e-517d-49b6-bb23-96a1aab47566@googlegroups.com>
In reply to#105182
On Tuesday, February 28, 2017 at 6:33:54 PM UTC-5, Lowell Gilbert wrote:
> Given the following program:
> 
>     #include <stdio.h>
> 
>     int main (void)
>     {
>             unsigned long a = 4000;
>             signed long b = -10;
>             int c;
> 
>             c = a * b;
>             printf("%d\n", c);
> 
>             return 0;
>     }
> 
> I am surprised that I get a negative answer. From reading the standard,
> my expectation was that the multiplication would convert the signed
> operand to unsigned, based on this bit of the standard:
> 
>     Otherwise, if the operand that has unsigned integer type has rank greater or
>     equal to the rank of the type of the other operand, then the operand with
>     signed integer type is converted to the type of the operand with unsigned
>     integer type.

Correct. So in this case, a value of -10 gets converted to unsigned long. The result of that conversion will be ULONG_MAX-9. Multiplying by 4000, you get 4000*ULONG_MAX - 4000*9, but that number is too big to be represented by an unsigned long. Therefore, you need to subtract 3999*(ULONG_MAX+1) in order to get the actual value: ULONG_MAX-36000-3999 == ULONG_MAX-39999.

The minimum permissible value for ULONG_MAX is 4294967295. Therefore, the result of your multiplication is at least 4294927296.

The result of that multiplication must then be converted to an int before being stored in c. Whether or not that conversion has a defined value depends upon whether or not ULONG_MAX-39999 <= INT_MAX. Since ULONG_MAX and INT_MAX are both implementation-defined, you'll have to check the actual values on your system. However, if you got a negative result, it implies that the number is too big to be represented as an 'int'.

[toc] | [prev] | [next] | [standalone]


#105190

FromKeith Thompson <kst-u@mib.org>
Date2017-02-28 16:03 -0800
Message-ID<ln8topeuc6.fsf@kst-u.example.com>
In reply to#105182
Lowell Gilbert <lgusenet@be-well.ilk.org> writes:
> Given the following program:
>
>     #include <stdio.h>
>
>     int main (void)
>     {
>             unsigned long a = 4000;
>             signed long b = -10;
>             int c;
>
>             c = a * b;
>             printf("%d\n", c);
>
>             return 0;
>     }
>
> I am surprised that I get a negative answer.
[...]

Multiplying an unsigned long by a signed long converts the signed long
operand to unsigned long.  Converting -10 to unsigned long yields
ULONG_MAX-9, which when multiplied by 4000 yields some large number that
wraps around in the usual manner.  On my system, with 64-bit long, the
result is 18446744073709511616; with 32-bit long, it's 4294927296.

The assignment converts that value from unsigned long to (signed) int.
The behavior is undefined (assuming that value exceeds INT_MAX, which it
almost certainly does, but *typically* it will discard the high-order
bits yielding -40000.

-- 
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]


#105309

FromNick Bowler <nbowler@draconx.ca>
Date2017-03-01 21:53 +0000
Message-ID<o97fs5$ai$1@dont-email.me>
In reply to#105190
On Tue, 28 Feb 2017 16:03:53 -0800, Keith Thompson wrote:
> The assignment converts that value from unsigned long to (signed) int.
> The behavior is undefined (assuming that value exceeds INT_MAX, which it
> almost certainly does, but *typically* it will discard the high-order
> bits yielding -40000.

Nit: the behaviour of converting out-of-range values to signed integer
types is not, strictly speaking, undefined.  See C11§6.3.1.3p3:

  "[if] the new type is signed and the value cannot be represented in
   it, either the result is implementation-defined or an implementation-
   defined signal is raised.

[toc] | [prev] | [next] | [standalone]


#105311

FromKeith Thompson <kst-u@mib.org>
Date2017-03-01 14:18 -0800
Message-ID<lnshmwbpzk.fsf@kst-u.example.com>
In reply to#105309
Nick Bowler <nbowler@draconx.ca> writes:
> On Tue, 28 Feb 2017 16:03:53 -0800, Keith Thompson wrote:
>> The assignment converts that value from unsigned long to (signed) int.
>> The behavior is undefined (assuming that value exceeds INT_MAX, which it
>> almost certainly does, but *typically* it will discard the high-order
>> bits yielding -40000.
>
> Nit: the behaviour of converting out-of-range values to signed integer
> types is not, strictly speaking, undefined.  See C11§6.3.1.3p3:
>
>   "[if] the new type is signed and the value cannot be represented in
>    it, either the result is implementation-defined or an implementation-
>    defined signal is raised.

Quite right.  I apologize for the misinformation.  Apparently there was
too much blood in my caffeine system.

If an arithmetic operation yielding a signed value overflows (such as
INT_MAX + 1), the behavior is undefined.

An overflowing conversion from a signed or unsigned integer type to a
signed integer type, as you say, yields an implementation-defined value
or raises an implementation-defined signal.  (Digression: The latter
permission was added in C99.  IMHO it made it more difficult to write
portable code, and I'm not aware of any implementation that takes
advantage of it.)

An overflowing conversion from a floating-point type to a signed or
unsigned integer type (other than _Bool) has undefined behavior.

-- 
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]


#105239

FromLowell Gilbert <lgusenet@be-well.ilk.org>
Date2017-03-01 08:29 -0500
Message-ID<44o9xlcegy.fsf@be-well.ilk.org>
In reply to#105182
Lowell Gilbert <lgusenet@be-well.ilk.org> writes:

> What am I missing? [It must be something, because I don't even get
> warnings on any of several compilers, with messages cranked up high.]

Okay, thanks all. It seems obvious now that it's pointed out.

I expected to find an option to warn on such things...

-- 
Lowell Gilbert, embedded/networking software engineer
	 http://be-well.ilk.org/~lowell/

[toc] | [prev] | [next] | [standalone]


#105248

FromLowell Gilbert <lgusenet@be-well.ilk.org>
Date2017-03-01 09:11 -0500
Message-ID<44zih5axyr.fsf@be-well.ilk.org>
In reply to#105239
ram@zedat.fu-berlin.de (Stefan Ram) writes:

> -pedantic -pedantic-errors -Werror=narrowing -Wall -W -Wconversion -Wextra
>
>   , which are my defaults. It might be possible that even with
>   »-Wall -W -Wextra«, one still needs »-Wconversion«.

Yep. I think I'll go add it.

-- 
Lowell Gilbert, embedded/networking software engineer
	 http://be-well.ilk.org/~lowell/

[toc] | [prev] | [next] | [standalone]


#105279

FromKeith Thompson <kst-u@mib.org>
Date2017-03-01 08:59 -0800
Message-ID<lno9xlc4rh.fsf@kst-u.example.com>
In reply to#105239
ram@zedat.fu-berlin.de (Stefan Ram) writes:
[...]
>   on gcc with
>
> -pedantic -pedantic-errors -Werror=narrowing -Wall -W -Wconversion -Wextra
>
>   , which are my defaults. It might be possible that even with
>   »-Wall -W -Wextra«, one still needs »-Wconversion«.
>
>   BTW, I then also have
>
> -Wno-parentheses -Wno-unused-parameter -Wno-unused-variable
> -Wno-empty-body -Wno-unused-but-set

-W is the same as -Wextra.  From the gcc manual:

    '-Wextra'
        This enables some extra warning flags that are not enabled by
        '-Wall'.  (This option used to be called '-W'.  The older name
        is still supported, but the newer name is more descriptive.)

-- 
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]


Page 1 of 2  [1] 2  Next page →

Back to top | Article view | comp.lang.c


csiph-web