code portability

W

Walter Roberson

Walter Roberson wrote:
What do you mean? The additive inverse of INT_MIN is INT_MIN.

It is true that the additive inverse is not required to be distinct
from the original value, but in order for the additive inverse of
INT_MIN to be INT_MIN, then INT_MIN + INT_MIN would have to equal 0.
That is not at all guaranteed in C's operator definitions:
the C definition of addition and subtraction on the signed integral
types leaves it up to the implementation (or undefined behaviour) as
to what happens in cases of overflow or underflow.

The C operations on the unsigned integral types are strictly defined;
those on the signed integral types are not.
 
W

websnarf

Walter said:
You gave a definition for ring,

I did? Look. Pay attention:

A cat is an animal with fur, four legs and whiskers.

Is that a definition? It was my intent just to give sufficient
properties of a ring to explain why Keith's notion of what a ring is is
just not going to cut it.
[...] but there are sets that match your
definition that are NOT rings, because your definition was incomplete
even for common types of rings.
http://mathworld.wolfram.com/Ring.html

Right -- so you wanted me to paste that whole thing in here? I know
what the definition is, but as you can clearly see, its quite wordy
relative to what its actual content is. I could paste in Russell and
Whitehead's proof that 2+2=4 (although the metamath proof appears to be
much shorter) every time I cite that, but I don't think that it would
be very useful to this audience.
It is not clear to me how someone can complain about someone
else's "bizarre relationship to technical terms" and then themselves
misuse a technical term that they themself have indicated is important
to part of their discussion.

How did I misuse it?
 
W

websnarf

Walter said:
It is true that the additive inverse is not required to be distinct
from the original value, but in order for the additive inverse of
INT_MIN to be INT_MIN, then INT_MIN + INT_MIN would have to equal 0.
That is not at all guaranteed in C's operator definitions:
the C definition of addition and subtraction on the signed integral
types leaves it up to the implementation (or undefined behaviour) as
to what happens in cases of overflow or underflow.

The C operations on the unsigned integral types are strictly defined;
those on the signed integral types are not.

But you're losing context again. That the *C standard* has these
weaknesses is well understood. And I'm sure there are 1s complement
machines, or otherwise where these weaknesses are truly manifest. In
real world 2s complement machines however, you will never see any such
problem. The ring properties are there for both signed and unsigned,
just as you may have learned about 2s complement in school. This is my
point -- the standard penalizes your working assumptions, while the
*defacto* standard supports them.

So if you want portability, you have to give up on using math. For
serious environments, that means you have to actually write more
verification code, which are completely pointless on platforms that
support the defacto 2s complement standard.

(Your point also has nothing to do with INT_MIN, BTW. That anomoly
just has to do with intuitive expectations about the negation
operation, which is only a problem of interpretation. Its not an
actual mathematical problem.)
 
K

Keith Thompson

I did? Look. Pay attention:

A cat is an animal with fur, four legs and whiskers.

Is that a definition? It was my intent just to give sufficient
properties of a ring to explain why Keith's notion of what a ring is is
just not going to cut it.

