On Tuesday, July 11, 2017 at 4:47:03 AM UTC-7, Ben Bacarisse wrote:

(snip, I wrote)

(snip, and later)

*Post by Ben Bacarisse**Post by h***@gmail.com*x==y && x+1==y+1

should work, even when two different zero values compare equal.

Now I don't know if you are talking about C in general, some specific C

implementation, or trying to explain some hardware property using C-like

notation.

OK, the problem with stating bit patterns is that they have a length.

I could write 0xfffffffff if I wanted a 36 bit word of all ones, but

then it is fixed at 36 bits. ~0 is a word of all ones, of whatever

length an int is in the C system it is written in.

If you don't like C hex constants, I sometimes write them in

the IBM S/360 and successor assembler form, X'FFFFFFFFF' which seems

obvious enough for just about everyone.

*Post by Ben Bacarisse**Post by h***@gmail.com**Post by Ben Bacarisse*But you are not talking about C here I think. You are talking about

what the hardware does, but describing it in what looks like C. That is

going to get confusing.

I suppose, but since I don't know 2200 assembly language, that

wouldn't have helped. And I suspect most readers here don't,

either.

There are other options!

There are languages meant for describing hardware, verilog

and VHDL. Verilog uses operators similar to C, though with

a few changes. Two interesing ones are the & and | unary

reduction operators. &x is the (single bit) value resulting

from anding all the bits of its operand, |x from oring all

the bits. There is the continuous assignment statement,

which represents the result of a wire connecting to some

combination of logic, and with a possible delay.

*Post by Ben Bacarisse*Often you can just say it in words (maybe "+ve and -ve zero don't

compare equal on the 2200"), or you can invent a notation to describe

bit patterns, maybe using # instead of 0x and/or separating the sign bit

off to make things clearer: 1#000....000 and so on. Sure, you need to

give a quick description of what you mean, but there is likely to be

less confusion that writing, say, a C hex constant. These don't

describe bit patterns -- they denote integer values.

Well, they describe bit patterns when used with bitwise operators,

but with other operators, they are integer values.

*Post by Ben Bacarisse**Post by h***@gmail.com*The design of the 2200 is such that it doesn't generate the

negative zero bit pattern in ordinary arithmetic. I suspect

that one compiles (or assembles) -x as 0-x, such that a positive

zero results. But if you do actually ~0 then you get the

negative zero bit pattern, which as noted above, on the 2200

does not compare equal to 0.

Now the English parts are clear, but even the two little bit of C-like

notation (-x and 0-x) introduce some confusion. I don't know what is

special about -x or 0-x (rather than +x or 0+x for example) that matters

to this discussion.

Twos complement adders, adding bit patterns representing twos

complement integers, generate the same bit patterns as unsigned

adders adding the same bit patterns, representing unsigned values.

Convenient, in that people can sometimes ignore which operation

is being done. This is not true for ones complement.

For all values except zero, the negative of a ones complement

number inverts all the bits. If you invert all the bits of a

normal (positive) zero, you get a negative zero. People expect

all zeros to compare equal, so the obvious way to build a ones

complement machine is to add logic that allow both the all zeros

and all ones bit pattern to compare equal. You can see that those

using the bit patterns to represent unsigned values, or just

patterns of bits without a value (such as flags) will be surprised

at that result. If you add a value, and its ones complement

(all bits inverted) the result is naturally all bits one, or

negative zero.

It turns out, though, that there is another way to build

such hardware. If you build the logic for a ones complement

subtractor, the most obvious way generates all zero bits

(positive zero) when subtracting equal values, including

both operands as positive zero. Even more, it is easy to

arrange such that subtracting the ones complement (invert

all bits) to do addition, generates positive zero, even

when both operands (before the complement) are positive zero.

For the case of the unary negation operator, you then don't

want to just invert all bits, but instead use the subtractor

with zero for the first operand. That will generate positive

zero negating positive zero, but otherwise generate the

appropriate complement.

If you add ones complement values using an unsigned adder,

and there is a carry out from the addition, you have to

add one to the sum. This is called end-around carry.

There is a similar operation for the subtractor. If you

are doing unsigned addition or subtraction using a ones

complement adder or subtractor, you need to correct for this.

The result, for the 2200, is that it is easy to do unsigned

arithmetic with the largest value of 2*INT_MAX, but takes

a lot of extra work to get it right for the value with

all bits one.

And with 36 bits, all the values of an unsigned 32 bit integer

can be represented just fine.

*Post by Ben Bacarisse**Post by h***@gmail.com*I suppose I could write them in Fortran notation, which

doesn't have an unsigned type. Then there will be no

confusion I even had to look these up, as I never tried

NOT(0).EQ.0

will be true on some machines, but not the 2200.

But then we'd have to know what the Fortran standard says about these

operators. They might require or permit a compiler to do more than the

hardware offers, though I take it you know otherwise or you would not

have used that example.

The Fortran standard, to allow for sign magnitude or ones

complement machines, leaves that open. As with C, if you only

use values between zero and INT_MAX, everything is fine.

NOT(x) is defined to invert all the bits of x, including the

sign bit. In the case of negative values, the value is

implementation dependent, but the bit representation isn't.

There are no unsigned integer types, so the problem of

representing their values doesn't occur.

Note that Fortran also allows for radix other than two.