Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c > #105182 > unrolled thread
| Started by | Lowell Gilbert <lgusenet@be-well.ilk.org> |
|---|---|
| First post | 2017-02-28 18:33 -0500 |
| Last post | 2017-03-01 10:58 -0500 |
| Articles | 20 on this page of 22 — 9 participants |
Back to article view | Back to comp.lang.c
"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 →
| From | Lowell Gilbert <lgusenet@be-well.ilk.org> |
|---|---|
| Date | 2017-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]
| From | BartC <bc@freeuk.com> |
|---|---|
| Date | 2017-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]
| From | Keith Thompson <kst-u@mib.org> |
|---|---|
| Date | 2017-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]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2017-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]
| From | Keith Thompson <kst-u@mib.org> |
|---|---|
| Date | 2017-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]
| From | BartC <bc@freeuk.com> |
|---|---|
| Date | 2017-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]
| From | Keith Thompson <kst-u@mib.org> |
|---|---|
| Date | 2017-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]
| From | BartC <bc@freeuk.com> |
|---|---|
| Date | 2017-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]
| From | Keith Thompson <kst-u@mib.org> |
|---|---|
| Date | 2017-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]
| From | BartC <bc@freeuk.com> |
|---|---|
| Date | 2017-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]
| From | Keith Thompson <kst-u@mib.org> |
|---|---|
| Date | 2017-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]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2017-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]
| From | supercat@casperkitty.com |
|---|---|
| Date | 2017-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]
| From | jameskuyper@verizon.net |
|---|---|
| Date | 2017-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]
| From | Keith Thompson <kst-u@mib.org> |
|---|---|
| Date | 2017-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]
| From | Nick Bowler <nbowler@draconx.ca> |
|---|---|
| Date | 2017-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]
| From | Keith Thompson <kst-u@mib.org> |
|---|---|
| Date | 2017-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]
| From | Lowell Gilbert <lgusenet@be-well.ilk.org> |
|---|---|
| Date | 2017-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]
| From | Lowell Gilbert <lgusenet@be-well.ilk.org> |
|---|---|
| Date | 2017-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]
| From | Keith Thompson <kst-u@mib.org> |
|---|---|
| Date | 2017-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