Ok. Here's the context. You wrote:
| > And if you need a correctly functioning ring modulo 2**n? If you can
| > assume 2s complement then you've *got one*. Otherwise, you get to
| > construct one somehow (not sure how hard this is, I have never ever
| > been exposed to a system that didn't *ONLY* support 2s complement).

and I replied:
| It's been a while since my last abstract algebra class, but isn't a
| "ring module 2**n" simply the set of integers from 0 to 2**n-1? And
| isn't that precisely what C's *unsigned* integer types are?

I wasn't attempting to offer a *definition* of "ring" either. I was
suggesting that the specified set of integers, along with the required
set of operations, is a ring, and that a C unsigned integer type,
along with those same operations, is also a ring.

You're saying that my "notion of what a ring is is just not going to
cut it". Apparently this is based on the fact that I offered a valid
example of a ring.
 
D

Dik T. Winter

> If I can't assume ASCII, then this solution has simply been taken away
> from me. Compare this with the Lua language, which allows unordered
> specific index auto-initialization.

Eh, well, if you want to limit the usability to languages that use only
the 26 letters of the Latin alphabet...
 
D

Dik T. Winter

>
> This is ... hardly a thorough definition. You need to add
> commutativity (for +) and distribution (of * over +), in particular.

And associativity.
>
> And that is where you have missed Keith Thompson's point -- because
> even on ones' complement machines, *unsigned* integers (in C) are
> still rings. So use "unsigned"; they give you the very property
> you want. They *guarantee* it.

I can look further, but I think that in both also signed arithmetic
forms a ring (when overflow gives a properly defined result). But
of course such is not defined in C.
 
D

Dik T. Winter

> Compare this to the situation in 2s complement. Suppose its
> *difficult* to prove something on signed integers, but easy to prove it
> for unsigned. But if it turns out you can "lift" from signed to
> unsigned through casting and your theorem still makes sense, then you
> likely can just apply the proof through this mechanism.

That is very true. But C does not specify the results on overflow of
signed numbers. For a good reason, various machines handle that
differently.

Assuming you can simply apply mathematical theorems to the arithemetic
provided can be very wrong. For instance, the triangle inequality does
not hold for IEEE floating point arithmetic with rounding to nearest.
And you do not need either NaNs or Infinities or overflow to show that.
 
D

Dik T. Winter

> In article <[email protected]>,

>
> Caution: on most 2s complement machines, the *signed* integers do
> not form a ring. In cases where INT_MIN is (-INT_MAX - 1)
> (e.g., INT_MIN is -32768 for an INT_MAX of 32767) then there
> is no "additive inverse" for INT_MIN -- no element in the set
> such that INT_MIN plus the element is 0.

Yes, there is. It is INT_MIN. Provided that overflow does not bother
the processor (which we assume, otherwise it would not be closed under
either addition or multiplication). That INT_MIN is its own additive
inverse is in itself not a problem. And I think it is indeed a ring.
Just as I think that signed 1's complement numbers form a ring (again,
provided that overflow is ignored).

But Paul Hsieh is talking about a ring mod 2**n, and in that case the
numbers are inherently unsigned. In that ring -1 == 2**n - 1.

Now his question was whether x << 7 would be equal to x * 128.
And indeed, that is the case, as long as there is no overflow,
it is the case, both on 1's complement and on 2's complement machines
(at least all machines I ever did use). For unsigned arithmetic and
for signed arithmetic.

And, contrary to Paul Hsieh's experience, in my career I think that
I have used 1's complement machines about as long as I have used
2's complement machines. Both about 25 years, but there is some
overlap when I used both.
 
D

Dik T. Winter

> But you're losing context again. That the *C standard* has these
> weaknesses is well understood. And I'm sure there are 1s complement
> machines, or otherwise where these weaknesses are truly manifest. In
> real world 2s complement machines however, you will never see any such
> problem.

You will. Unless you think that the Cray-1 and successors is not a
real world machine (but they still were at the time of C89). Or
consider the Gould, also fairly popular around that time. On that
machine INT_MIN was -INT_MAX, although it was 2's complement. What
would normally be seen as INT_MIN on 2's complement machines was a
trap representation on the Gould.
> The ring properties are there for both signed and unsigned,
> just as you may have learned about 2s complement in school. This is my
> point -- the standard penalizes your working assumptions, while the
> *defacto* standard supports them.

Your de facto standard is just a subset of the working machines.
> So if you want portability, you have to give up on using math. For
> serious environments, that means you have to actually write more
> verification code, which are completely pointless on platforms that
> support the defacto 2s complement standard.

Yes, so if you omit that code you are *implicitly* doing something
non-portable.
 
C

Chris Torek

Yes, there is. It is INT_MIN. Provided that overflow does not bother
the processor (which we assume, otherwise it would not be closed under
either addition or multiplication).

Indeed, the problem is that overflow is not defined in C. It seems
to me odd for anyone to argue that 10414516 * 50120 "should" be
-2010468192 in ordinary (signed integer) arithmetic; it seems to
me more likely that it should be "caught runtime error: integer
overflow" with the default behavior being to terminate the process
(on a Unix-like system anyway). It would be better if the behavior
*were* defined as "caught at runtime" (among other things, perhaps
Yahoo finance would not show NYSE stock trading volume as a negative
number when 2147483600 + 100 becomes -2147483596 -- apparently
someone forgot to use unsigned there too). But, as we see over
and over again in computing, it tends to be more important to get
the wrong answer as fast as possible... :)
But Paul Hsieh is talking about a ring mod 2**n, and in that case the
numbers are inherently unsigned. In that ring -1 == 2**n - 1.

