In the Forth 83 topic, I mentioned a bug in D< that I think is in the in the public-domain material (but if not, it's certainly nothing to defend if it is wrong!), but apparently I never discussed it. D< gets defined as:
which looks quite innocent, and the D- and D0< do work right, but the algorithm doesn't take into account the underflow problems that occur when D1 minus D2 has a result that is so negative that it again becomes positive.
My first encounter with the problem occurred with the numbers -70007FFF and FFF8002. D< said the negative number was not less than the positive one, because -70007FFF (8FFF8001) minus FFF8002 gives 7FFFFFFF, which is positive. However if we add 1 to the first number making it -70007FFE, the D- gives an answer of 80000000, and D< correctly says that yes, the first number is indeed less than the second.
: D< ROT
2DUP =
IF 2DROP U< EXIT THEN
> -ROT 2DROP ;
In my '816 Forth, I defined it as a primitive instead, because although D< is not used that much, the fact that it was not only faster as a primitive but even shorter than the secondary made it doubly worth it:
HEADER "D<", NOT_IMMEDIATE ; ( d1 d2 -- f )
DLT: PRIMITIVE
LDA 6,X
CMP 2,X
LDA 4,X
SBC 0,X
BVC dlt1 ; If the resulting sign is wrong,
EOR #$8000 ; then invert it.
dlt1: BPL dult1 ; Now if N is set, d2 < d1.
JMP POP3_TRUE
;-------------------
There's something wrong with your second code sample; it looks like the BPL instruction should branch somewhere other than itself! Sorry that it took me nearly a year to notice ...
You're right. Thanks for catching that. It's on my DOS computer w/o internet or other networking, and I probably just copied it by hand instead of copying first to SD card and then over, which unfortunately requires resetting the DOS machine because the SD card is on an IDE converter. (Later it got backed up on this computer anyway, with the SD card.) I'll fix it above. I left out a letter. It should say BPL dult1, not dlt1. dult1 is at the end of DU<, and is a jump to POP3_FALSE.
In the Forth 83 topic, I mentioned a bug in D< that I think is in the in the public-domain material (but if not, it's certainly nothing to defend if it is wrong!), but apparently I never discussed it...
Most early Forths defined '<' and 'D<' in the same manner. It wasn't a bug but a "feature" - the characteristics of which are discussed in the polyForth manual (DB005-120825-PF-REF.pdf). By the time the Forth-79 Standard came out it required '<' operate over the full 16-bit signed range (though it's not clear whether similar provisions applied to 'D<').
It wasn't a bug but a "feature" - the characteristics of which are discussed in the polyForth manual (DB005-120825-PF-REF.pdf).
Do you have a URL for it? Any way you look at it, for D< to say that -70007FFF was not less than 0FFF8002 in signed 32-bit numbers is just plain wrong.
... It wasn't a bug but a "feature" - the characteristics of which are discussed in the polyForth manual (DB005-120825-PF-REF.pdf).
I'm a bit fuzzy this morning (haven't gone to the gym yet), but a simple-minded search of the document yields no "discussion" of D< ... just a brief one-line definition.
I'm a bit fuzzy this morning (haven't gone to the gym yet), but a simple-minded search of the document yields no "discussion" of D< ... just a brief one-line definition.
See section 2.2.2
GARTHWILSON wrote:
Any way you look at it, for D< to say that -70007FFF was not less than 0FFF8002 in signed 32-bit numbers is just plain wrong.
Clearly that wasn't the way C.Moore and Forth Inc looked at it.
If there be a mistake it lies in expecting modern behaviours from 35+ year software and being caught out by it.
... If there be a mistake it lies in expecting modern behaviours from 35+ year software and being caught out by it.
To me, it's not a matter of modern vs. vintage, but more of a matter of correct vs. incorrect. The 6502's overflow flag worked correctly for signed binary from its inception ... it was up to the programmer to make proper use of it. In the case of Forth, I believe that access to the overflow flag was limited for application programs, so that duty should have fallen on the person writing the primitives.
The discussion in Section 2.2.2 makes clever use of the phrases "handles the vast majority" and "is generally safe", so I guess it boiled down to a matter of efficiency vs. exhaustive correctness. I have little doubt that a non-zero percentage of modern software hides similar trade-offs, documented or not.
That clock view of binary arithmetic reminded me - rightly or wrongly - of the timer arithmetic used by Occam for the Transputer. The chip has built in timers, and you want to be able to calculate time intervals regardless of the actual value of the time. You need a circular model.
To me, it's not a matter of modern vs. vintage, but more of a matter of correct vs. incorrect. ... In the case of Forth, I believe that access to the overflow flag was limited for application programs, so that duty should have fallen on the person writing the primitives.
In Borland Pascal I can mistakenly enter a literal number which exceeds the range of an int and the compiler will detect it. Not so with Borland C which will happily accept the number and produce nonsense leaving me to find the error myself. Which behaviour is "correct"?
IMO it depends on the language and the extent to which it's prepared to protect users from themselves. Could I live with the signed relationals of Fig-Forth and PolyForth? Possibly. I've never had to because my Forth follows later Standards. For all I know it may be as easy as Forth Inc claim - particularly when there is D< to handle the exceptional cases.
HEADER "D<", NOT_IMMEDIATE ; ( d1 d2 -- f )
DLT: PRIMITIVE
LDA 4,X ; compare high words first
CMP 0,X
BMI dlt1
BNE dult1 ; ( POP3_FALSE )
LDA 6,X ; only need to compare low words
CMP 2,X ; if the high words were equal
BPL dult1
dlt1: JMP POP3_TRUE
;-------------------