Page 1 of 2
Common D< Forth bug
Posted: Mon Aug 12, 2013 12:37 am
by GARTHWILSON
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.
I redefined it in 6502 Forth as:
Code: Select all
: 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:
Code: Select all
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
;-------------------
Edited: See explanation below.
Re: Common D< Forth bug
Posted: Mon Aug 04, 2014 9:37 pm
by barrym95838
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 ...
Mike
Re: Common D< Forth bug
Posted: Mon Aug 04, 2014 9:45 pm
by GARTHWILSON
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.
Re: Common D< Forth bug
Posted: Sun Sep 14, 2014 12:46 pm
by edx
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<').
Re: Common D< Forth bug
Posted: Sun Sep 14, 2014 3:22 pm
by GARTHWILSON
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.
Re: Common D< Forth bug
Posted: Sun Sep 14, 2014 4:15 pm
by Klaus2m5
The manual can be downloaded here:
http://www.greenarraychips.com/home/doc ... PF-REF.pdf
off topic edit: The discussion wether something is a bug or a feature reminds me of Monty Python's dead parrot sketch.
"It is dead"
"No it's not. It is resting"
http://www.youtube.com/watch?v=4vuW6tQ0218
Re: Common D< Forth bug
Posted: Sun Sep 14, 2014 4:28 pm
by barrym95838
... 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.
Mike
Re: Common D< Forth bug
Posted: Mon Sep 15, 2014 9:45 am
by edx
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
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.

Re: Common D< Forth bug
Posted: Mon Sep 15, 2014 3:14 pm
by barrym95838
... 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.
Mike
Re: Common D< Forth bug
Posted: Mon Sep 15, 2014 4:00 pm
by BigEd
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.
Re: Common D< Forth bug
Posted: Tue Sep 16, 2014 7:05 am
by edx
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.
Re: Common D< Forth bug
Posted: Tue Sep 16, 2014 3:21 pm
by barrym95838
... Which behaviour is "correct"?
That's a rhetorical question, right?
... particularly when there is D< to handle the exceptional cases.
But that was Garth's original beef. D<
DIDN'T handle the exceptional cases for him, and he had to re-write the primitive.
Mike
Re: Common D< Forth bug
Posted: Tue Sep 16, 2014 6:36 pm
by barrym95838
Hey, Garth. Did you try checking the most significant half first? I haven't tested this, but I think that I might have saved you a whole byte!
Code: Select all
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
;-------------------
Mike
Re: Common D< Forth bug
Posted: Wed Sep 17, 2014 4:19 am
by White Flame
I see you fell into the same trap as the original D<.
BMI/BPL are insufficient for detecting signed less-than; you need to involve the overflow flag as well.
http://www.6502.org/tutorials/compare_beyond.html#5
Re: Common D< Forth bug
Posted: Wed Sep 17, 2014 5:19 am
by barrym95838
I see you fell into the same trap as the original D<.

Oops! ... brain hiccup. Sorry, folks.
But ... looking at the other technique at your link, involving flipping the sign bits then doing an unsigned compare:
Code: Select all
HEADER "D<", NOT_IMMEDIATE ; ( d1 d2 -- f )
DLT: PRIMITIVE
LDA 4,X ; flip sign bit of d1
EOR #$8000
STA 4,X
LDA 0,X ; flip sign bit of d2
EOR #$8000
STA 0,X
BRA DULT+2 ; exit through DULT machine code
dlt1: JMP POP3_TRUE
;-------------------
Meh, cost me a byte, unless I can be sure that it's okay to get rid of the
JMP POP3_TRUE ... never mind!
Mike