Yes ... so I found his later remarks (along the lines of "what if
I want negative numbers in my ring") rather puzzling. "Negative"
numbers are just positive numbers. This is one of the few places
where C gets the math right "right out of the box". Perhaps he
wants certain large positive numbers to print out, for output
purposes, as negative numbers. This is of course easy to achieve.
For instance, suppose we want our ring mod 2**32 (or whatever) to
hold positive numbers up to 17, and then use the simplest "negative
number" form for the rest of the values:

unsigned result;
...
printf("result is %s%u\n",
result > 17 ? "-" : "", result > 17 ? -result : result);

does the trick. This is slightly less convenient than simply lying
with "%d", perhaps -- the lie will work on most machines but is not
portable -- but clearly much more flexible, as in this example.
(So, I do not understand this complaint. Especially when there
are much more useful things to complain about, such as the lack
of a 2n-bit product when multiplying two n-bit numbers, or even
a full-precision (a*b/c) routine, both of which would be quite
useful in certain fields.)
 
P

Philip Potter

*something in which the content was lost in a swathe of personal attacks*

Calm down. You do not make your points any clearer by insulting others and
ignoring their arguments. While I think that *what* you were saying has some
validity [1], the way you said it was completely OTT.

You have become my first *plonk*

Philip

[1] Not that Keith Thompson has any less validity.
 
R

Richard

Chris Torek said:
Indeed, the problem is that overflow is not defined in C. It seems
to me odd for anyone to argue that 10414516 * 50120 "should" be
-2010468192 in ordinary (signed integer) arithmetic; it seems to
me more likely that it should be "caught runtime error: integer
overflow" with the default behavior being to terminate the process
(on a Unix-like system anyway). It would be better if the behavior
*were* defined as "caught at runtime" (among other things, perhaps
Yahoo finance would not show NYSE stock trading volume as a negative
number when 2147483600 + 100 becomes -2147483596 -- apparently
someone forgot to use unsigned there too). But, as we see over
and over again in computing, it tends to be more important to get
the wrong answer as fast as possible... :)

I would be surprised and irritated to have a language like C do any
runtime checks. Its why ADA was invented :)

Failing that there are loads of libraries out there which deal with
large ints, financial rounding and so forth.
 
R

Richard

Philip Potter said:
*something in which the content was lost in a swathe of personal attacks*

Calm down. You do not make your points any clearer by insulting others and
ignoring their arguments. While I think that *what* you were saying has some
validity [1], the way you said it was completely OTT.

You have become my first *plonk*

He wasnt overly rude : just somewhat miffed, as a lot of posters get,
when dealing with the typically overtly imperious tone taken by a
certain poster.
 
E

ena8t8si

Frederick said:
Ian Collins posted:



I use "int unsigned" when I want to store a positive integer.

I use "int unsigned" when I want to store an integer nonnegative.

If, on the other hand, I want to store nonnegative integers, I
use "unsigned int" or just "unsigned".
 
E

ena8t8si

Dik said:
Yes, there is. It is INT_MIN. Provided that overflow does not bother
the processor (which we assume, otherwise it would not be closed under
either addition or multiplication). That INT_MIN is its own additive
inverse is in itself not a problem. And I think it is indeed a ring.
Just as I think that signed 1's complement numbers form a ring (again,
provided that overflow is ignored).

A ones complement representation can form a ring, depending
on how overflow is handled. Which, in terms of the C standard,
makes it no different from twos complement.
But Paul Hsieh is talking about a ring mod 2**n, and in that case the
numbers are inherently unsigned. In that ring -1 == 2**n - 1.

Now his question was whether x << 7 would be equal to x * 128.
And indeed, that is the case, as long as there is no overflow,
it is the case, both on 1's complement and on 2's complement machines
(at least all machines I ever did use). For unsigned arithmetic and
for signed arithmetic.

