6.1.0270 0= zero-equals CORE

( x -- flag )

flag is true if and only if x is equal to zero.

Testing:

T{        0 0= -> <TRUE>  }T
T{        1 0= -> <FALSE> }T
T{        2 0= -> <FALSE> }T
T{       -1 0= -> <FALSE> }T
T{ MAX-UINT 0= -> <FALSE> }T
T{ MIN-INT  0= -> <FALSE> }T
T{ MAX-INT  0= -> <FALSE> }T

ContributeContributions

EricBlakeavatar of EricBlake [409] Hardware zero vs all bits zeroRequest for clarification2025-08-25 13:24:21

"is equal to zero" is unambiguous for an implementation with twos-complement arithmetic (the only bit value equal to zero is all bits zero); but for ones complement or sign-magnitude, the sentence is ambiguous thanks to hardware negative zero. A better wording would be "if all bits in the cell are equal to zero", to ensure that no implementation returns a true flag because the contents of x happen to be equal to hardware negative zero.

The standard is already clear at 3.2.1.1 that "Arithmetic zero is represented as the value of a single cell with all bits clear."? The non-normative text in A.3.2.1 adds "Per 3.2.1.1 Internal number representation and 6.1.0270 0=, the implementor must ensure that no standard or supported word return negative zero for any numeric (non-Boolean or flag) result. Many existing programmer assumptions will be violated otherwise." So in practice, that means no combination of a b + or a b * should result in a negative zero hardware output (at least absent any other trigger of ambiguous behavior, such as arithmetic overflow), and any word with a stack effect producing n or u can be assumed to have produced that value via arithmetic means, and so will not have left a negative zero on the stack. But there are non-numeric words, such as AND and OR, which operate on the bits of x rather than on an arithmetic value of n or u; and those words can indeed be used to generate the bit pattern of negative zero. Less obvious is whether LSHIFT (which operates on and produces x) falls into this category of being able to produce a hardware negative zero without triggering ambiguous behavior.

The problem is probably less pronounced on ones complement machines (since the very value of TRUE with all bits set is the hardware encoding of negative zero; any ones complement implementation that blindly uses a hardware signed equality assembly opcode that treats 0 and -0 as equivalent would be setting up for TRUE 0= to return the surprising TRUE that the rationale warns against), it would be surprising if such hardware did not also provide an opcode for unsigned zero equality (which should only admit the all bits zero case). But even sign-magnitude systems should be careful on how 0= is implemented.

This may be a moot point going forward, since future versions of the standard will require twos complement behavior.

AntonErtlavatar of AntonErtl

I do not know of any Forth implementation on a ones-complement or sign-magnitude machine, much less of a Forth-94/2012 implementation. It's an interesting, but practically irrelevant, question if Forth-94/-2012 can be implemented on such hardware with + etc. performing ones-complement or sign-magnitude addition, and then run commonly available code.

My thinking when reading the specification is that 0= should return true for negative 0. Same for 0 =. If the authors of Forth-94 had bitwise equality in mind, they should have specified it (like they did for 0e F~; I don't know what point that specification has, however). The consequence of that would be that code that uses 0= on flags does not work as intended on ones-complement machines, as you point out. There might also be some fallout on sign-magnitude machines.

There might indeed be less fallout if 0= and 0 = produced false when there is a negative 0 on the top of stack. If there ever is someone who implements Forth on such a system, it will be interesting to get a report from them what they did about this problem and what the result was.

EricBlakeavatar of EricBlake

0 = is already required to only return TRUE for bit-for-bit equality; so it is only 0= that didn't distinguish which type of equality is meant, where there is an ambiguity on whether it is implemented as : 0= 0 = ; or as signed hardware equality. But I've seen plenty of code that assumes action DUP 0= IF or similar does the right thing when action returns a flag; declaring that such code has an environmental dependency unless it inserts the space between 0 and = seems like it would be chasing windmills.

ruvavatar of ruv

Overall, this is an interesting observation.

so it is only 0= that didn't distinguish which type of equality is meant

I think that from 3.2.1.1 and A.3.2.1 it should be concluded that:

  • any formal reference to zero without qualification means a single-cell value with all bits clear, which can be universally interpreted as arithmetic zero (positive zero or unsigned zero);
  • there is no standard way to obtain a negative zero value;

Then, 0= shall be equivalent to 0 =.

But there are non-numeric words, such as AND and OR, which operate on the bits of x rather than on an arithmetic value of n or u; and those words can indeed be used to generate the bit pattern of negative zero.

Yes, using these words a Forth-2012 compliant program can generate a value that can be interpreted as the negative zero on some plausible Forth-2012 systems, but the program cannot universally interpret this value as a specific integer number in arithmetic operations — i.e., this is ambiguous. The program can probably test the environment and use such an interpretation when it matches the system.

ruvavatar of ruv

Anton wrote:

My thinking when reading the specification is that 0= should return true for negative 0. Same for 0 =.

After the first glance, I thought so too. But a consequence of this interpretation is that TRUE 0= is ambiguous.

But the argument of 0= (and =) has type x (hence, flag is allowed as an argument), and no ambiguous conditions are specified. Then, TRUE 0= must be unambiguous and return the same result on any standard system.

Reply New Version