For unsigned, x << 7 is equal to x * 128 whether or not there is
overflow.
And, contrary to Paul Hsieh's experience, in my career I think that
I have used 1's complement machines about as long as I have used
2's complement machines. Both about 25 years, but there is some
overlap when I used both.

Funny, I've read about various ones complement machines
but I don't think I've ever used one. I have used a
sign/magnitude machine though.
 
F

Frederick Gotham

Ena8t posted:
I use "int unsigned" when I want to store an integer nonnegative.

If, on the other hand, I want to store nonnegative integers, I
use "unsigned int" or just "unsigned".


Congratulations, you've discovered that the word order is at the discretion
of the programmer.

Good luck with that.

Here's some more toys to play with:

int const inline static *const Func() { ...

static inline const int* const Func() { ...
 
K

Keith Thompson

Frederick Gotham said:
Ena8t posted:



Congratulations, you've discovered that the word order is at the discretion
of the programmer.

Good luck with that.

Here's some more toys to play with:

int const inline static *const Func() { ...

static inline const int* const Func() { ...

Yes, it is. And how exactly is that useful? "int unsigned" is legal,
but all it's going to do is confuse the reader. "unsigned int" or
just "unsigned" is far more common, and won't confuse the reader.

Is there some reason you want to confuse your readers?
 
F

Frederick Gotham

Keith Thompson posted:
Yes, it is. And how exactly is that useful? "int unsigned" is legal,
but all it's going to do is confuse the reader.


People have different coding styles.

"unsigned int" or just "unsigned" is far more common, and won't confuse
the reader.

Is there some reason you want to confuse your readers?


When I have a definition (or a declaration), I put things in order of
descending importance. The most important thing comes first.

First and foremost, I specify the type.

Next, I specify things such as "long", "short".

Next, I specify signedness, e.g. "signed", "unsigned".

Next, I specify const, or volatile, or neither.

Next, I specify static, or extern, or neither.

Next, I specify inline, or not.

Next, I specify the name, which may involve asterisks, more const/volatile
qualifiers, or perhaps array bounds.

char unsigned const static inline (*const Func(void))[3]
{
char unsigned const static ch[3] = {'a','b','c'};

return &ch;
}


int main()
{
Func();
}
 
I

Ian Collins

Frederick said:
Keith Thompson posted:

Yes, it is. And how exactly is that useful? "int unsigned" is legal,
but all it's going to do is confuse the reader.



People have different coding styles.


"unsigned int" or just "unsigned" is far more common, and won't confuse
the reader.

Is there some reason you want to confuse your readers?



When I have a definition (or a declaration), I put things in order of
descending importance. The most important thing comes first.

First and foremost, I specify the type.

Next, I specify things such as "long", "short".

Next, I specify signedness, e.g. "signed", "unsigned".

Next, I specify const, or volatile, or neither.

Next, I specify static, or extern, or neither.

Next, I specify inline, or not.

Next, I specify the name, which may involve asterisks, more const/volatile
qualifiers, or perhaps array bounds.

char unsigned const static inline (*const Func(void))[3]
{
char unsigned const static ch[3] = {'a','b','c'};

return &ch;
}


int main()
{
Func();
}
I pity anyone who has to maintain your code.
 
K

Keith Thompson

Frederick Gotham said:
Keith Thompson posted:

People have different coding styles.

Yes. That doesn't mean all styles are equally good.
When I have a definition (or a declaration), I put things in order of
descending importance. The most important thing comes first.

First and foremost, I specify the type.

Next, I specify things such as "long", "short".

Next, I specify signedness, e.g. "signed", "unsigned".
[..]

Ok, so your style is internally consistent, but it's inconsistent with
perhaps 99.73% of other C programmers (I made up that number).
char unsigned const static inline (*const Func(void))[3]

Anyone seeing that almost certainly going to spend far more time
wondering why the keywords are in that order and mentally translating
it to a more traditional order than appreciating the esthetics.

As Henry Spencer wrote in "The Ten Commandments for C Programmers",
<http://www.lysator.liu.se/c/ten-commandments.html>:

... thy creativity is better used in solving problems than in
creating beautiful new impediments to understanding.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,798
Messages
2,569,649
Members
45,382
Latest member
tallzebra

Latest Threads

Top