mhx wrote:
On Sun, 6 Oct 2024 7:51:31 +0000, dxf wrote:
Is there an easier way of doing this? End goal is a double number representing centi-secs.
empty decimal
: SPLIT ( a u c -- a2 u2 a3 u3 ) >r 2dup r> scan 2swap 2 pick - ;
: >INT ( adr len -- u ) 0 0 2swap >number 2drop drop ;
: /T ( a u -- $hour $min $sec )
2 0 do [char] : split 2swap dup if 1 /string then loop
2 0 do dup 0= if 2rot 2rot then loop ;
: .T 2swap 2rot cr >int . ." hr " >int . ." min " >int . ." sec " ;
s" 1:2:3" /t .t
s" 02:03" /t .t
s" 03" /t .t
s" 23:59:59" /t .t
s" 0:00:03" /t .t
Why don't you use the fact that >NUMBER returns the given
string starting with the first unconverted character?
SPLIT should be redundant.
-marcel
: CHAR-NUMERIC? 48 58 WITHIN ;
: SKIP-NON-NUMERIC ( adr u -- adr2 u2)
BEGIN
DUP IF OVER C@ CHAR-NUMERIC? NOT ELSE 0 THEN
WHILE
1 /STRING
REPEAT ;
: SCAN-NEXT-NUMBER ( n adr len -- n2 adr2 len2)
2>R 60 * 0. 2R> >NUMBER
2>R D>S + 2R> ;
: PARSE-TIME ( adr len -- seconds)
0 -ROT
BEGIN
SKIP-NON-NUMERIC
DUP
WHILE
SCAN-NEXT-NUMBER
REPEAT
2DROP ;
S" hello 1::36 world" PARSE-TIME CR .
96 ok
On 8/06/2025 3:07 am, LIT wrote:
My solution is rather straightforward:
1 VARIABLE C6
1 VARIABLE C1
: TIMESTRSCAN ( addr count -- d )
t;R >R 0 0 R> R>OVER + 1-
DO
I C@ DUP 58 =
IF
DROP
C6 @ 60 * C6 !
1 C1 !
ELSE
48 - C1 @ * C6 @ M* D+
10 C1 !
THEN
-1 +LOOP
1 C6 ! 1 C1 !
;
Virtue is its own reward.
This does the same thing:
This does the same thing:
Indeed - "this does the same thing"
introducing plenty of stack noise.
That's why I preferred to use two
variables.
--
Mr. Fifo - self-proclaimed "Mark Twain of Forth"
- has no idea, that writing Forth code doesn't
mean to move bytes around "Back and Forth"
(where did I see that? Let's see... :D ).
Stack jugglery means wasting CPU cycles for
moving the bytes around - it's contrproductive.
Variables have been invented to be used. They're
useful, if you didn't notice, or if they didn't
tell you that in your college, or wherever.
...
Stack jugglery means wasting CPU cycles for
moving the bytes around - it's contrproductive.
Variables have been invented to be used. They're
useful, if you didn't notice, or if they didn't
tell you that in your college, or wherever.
OTOH some implementations
are just neater and its a matter of finding them!
2>r 0 0 0 2r> begin[..]
/int 5 -roll rot drop dup while [char] : ?skip
repeat 2drop ;
On Tue, 10 Jun 2025 2:31:51 +0000, dxf wrote:
[..]
OTOH some implementations
are just neater and its a matter of finding them!
[..]
2>r 0 0 0 2r> begin[..]
/int 5 -roll rot drop dup while [char] : ?skip
repeat 2drop ;
I don't get if you are joking or not.
mhx wrote:
On Sun, 6 Oct 2024 7:51:31 +0000, dxf wrote:
Is there an easier way of doing this? End goal is a double number representing centi-secs.
empty decimal
: SPLIT ( a u c -- a2 u2 a3 u3 ) >r 2dup r> scan 2swap 2 pick - ;
: >INT ( adr len -- u ) 0 0 2swap >number 2drop drop ;
: /T ( a u -- $hour $min $sec )
2 0 do [char] : split 2swap dup if 1 /string then loop
2 0 do dup 0= if 2rot 2rot then loop ;
: .T 2swap 2rot cr >int . ." hr " >int . ." min " >int . ." sec " ;
s" 1:2:3" /t .t
s" 02:03" /t .t
s" 03" /t .t
s" 23:59:59" /t .t
s" 0:00:03" /t .t
Why don't you use the fact that >NUMBER returns the given
string starting with the first unconverted character?
SPLIT should be redundant.
-marcel
: CHAR-NUMERIC? 48 58 WITHIN ;
: SKIP-NON-NUMERIC ( adr u -- adr2 u2)
BEGIN
DUP IF OVER C@ CHAR-NUMERIC? NOT ELSE 0 THEN
WHILE
1 /STRING
REPEAT ;
: SCAN-NEXT-NUMBER ( n adr len -- n2 adr2 len2)
2>R 60 * 0. 2R> >NUMBER
2>R D>S + 2R> ;
: PARSE-TIME ( adr len -- seconds)
0 -ROT
BEGIN
SKIP-NON-NUMERIC
DUP
WHILE
SCAN-NEXT-NUMBER
REPEAT
2DROP ;
S" hello 1::36 world" PARSE-TIME CR .
96 ok
Mr. Fifo - self-proclaimed "Mark Twain of Forth"
- has no idea, that writing Forth code doesn't
mean to move bytes around "Back and Forth"
(where did I see that? Let's see... :D ).
Stack jugglery means wasting CPU cycles for
moving the bytes around - it's contrproductive.
Variables have been invented to be used. They're
useful, if you didn't notice, or if they didn't
tell you that in your college, or wherever.
First, a stack has been invented to be used as well.
And a canonical
Forth program uses the stack. Tut mir Leid.
If you want to use local variables, there are plenty
of languages that use that stuff. Like Python, the
language of champions.
Second, there are CPUs that are stack oriented - and using variables
should be wasteful in that case. There are also plenty of CPUs that have
a stack, like Z80, 8080 and friends. Sorry to say, but using ANY
instruction burns CPU cycles. There is no way around that.
Third, any statement must come with proof. And in this case that means extended benchmarking. I can tell you beforehand that I've never seen significant differences between locals and stack. I'm sorry to say that
- but it's true.
Finally, you have to know that good software comes with MANY qualities,
speed being only one of them. If I have to choose between correct and
fast, there has to be a very convincing argument for the speed
requirement. They didn't tell you that in your college? Oh dear. I'm so
sorry for you! You must have had a miserable life.
Stack jugglery means wasting CPU cycles for
moving the bytes around - it's contrproductive.
Variables have been invented to be used. They're
useful, if you didn't notice, or if they didn't
tell you that in your college, or wherever.
Forth uses variables in the global sense and this works well.
Variables at the word level is often an indication something is
wrong. Locals users rarely justify on grounds of performance as
experience over the years has shown time and again well-written
stack code is both shorter and faster.
...
: SCAN-NUMBER-OR-SKIP ( n adr len -- n' adr' len')
DUP >R
0 0 2SWAP >NUMBER
DUP R> =
IF 2SWAP 2DROP 1 /STRING
ELSE
2>R D>S SWAP 60 * + 2R>
THEN ;
: SCAN-NUMBER-OR-SKIP ( n adr len -- n' adr' len')
DUP >R
0 0 2SWAP >NUMBER
DUP R> =
IF 2SWAP 2DROP 1 /STRING
ELSE
2>R D>S SWAP 60 * + 2R>
THEN ;
0 0 2SWAP >NUMBER invariably crops up so I have it in the kernel as (NUMBER). 2SWAP 2DROP is another and becomes 2NIP. Forth would
have one define the same thing over and over. Do it once and be done
with it IMO.
Mr. Fifo - self-proclaimed "Mark Twain of Forth"
- has no idea, that writing Forth code doesn't
mean to move bytes around "Back and Forth"
(where did I see that? Let's see... :D ).
Stack jugglery means wasting CPU cycles for
moving the bytes around - it's contrproductive.
Variables have been invented to be used. They're
useful, if you didn't notice, or if they didn't
tell you that in your college, or wherever.
----
Stack jugglery means wasting CPU cycles for
moving the bytes around - it's contrproductive.
Variables have been invented to be used. They're
useful, if you didn't notice, or if they didn't
tell you that in your college, or wherever.
Have you looked at my Roman digits?
There are two stack words DUP used in whole, meaning that you
use a stack item twice.
That is in a word `_row that isn't even essential to the whole
exercise.
The stack shuffling is probably a sign of bad code.
Third, any statement must come with proof. And in this case that means extended benchmarking. I can tell you beforehand that I've never seen significant differences between locals and stack. I'm sorry to say that
- but it's true.
In article <6ea4ccd1cb6ae8c828144444fe51fea9@www.novabbs.com>,
LIT <zbigniew2011@gmail.com> wrote:
Mr. Fifo - self-proclaimed "Mark Twain of Forth"
- has no idea, that writing Forth code doesn't
mean to move bytes around "Back and Forth"
(where did I see that? Let's see... :D ).
Stack jugglery means wasting CPU cycles for
moving the bytes around - it's contrproductive.
Variables have been invented to be used. They're
useful, if you didn't notice, or if they didn't
tell you that in your college, or wherever.
Have you looked at my Roman digits?
There are two stack words DUP used in whole, meaning that you
use a stack item twice.
That is in a word `_row that isn't even essential to the whole
exercise.
The stack shuffling is probably a sign of bad code.
Stack jugglery means wasting CPU cycles for
moving the bytes around - it's contrproductive.
Variables have been invented to be used. They're
useful, if you didn't notice, or if they didn't
tell you that in your college, or wherever.
'+' does something useful. It produces a sum.
'SWAP' merely changes the order of two subsequent
bytes.
Mr. Fifo - self-proclaimed "Mark Twain of Forth"
- has no idea, that writing Forth code doesn't
mean to move bytes around "Back and Forth"
(where did I see that? Let's see... :D ).
Stack jugglery means wasting CPU cycles for
moving the bytes around - it's contrproductive.
Variables have been invented to be used. They're
useful, if you didn't notice, or if they didn't
tell you that in your college, or wherever.
Have you looked at my Roman digits?
There are two stack words DUP used in whole, meaning that you
use a stack item twice.
That is in a word `_row that isn't even essential to the whole
exercise.
The stack shuffling is probably a sign of bad code.
Well, that's exactly the point. If you can't write Forth, you'll end up juggling stuff around - and yes, that results in unmaintainable Forth.
Stack jugglery means wasting CPU cycles for
moving the bytes around - it's contrproductive.
Variables have been invented to be used. They're
useful, if you didn't notice, or if they didn't
tell you that in your college, or wherever.
'+' does something useful. It produces a sum.
'SWAP' merely changes the order of two subsequent
bytes.
Interesting. So you claim that instructions like:
LD HL, (23672)
PUSH HL
POP HL
Have no use, since they are just "moving bytes around"? Which means [..]
First, a stack has been invented to be used as well.
..which doesn't deny use of variables where it's useful.
You're sorry that you 'have' (IYO) to use the stack
not quite senseful way? They force you to do so?
Poor you.
Contact me with your "recommendation" again when you
become appointed of some kind of 'authority' who'll
decide, who should use what. Until then - I'll use
whatever language I'll decide to use at the moment.
Yes, indeed - these CPUs have a stack, and what you've
presented is pointless stack jugglery. We may 'burn CPU
cycles' to do something useful - or, following you (if
anyone would be keen, which I doubt) we could use that
stack for pointless moving the bytes "Back and Forth".
Oh, really? That speaks in favor of the most clean solution.
Yes, software comes with many qualities - and one of them,
which you didn't mention (maybe you have no clue), is
clarity of the code. The code filled with strings of that
DUPs, SWAPs and ROTs not only works inefficiently, but also
rather quickly becomes unreadable. And then serves as
another example of 'typical write-only Forth code'.
Once full native code compilation and optimisation is turned on, you
can get surprising results. At one stage we (MPE) de-localled a
substantial portion of the PowerNet TCP/IP stack - all in high-level
Forth. For the modified code, size decreased by 25% and performance
increased by 50%.
Which DOESN'T 'mean', because I DIDN'T write anywhere
that the instructions used for moving bytes are useless;
Since you aren't able to read WITH UNDERSTANDING - or
maybe you're twisting my words on purpose, which means
you're trolling - we better end such 'discussion',
because it's as pointless as creating the words that half of their
execution time just move the bytes back and forth.
Like this example:
: timescan
over swap chars + >r 0 tuck begin
over r@ <
while
over c@ [char] : =
if rot + 60 * swap 0 else 10 * over c@ [char] 0 - + then
swap char+ swap
repeat r> drop nip +
;
15 "byte shufflers" within single word,
and even 8 of them within a loop(!). So
byte threshing over and over again.
Mr. Fifo - self-proclaimed "Mark Twain of Forth"
- has no idea, that writing Forth code doesn't
mean to move bytes around "Back and Forth"
(where did I see that? Let's see... :D ).
: SCAN-NUMBER-OR-SKIP ( n adr len -- n' adr' len')
DUP >R
0 0 2SWAP >NUMBER
DUP R> =
IF 2SWAP 2DROP 1 /STRING
ELSE
2>R D>S SWAP 60 * + 2R>
THEN ;
0 0 2SWAP >NUMBER invariably crops up so I have it in the kernel as
(NUMBER). 2SWAP 2DROP is another and becomes 2NIP. Forth would
have one define the same thing over and over. Do it once and be done
with it IMO.
So now you see: one can either use variable to
make the solution cleaner - or one can create new word(s).
Either way - some new names are added to the vocabulary.
Why the use of variable, instead of new words,
should be perceived of 'inferior'? Variable is a word too.
Unlike variables, factors (subroutines) don't contradict effective stack-based programming.
Unlike variables, factors (subroutines) don't contradict effective
stack-based programming.
Variables "per se" don't contradict that neither.
So now you see: one can either use variable to
make the solution cleaner - or one can create new word(s).
Either way - some new names are added to the vocabulary.
Why the use of variable, instead of new words,
should be perceived of 'inferior'? Variable is a word too.
Unlike variables, factors (subroutines) don't contradict effective stack-based programming.
3. As Mr. Pelc remarked, stack operators are faster.
...
Fourth, if the definition is extremely time-critical, those
tricky stack manipulators, (e.g., ROT ROT) can really eat up
clock cycles. Direct access to variables is faster."
Yes, Brodie warns us next "but careful with variables' use
too" - and I still think my use of variables in two examples
I recently pasted wasn't "legit" in any way. It was just
applying the tips you see above.
When is it "legit" to give up? I've written routines I believed
needed VARIABLEs. But after a 'cooling off' period, I can look
at the problem again afresh and find I can do better. Folks will
say in the real world one couldn't afford this. That's true and
likely why I'm a hobbyist and not a professional programmer.
OTOH it's pretty rare that I write routines with variables in them
to begin with.
3. As Mr. Pelc remarked, stack operators are faster.
This is what Mr. Pelc remarked, regarding such style
of programming - yes, many years ago I was guilty of
that too - already 15 years ago:
https://groups.google.com/g/comp.lang.forth/c/m9xy5k5BfkY/m/FFmH9GE5UJAJ
"Although the code is compilable and can be made efficient,
the source code is a maintenance nightmare!"
Maybe he changed his mind since that time - well, since
he's here, you may want to ask him a question.
B. Pym wrote:
mhx wrote:
On Sun, 6 Oct 2024 7:51:31 +0000, dxf wrote:
Is there an easier way of doing this? End goal is a double number representing centi-secs.
empty decimal
: SPLIT ( a u c -- a2 u2 a3 u3 ) >r 2dup r> scan 2swap 2 pick - ;
: >INT ( adr len -- u ) 0 0 2swap >number 2drop drop ;
: /T ( a u -- $hour $min $sec )
2 0 do [char] : split 2swap dup if 1 /string then loop
2 0 do dup 0= if 2rot 2rot then loop ;
: .T 2swap 2rot cr >int . ." hr " >int . ." min " >int . ." sec " ;
s" 1:2:3" /t .t
s" 02:03" /t .t
s" 03" /t .t
s" 23:59:59" /t .t
s" 0:00:03" /t .t
Why don't you use the fact that >NUMBER returns the given
string starting with the first unconverted character?
SPLIT should be redundant.
-marcel
: CHAR-NUMERIC? 48 58 WITHIN ;
: SKIP-NON-NUMERIC ( adr u -- adr2 u2)
BEGIN
DUP IF OVER C@ CHAR-NUMERIC? NOT ELSE 0 THEN
WHILE
1 /STRING
REPEAT ;
: SCAN-NEXT-NUMBER ( n adr len -- n2 adr2 len2)
2>R 60 * 0. 2R> >NUMBER
2>R D>S + 2R> ;
: PARSE-TIME ( adr len -- seconds)
0 -ROT
BEGIN
SKIP-NON-NUMERIC
DUP
WHILE
SCAN-NEXT-NUMBER
REPEAT
2DROP ;
S" hello 1::36 world" PARSE-TIME CR .
96 ok
: SCAN-NUMBER-OR-SKIP ( n adr len -- n' adr' len')
DUP >R
0 0 2SWAP >NUMBER
DUP R> =
IF 2SWAP 2DROP 1 /STRING
ELSE
2>R D>S SWAP 60 * + 2R>
THEN ;
: PARSE-TIME ( adr len -- seconds)
0 -ROT
BEGIN
DUP
WHILE
SCAN-NUMBER-OR-SKIP
REPEAT
2DROP ;
S" hi 5 or 1 is 44 ho " PARSE-TIME CR .
18104
So now you see: one can either use variable to
make the solution cleaner - or one can create new word(s).
Either way - some new names are added to the vocabulary.
Why the use of variable, instead of new words,
should be perceived of 'inferior'? Variable is a word too.
Unlike variables, factors (subroutines) don't contradict effective
stack-based programming.
From "Thinking Forth":
"TIP: Simplify code by using the stack. But don’t stack too
deeply within any single definition. Redesign, or, as a last resort,
use a named variable.
Some newcomers to Forth view the stack the way a gymnast views
a trampoline: as a fun place to bounce around on. But the stack
is meant for data-passing, not acrobatics.
[..]
(not going to paste (almost) whole chapter, but you may want
to read the section "Redesign" that follows the above)
I’ve been guilty many times of playing hotshot, trying to do
as much as possible on the stack rather than define a local
variable. There are three reasons to avoid this cockiness.
First, it’s a pain to code that way. Second, the result
is unreadable. Third, all your work becomes useless when
a design change becomes necessary, and the order of two
arguments changes on the stack. The DUPs, OVERs and ROTs
weren’t really solving the problem, just jockeying things
into position. With this third reason in mind, I recommend
the following:
TIP: Especially in the design phase, keep on the stack only
the arguments you’re using immediately. Create local
variables for any others. (If necessary, eliminate the variables
during the optimization phase.)
Fourth, if the definition is extremely time-critical, those
tricky stack manipulators, (e.g., ROT ROT) can really eat up
clock cycles. Direct access to variables is faster."
Yes, Brodie warns us next "but careful with variables' use
too" - and I still think my use of variables in two examples
I recently pasted wasn't "legit" in any way. It was just
applying the tips you see above.
On 11/06/2025 3:34 am, LIT wrote:
...
Fourth, if the definition is extremely time-critical, those
tricky stack manipulators, (e.g., ROT ROT) can really eat up
clock cycles. Direct access to variables is faster."
Pushing variables on the stack, executing them, along with their
associated @ and ! eats clock cycles. This is certainly the case
in the systems you use.
Yes, Brodie warns us next "but careful with variables' use
too" - and I still think my use of variables in two examples
I recently pasted wasn't "legit" in any way. It was just
applying the tips you see above.
When is it "legit" to give up? I've written routines I believed
needed VARIABLEs. But after a 'cooling off' period, I can look
at the problem again afresh and find I can do better. Folks will
say in the real world one couldn't afford this. That's true and
likely why I'm a hobbyist and not a professional programmer.
OTOH it's pretty rare that I write routines with variables in them
to begin with.
If you wanna win, you gotta cheat
On 11-06-2025 03:49, dxf wrote:
On 11/06/2025 3:34 am, LIT wrote:
...
Fourth, if the definition is extremely time-critical, those
tricky stack manipulators, (e.g., ROT ROT) can really eat up
clock cycles. Direct access to variables is faster."
Pushing variables on the stack, executing them, along with their
associated @ and ! eats clock cycles. This is certainly the case
in the systems you use.
Agreed.
Yes, Brodie warns us next "but careful with variables' use
too" - and I still think my use of variables in two examples
I recently pasted wasn't "legit" in any way. It was just
applying the tips you see above.
When is it "legit" to give up? I've written routines I believed
needed VARIABLEs. But after a 'cooling off' period, I can look
at the problem again afresh and find I can do better. Folks will
say in the real world one couldn't afford this. That's true and
likely why I'm a hobbyist and not a professional programmer.
OTOH it's pretty rare that I write routines with variables in them
to begin with.
As a guy who used Forth programming in a professional environment, I can
at least tell you how I did it..
When you're on the spot, you're on the spot - and you got to provide in
the allotted time, even if it means making sub-optimal code. That's just
the way it is, that's corporate life.
If you tell your boss "Brodie told you to", he's gonna shake his head,
ask who Brodie is and then ship you to the corporate shrink for an
emergency session.
But what I did was to either collect stuff in advance ("Hey, that's a
nice comma'd printout word by Ed. Better make it work in 4tH!") - or
make certain libraries beforehand. In that case, all you have to do is
to shove all those elements together and you're done. The tricky stuff
is already in your tool chest..
Take a look at the 4tH library and notice how much of this stuff is of
no interest at all to the occasional user. Well, that was because it
wasn't written for you. It was written to be applied at work, so I can
do miracles and save my reputation. If you wanna win, you gotta cheat ;-)
Hans Bezemer
When is it "legit" to give up? I've written routines I believed
needed VARIABLEs. But after a 'cooling off' period, I can look
at the problem again afresh and find I can do better. Folks will
say in the real world one couldn't afford this. That's true and
likely why I'm a hobbyist and not a professional programmer.
OTOH it's pretty rare that I write routines with variables in them
to begin with.
TO ME it doesn't make a problem how YOU create
your code, if it suits you better in any way.
I just explained why MY code looks differently:
because I reckon the merits listed by Brodie,
and longer time ago 'switched' from long strings
of that DUPs, ROTs and SWAPs to something
I perceive as a cleaner and more comprehensible
solution - also not being "maintenance nightmare"
if I later decide to change anything.
It's the others - or maybe single "other" - who
decided: "I'll show you how to write 'canonical
Forth', look how the masters do this and learn".
Obviously Brodie's "tips", applied in "real life",
must be irritating for some "real programmers", or
something.
Seriously? They ask me beforehand. Philips matlab got upset
because they were not used to people finishing in the time they
estimate.
In another project I was 10% accurate on a total of 30 bugs, and
within 30% of each bug individually.
Then I was dropped as project leader into a project that had to finish
in three months, and I succeeded. If I failed, nobody would complain.
Fokker Space had a architectural design disapproved by ESO. It was due
at a certain date. Big kudos if you succeed, and I did.
If you tell your boss "Brodie told you to", he's gonna shake his head,
ask who Brodie is and then ship you to the corporate shrink for an
emergency session.
There are stupid bosses, that insist on a one line change over
as 10 line change, as if this made the change more "reliable".
At the same time they disapprove of test automation.
But what I did was to either collect stuff in advance ("Hey, that's a
nice comma'd printout word by Ed. Better make it work in 4tH!") - or
make certain libraries beforehand. In that case, all you have to do is
to shove all those elements together and you're done. The tricky stuff
is already in your tool chest..
Take a look at the 4tH library and notice how much of this stuff is of
no interest at all to the occasional user. Well, that was because it
wasn't written for you. It was written to be applied at work, so I can
do miracles and save my reputation. If you wanna win, you gotta cheat ;-)
I don't agree that tool chests built on practice are personal.
If you wanna win, you gotta cheat
Indeed it's what you're doing all the time:
insisting on "winning" and cheating.
But it's not that helpful for your so-called
reputation.
--
3. As Mr. Pelc remarked, stack operators are faster.
This is what Mr. Pelc remarked, regarding such style
of programming - yes, many years ago I was guilty of
that too - already 15 years ago:
https://groups.google.com/g/comp.lang.forth/c/m9xy5k5BfkY/m/FFmH9GE5UJAJ
"Although the code is compilable and can be made efficient,
the source code is a maintenance nightmare!"
Maybe he changed his mind since that time - well, since
he's here, you may want to ask him a question.
B. Pym wrote:
B. Pym wrote:
mhx wrote:
On Sun, 6 Oct 2024 7:51:31 +0000, dxf wrote:
Is there an easier way of doing this? End goal is a double number
representing centi-secs.
empty decimal
: SPLIT ( a u c -- a2 u2 a3 u3 ) >r 2dup r> scan 2swap 2 pick - ;
: >INT ( adr len -- u ) 0 0 2swap >number 2drop drop ;
: /T ( a u -- $hour $min $sec )
2 0 do [char] : split 2swap dup if 1 /string then loop
2 0 do dup 0= if 2rot 2rot then loop ;
: .T 2swap 2rot cr >int . ." hr " >int . ." min " >int . ." sec " ; >>>>>
s" 1:2:3" /t .t
s" 02:03" /t .t
s" 03" /t .t
s" 23:59:59" /t .t
s" 0:00:03" /t .t
Why don't you use the fact that >NUMBER returns the given
string starting with the first unconverted character?
SPLIT should be redundant.
-marcel
: CHAR-NUMERIC? 48 58 WITHIN ;
: SKIP-NON-NUMERIC ( adr u -- adr2 u2)
BEGIN
DUP IF OVER C@ CHAR-NUMERIC? NOT ELSE 0 THEN
WHILE
1 /STRING
REPEAT ;
: SCAN-NEXT-NUMBER ( n adr len -- n2 adr2 len2)
2>R 60 * 0. 2R> >NUMBER
2>R D>S + 2R> ;
: PARSE-TIME ( adr len -- seconds)
0 -ROT
BEGIN
SKIP-NON-NUMERIC
DUP
WHILE
SCAN-NEXT-NUMBER
REPEAT
2DROP ;
S" hello 1::36 world" PARSE-TIME CR .
96 ok
: SCAN-NUMBER-OR-SKIP ( n adr len -- n' adr' len')
DUP >R
0 0 2SWAP >NUMBER
DUP R> =
IF 2SWAP 2DROP 1 /STRING
ELSE
2>R D>S SWAP 60 * + 2R>
THEN ;
: PARSE-TIME ( adr len -- seconds)
0 -ROT
BEGIN
DUP
WHILE
SCAN-NUMBER-OR-SKIP
REPEAT
2DROP ;
S" hi 5 or 1 is 44 ho " PARSE-TIME CR .
18104
Using local variables.
: SCAN-NUMBER-OR-SKIP { n adr len -- n' adr' len' }
0. adr len >NUMBER { adr' len' } D>S { m }
len' len =
IF n adr len 1 /STRING
ELSE
n 60 * m + adr' len'
THEN ;
Stack jugglery means wasting CPU cycles for
moving the bytes around - it's contrproductive.
Variables have been invented to be used. They're
useful, if you didn't notice, or if they didn't
tell you that in your college, or wherever.
Forth uses variables in the global sense and this works well.
Variables at the word level is often an indication something is
wrong.
Stack jugglery means wasting CPU cycles for
moving the bytes around - it's contrproductive.
Variables have been invented to be used. They're
useful, if you didn't notice, or if they didn't
tell you that in your college, or wherever.
Forth uses variables in the global sense and this works well.
Variables at the word level is often an indication something is
wrong.
"As as result of Forth's heavy use of the stack
for parameter passing and the efficiency with which
the stack operators execute, it is easy for the beginner
to run away with the idea that the stack operators should
be employed at every opportunity. This is not the case,
the key to good Forth programming being structure and
simplicity of coding achieved through correct analysis
of the problem. Part of the beauty of Forth program is
the ease with which they can be maintained and adapted
to cope with new circumstances, due to their extreme
modularity. A major factor in program maintenance is
the readability of the code and this is certainly not
helped by massive clusters of stack operators. The use
of named variables, whilst consuming more dictionary
space and executing more slowly, leads to far more
readable source code and often to a better factored
and ultimately more efficient application. This becomes
ever more significant the larger the application."
(R. Olney, M. Benson - "Forth Fundamentals" - chapter
7.4. "Stack versus variables")
Your talk with Marcel reminded me the above.
Counter-example: a good number of my apps involve structs, arrays
and signal vectors in heap memory. Stack juggling? Absolutely not.
The code would be unreadable and a nightmare to debug.
Factoring in smaller code portions is often impossible because
you can't always distribute data, that inherently belongs together,
over separate words.
Then why factor, when with using named parameters = locals, the
code is already short, readable, maintainable, and bug-free.
Ask yourself why the Forth Scientific Library makes heavy use of
locals.
Of course things look different with simpler applications.
Counter-example: a good number of my apps involve structs, arrays
and signal vectors in heap memory. Stack juggling? Absolutely not.
The code would be unreadable and a nightmare to debug.
Factoring in smaller code portions is often impossible because
you can't always distribute data, that inherently belongs together,
over separate words.
Then why factor, when with using named parameters = locals, the
code is already short, readable, maintainable, and bug-free.
Ask yourself why the Forth Scientific Library makes heavy use of
locals.
Of course things look different with simpler applications.
On 20/06/2025 3:36 pm, minforth wrote:
Counter-example: a good number of my apps involve structs, arrays
and signal vectors in heap memory. Stack juggling? Absolutely not.
The code would be unreadable and a nightmare to debug.
Factoring in smaller code portions is often impossible because
you can't always distribute data, that inherently belongs together,
over separate words.
Then why factor, when with using named parameters = locals, the
code is already short, readable, maintainable, and bug-free.
Ask yourself why the Forth Scientific Library makes heavy use of
locals.
Of course things look different with simpler applications.
What you're saying is at the level you program, it hardly matters
whether it's Forth or something else. It's true I have little to
no reason to use floating-point. I did wonder why Julian Noble
persisted with Forth.
On 20/06/2025 3:36 pm, minforth wrote:
Counter-example: a good number of my apps involve structs, arrays
and signal vectors in heap memory. Stack juggling? Absolutely not.
The code would be unreadable and a nightmare to debug.
Factoring in smaller code portions is often impossible because
you can't always distribute data, that inherently belongs together,
over separate words.
Then why factor, when with using named parameters = locals, the
code is already short, readable, maintainable, and bug-free.
Ask yourself why the Forth Scientific Library makes heavy use of
locals.
Of course things look different with simpler applications.
What you're saying is at the level you program, it hardly matters
whether
it's Forth or something else.
The claim made 40 years ago was: "Forth's heavy use of the stack for parameter passing [...] it is easy for the beginner to run away with
the idea that the stack operators should be employed at every
opportunity." The suggestion being there's so much traffic one must
use stack juggling to solve it.
That's the fear and bogeyman that's regularly trotted out about Forth.
But is it true? None of the colon definitions the authors provide in
their book would indicate it. Each used 0, 1, 2 and occasionally 3 parameters. Any variables they employed were sparse and global in
nature.
How about more comprehensive applications?
On Fri, 20 Jun 2025 5:36:05 +0000, minforth wrote:
Counter-example: a good number of my apps involve structs, arrays
and signal vectors in heap memory. Stack juggling? Absolutely not.
The code would be unreadable and a nightmare to debug.
Factoring in smaller code portions is often impossible because
you can't always distribute data, that inherently belongs together,
over separate words.
Then why factor, when with using named parameters = locals, the
code is already short, readable, maintainable, and bug-free.
Interesting questions. My experience says that arrays and vectors are
ok, but structs are dangerous, (especially?) when nested. In a 'C'
project that I contribute to, structs arbitrarily glue data together,
and then forwardly defined macros hide the details.
It is impossible to debug this code without tools to decompile/inspect
the source. It is very difficult to change/rearrange/delete struct
fields, because they may be used in other places of the code for a
completely different purpose. The result is that structs only grow
and nobody dares to prune them. The only remedy is to completely
start over.
Ask yourself why the Forth Scientific Library makes heavy use of
locals.
Because the original algorithms do.
Of course things look different with simpler applications.
And then Einstein's famous quote spoils the fun.
-marcel--
On Fri, 20 Jun 2025 6:29:35 +0000, dxf wrote:
On 20/06/2025 3:36 pm, minforth wrote:
Counter-example: a good number of my apps involve structs, arrays
and signal vectors in heap memory. Stack juggling? Absolutely not.
The code would be unreadable and a nightmare to debug.
Factoring in smaller code portions is often impossible because
you can't always distribute data, that inherently belongs together,
over separate words.
Then why factor, when with using named parameters = locals, the
code is already short, readable, maintainable, and bug-free.
Ask yourself why the Forth Scientific Library makes heavy use of
locals.
Of course things look different with simpler applications.
What you're saying is at the level you program, it hardly matters
whether
it's Forth or something else.
But yes, it does matter! Because Forth is compact and can can run
on devices with limited resources. With Forth I can do realtime math
on the device without huge libraries, such as LAPACK.
The claim made 40 years ago was: "Forth's heavy use of the stack for
parameter passing [...] it is easy for the beginner to run away with
the idea that the stack operators should be employed at every
opportunity." The suggestion being there's so much traffic one must
use stack juggling to solve it.
How many years ago it was made — it doesn't that matter.
Pythagorean theorem was made over 2500 years ago, and
AFAIK it's still actual.
That's the fear and bogeyman that's regularly trotted out about Forth.
But is it true? None of the colon definitions the authors provide in
their book would indicate it. Each used 0, 1, 2 and occasionally 3
parameters. Any variables they employed were sparse and global in
nature.
How about more comprehensive applications?
Then just compare the two examples from "my"
thread "May the numbers speak". Is really the
solution that uses strings of "r> drop nip s>d"
etc. more clear and comprehensible? Oh, really?
It's what we were talking about - not about
"one of yours that had 154 colon definitions".
You can repair such things by using new stack paradigms. I've addedThe claim made 40 years ago was: "Forth's heavy use of the stack for
parameter passing [...] it is easy for the beginner to run away with
the idea that the stack operators should be employed at every
opportunity." The suggestion being there's so much traffic one must
use stack juggling to solve it.
How many years ago it was made — it doesn't that matter.
Pythagorean theorem was made over 2500 years ago, and
AFAIK it's still actual.
That's the fear and bogeyman that's regularly trotted out about Forth.
But is it true? None of the colon definitions the authors provide in
their book would indicate it. Each used 0, 1, 2 and occasionally 3
parameters. Any variables they employed were sparse and global in
nature.
How about more comprehensive applications?
Then just compare the two examples from "my"
thread "May the numbers speak". Is really the
solution that uses strings of "r> drop nip s>d"
etc. more clear and comprehensible? Oh, really?
It's what we were talking about - not about
"one of yours that had 154 colon definitions".
--
You can repair such things by using new stack paradigms. I've added<snip>
several ones, most of 'em inspired by others. E.g
"swap 3OS with TOS" (SPIN, a b c -- c b a)
"DUP 2OS" (STOW, a b -- a a b)
But of course, you have to do the work. If you're incapable or too lazy
to do the work, yeah, then you will find Forth bites you. Note that C is
a very nice language as well. Beats Forth performance wise - so, what's
there not to like :)
On Sun, 22 Jun 2025 14:35:40 +0000, Hans Bezemer wrote:
You can repair such things by using new stack paradigms. I've added<snip>
several ones, most of 'em inspired by others. E.g
"swap 3OS with TOS" (SPIN, a b c -- c b a)
"DUP 2OS" (STOW, a b -- a a b)
But of course, you have to do the work. If you're incapable or too lazy
to do the work, yeah, then you will find Forth bites you. Note that C is
a very nice language as well. Beats Forth performance wise - so, what's
there not to like :)
I mostly belong to the lazy kind. Therefore, I prefer to let the
computer
do the tedious work before spending time on premature optimizations,
such as stack ordering.
So, I made me a small extension to the locals word set. Using your
example SPIN (abc — cba), I can define it as follows:
: SPIN { a b c == c b a } ; \ no need for additional code before ;
or likewise for floats, doubles, strings, matrices
: FSPIN { f: a b c == c b a } ;
: DSPIN { d: a b c == c b a } ;
: "SPIN { s: a b c == c b a } ;
: MSPIN { m: a b c == c b a } ;
Code generation and register optimization is the computer's job.
SPIN/STOW or similar microexamples can, of course, be defined quickly
with classic Forth stack juggling too. The power of the extension
becomes more apparent with mixed parameter types and/or more parameters,
and of course, with some non-trivial algorithm to solve.
I couldn't have done this in C, but Forth allows you to modify the
compiler until it suits your work domain.
On 20-06-2025 10:46, LIT wrote:
The claim made 40 years ago was: "Forth's heavy use of the stack for
parameter passing [...] it is easy for the beginner to run away with
the idea that the stack operators should be employed at every
opportunity." The suggestion being there's so much traffic one must
use stack juggling to solve it.
How many years ago it was made — it doesn't that matter.
Pythagorean theorem was made over 2500 years ago, and
AFAIK it's still actual.
Forth.That's the fear and bogeyman that's regularly trotted out about
inBut is it true? None of the colon definitions the authors provide
their book would indicate it. Each used 0, 1, 2 and occasionally 3
parameters. Any variables they employed were sparse and global in
nature.
How about more comprehensive applications?
Then just compare the two examples from "my"
thread "May the numbers speak". Is really the
solution that uses strings of "r> drop nip s>d"
etc. more clear and comprehensible? Oh, really?
It's what we were talking about - not about
"one of yours that had 154 colon definitions".
--You can repair such things by using new stack paradigms. I've added
several ones, most of 'em inspired by others. E.g
"swap 3OS with TOS" (SPIN, a b c -- c b a)
"DUP 2OS" (STOW, a b -- a a b)
-- and several Return Stack operators like R'@, R"@ and RDROP. They're
not just shorthand, but also a template for stack manipulations.
The R-stack operators are excellently suited to store (almost)
constants. The D-stack operators document the intentions of the
programmer.
I'm sure your example comes from a "clean up" operation. It cleans up
the stacks. Most probably TOS is a return value, that has to be extended
to a double word (most likely because it is interfaced with a double
word word).
One of the techniques I developed is to figure out which stack diagram
is most suited for the next set of operations. You do your stuff to get there, document the resulting scheme - and the rest of your stack manipulations are simple and shallow.
But of course, you have to do the work. If you're incapable or too lazy
to do the work, yeah, then you will find Forth bites you. Note that C is
a very nice language as well. Beats Forth performance wise - so, what's
there not to like :)
Hans Bezemer
So, I made me a small extension to the locals word set. Using your
example SPIN (abc — cba), I can define it as follows:
: SPIN { a b c == c b a } ; \ no need for additional code before ;
or likewise for floats, doubles, strings, matrices
: FSPIN { f: a b c == c b a } ;
So, I made me a small extension to the locals word set. Using your
example SPIN (abc — cba), I can define it as follows:
: SPIN { a b c == c b a } ; \ no need for additional code before ;
or likewise for floats, doubles, strings, matrices
: FSPIN { f: a b c == c b a } ;
: DSPIN { d: a b c == c b a } ;
: "SPIN { s: a b c == c b a } ;
: MSPIN { m: a b c == c b a } ;
Code generation and register optimization is the computer's job.
SPIN/STOW or similar microexamples can, of course, be defined quickly
with classic Forth stack juggling too. The power of the extension
becomes more apparent with mixed parameter types and/or more parameters,
and of course, with some non-trivial algorithm to solve.
On Sun, 22 Jun 2025 21:27:40 +0000, minforth wrote:
[..]
So, I made me a small extension to the locals word set. Using your
example SPIN (abc — cba), I can define it as follows:
: SPIN { a b c == c b a } ; \ no need for additional code before ;
or likewise for floats, doubles, strings, matrices
: FSPIN { f: a b c == c b a } ;
: DSPIN { d: a b c == c b a } ;
: "SPIN { s: a b c == c b a } ;
: MSPIN { m: a b c == c b a } ;
Code generation and register optimization is the computer's job.
SPIN/STOW or similar microexamples can, of course, be defined quickly
with classic Forth stack juggling too. The power of the extension
becomes more apparent with mixed parameter types and/or more parameters,
and of course, with some non-trivial algorithm to solve.
Do you mean your compiler automatically handles/allows combinations
like
... 22e-12 69. A{{ ( F: -- a ) ( D: -- b ) ( M: -- c ) SPIN ...
I found that handling mixed types explodes the code that needs
to be written for a simple compiler like, e.g., Tiny-KISS . It
would be great if that can be automated.
On Sun, 22 Jun 2025 14:35:40 +0000, Hans Bezemer wrote:
On 20-06-2025 10:46, LIT wrote:
Forth.The claim made 40 years ago was: "Forth's heavy use of the stack for;
parameter passing [...] it is easy for the beginner to run away with
the idea that the stack operators should be employed at every
opportunity." The suggestion being there's so much traffic one must >> >> use stack juggling to solve it.
How many years ago it was made — it doesn't that matter.
Pythagorean theorem was made over 2500 years ago, and
AFAIK it's still actual.
;
That's the fear and bogeyman that's regularly trotted out about
inBut is it true? None of the colon definitions the authors provide
You can repair such things by using new stack paradigms. I've addedtheir book would indicate it. Each used 0, 1, 2 and occasionally 3;
parameters. Any variables they employed were sparse and global in
nature.
;
How about more comprehensive applications?
Then just compare the two examples from "my"
thread "May the numbers speak". Is really the
solution that uses strings of "r> drop nip s>d"
etc. more clear and comprehensible? Oh, really?
;
It's what we were talking about - not about
"one of yours that had 154 colon definitions".
;
--
several ones, most of 'em inspired by others. E.g
"swap 3OS with TOS" (SPIN, a b c -- c b a)
"DUP 2OS" (STOW, a b -- a a b)
-- and several Return Stack operators like R'@, R"@ and RDROP. They're
not just shorthand, but also a template for stack manipulations.
The R-stack operators are excellently suited to store (almost)
constants. The D-stack operators document the intentions of the
programmer.
I'm sure your example comes from a "clean up" operation. It cleans up
the stacks. Most probably TOS is a return value, that has to be extended
to a double word (most likely because it is interfaced with a double
word word).
One of the techniques I developed is to figure out which stack diagram
is most suited for the next set of operations. You do your stuff to get
there, document the resulting scheme - and the rest of your stack
manipulations are simple and shallow.
But of course, you have to do the work. If you're incapable or too lazy
to do the work, yeah, then you will find Forth bites you. Note that C is
a very nice language as well. Beats Forth performance wise - so, what's
there not to like :)
Hans Bezemer
Mr. FIFO, that Forth of yours bites you from time to time?
It must have rabies, or something. You may want to take
it to the nearest vet.
On 23-06-2025 07:05, LIT wrote:
On Sun, 22 Jun 2025 14:35:40 +0000, Hans Bezemer wrote:
On 20-06-2025 10:46, LIT wrote:
Forth.The claim made 40 years ago was: "Forth's heavy use of the stack for >>> >> parameter passing [...] it is easy for the beginner to run away with >>> >> the idea that the stack operators should be employed at every;
opportunity." The suggestion being there's so much traffic one must >>> >> use stack juggling to solve it.
How many years ago it was made — it doesn't that matter.
Pythagorean theorem was made over 2500 years ago, and
AFAIK it's still actual.
;
That's the fear and bogeyman that's regularly trotted out about
You can repair such things by using new stack paradigms. I've addedBut is it true? None of the colon definitions the authors provide >>> in;
their book would indicate it. Each used 0, 1, 2 and occasionally 3 >>> >> parameters. Any variables they employed were sparse and global in >>> >> nature.
;
How about more comprehensive applications?
Then just compare the two examples from "my"
thread "May the numbers speak". Is really the
solution that uses strings of "r> drop nip s>d"
etc. more clear and comprehensible? Oh, really?
;
It's what we were talking about - not about
"one of yours that had 154 colon definitions".
;
--
several ones, most of 'em inspired by others. E.g
"swap 3OS with TOS" (SPIN, a b c -- c b a)
"DUP 2OS" (STOW, a b -- a a b)
-- and several Return Stack operators like R'@, R"@ and RDROP. They're
not just shorthand, but also a template for stack manipulations.
The R-stack operators are excellently suited to store (almost)
constants. The D-stack operators document the intentions of the
programmer.
I'm sure your example comes from a "clean up" operation. It cleans up
the stacks. Most probably TOS is a return value, that has to be extended >>> to a double word (most likely because it is interfaced with a double
word word).
One of the techniques I developed is to figure out which stack diagram
is most suited for the next set of operations. You do your stuff to get
there, document the resulting scheme - and the rest of your stack
manipulations are simple and shallow.
But of course, you have to do the work. If you're incapable or too lazy
to do the work, yeah, then you will find Forth bites you. Note that C is >>> a very nice language as well. Beats Forth performance wise - so, what's
there not to like :)
Hans Bezemer
Mr. FIFO, that Forth of yours bites you from time to time?
It must have rabies, or something. You may want to take
it to the nearest vet.
No, I'm a good Forth wrangler after 40 years - given all the atrocities
I am willing to rewrite into proper Forth. It's there for all people to
see. I don't cheat. I'm not secretly writing locals infested code.
No, frankly it was more targeted to those who are obviously unable to
write proper Forth. Unwilling to admit it - and unwilling to migrate to
much simpler languages, which are more suited for their limited talents.
I can only assume they've run into a wall in private, got angry at the language instead of themselves - and then tried to turn it into the abomination they so desperately promote as "the true Forth", which
actually is nothing more than a flawed C in wrapped in a thin Forth
skin.
It's silly. I know Forth is an elite language
- but sometimes you have
to realize you simply don't belong to that group, not in a million
years.
I pity those people. Really. My prayers are for them. Every night.
Hans Bezemer
On Mon, 23 Jun 2025 5:40:37 +0000, mhx wrote:
On Sun, 22 Jun 2025 21:27:40 +0000, minforth wrote:I don't know if I got you right, because as previously
[..]
Do you mean your compiler automatically handles/allows combinations
like
... 22e-12 69. A{{ ( F: -- a ) ( D: -- b ) ( M: -- c ) SPIN ...
I found that handling mixed types explodes the code that needs
to be written for a simple compiler like, e.g., Tiny-KISS . It
would be great if that can be automated.
defined, SPIN expects three integers on the data stack.
What you mean "you don't cheat"? Since when?
You cheat all the time:
"If you wanna win, you gotta cheat ;-)"
https://www.novabbs.com/devel/article-flat.php?id=29346&group=comp.lang.forth#29346
Actually it isn't; it's probably the most "democratic"
programming language in the world...
..it's just some people try hard to present themselves
as a "members of (supposed) elite circle" - so at the
moment they came up with the idea "the best way will
be when I promote myself as an elite programmer".
I mean
someone who made a whole YT recording persuading the
audience, that "Forth uses FIFO stack"?
On Mon, 23 Jun 2025 10:02:44 +0000, minforth wrote:
On Mon, 23 Jun 2025 5:40:37 +0000, mhx wrote:
On Sun, 22 Jun 2025 21:27:40 +0000, minforth wrote:I don't know if I got you right, because as previously
[..]
Do you mean your compiler automatically handles/allows combinations
like
... 22e-12 69. A{{ ( F: -- a ) ( D: -- b ) ( M: -- c ) SPIN ...
I found that handling mixed types explodes the code that needs
to be written for a simple compiler like, e.g., Tiny-KISS . It
would be great if that can be automated.
defined, SPIN expects three integers on the data stack.
I was indeed too hasty. If items are stacked, no type conversion
is needed if they are only reordered. (Reordering needs
no code anyway, as it is a only a memo to the compiler.) The
problems only arise when an cell must be translated to a complex
extended float, or when using floats to initialize an arbitrary
precision matrix.
Do you really support matrix and string type locals? The former
I only do for arbitrary precision, the latter can be handled
with DLOCALS| .
On 23-06-2025 14:49, LIT wrote:
What you mean "you don't cheat"? Since when?
You cheat all the time:
"If you wanna win, you gotta cheat ;-)"
Ooh - another quote out of context. BTW, I also fart loudly at weddings.
:)
https://www.novabbs.com/devel/article-flat.php?id=29346&group=comp.lang.forth#29346
Thanks for the link. People can now see the quote in full context -
usually I gotta find out myself!
Actually it isn't; it's probably the most "democratic"
programming language in the world...
If you make a claim, you have to prove it (the burden of proof is on
your side). However, there is no proof - so what can be asserted without proof can be dismissed without proof.
..it's just some people try hard to present themselves
as a "members of (supposed) elite circle" - so at the
moment they came up with the idea "the best way will
be when I promote myself as an elite programmer".
Where EXACTLY did I claim I was "an elite programmer"? But I do thank
you for putting me into that group! Thanks!! I appreciate it - never
thought I'd get such a compliment from you! <BLUSH>
I mean
someone who made a whole YT recording persuading the
audience, that "Forth uses FIFO stack"?
You might want to sing another song. This one is getting old. It might
not only indicate a severe lack of true arguments, it also clearly
indicates that your only "arguments" are just logical fallacies of the
most primitive sort (according to "Grahams triangle of disagreement" - I
can help you with that one if assistance is required).
It is as if you never left high school. Because that is where you
normally observe such infantile behavior. Especially with teenage girls.
Hans Bezemer
minforth@gmx.net (minforth) writes:
So, I made me a small extension to the locals word set. Using your
example SPIN (abc — cba), I can define it as follows:
: SPIN { a b c == c b a } ; \ no need for additional code before ;
What is the advantage of using this extension over the Forth-2012:
: spin {: a b c :} c b a ;
?
On Mon, 23 Jun 2025 5:18:34 +0000, Anton Ertl wrote:
minforth@gmx.net (minforth) writes:
So, I made me a small extension to the locals word set. Using your
example SPIN (abc — cba), I can define it as follows:
: SPIN { a b c == c b a } ; \ no need for additional code before ;
What is the advantage of using this extension over the Forth-2012:
: spin {: a b c :} c b a ;
?
Obviously, there is no advantage for such small definitions.
For me, the small syntax extension is a convenience when working
with longer definitions. A bit contrived (:= synonym for TO):
: SOME-APP { a f: b c | temp == n: flag z: freq }
\ inputs: integer a, floats b c
\ uninitialized: float temp
\ outputs: integer flag, complex freq
<: FUNC < ... calc function ... > ;>
\ emulated embedded function using { | xt: func }
< ... calc something ... > := temp
< ... calc other things ... > := freq / basic formula
< ... calc other things ... > := flag
< ... calc correction ... > := freq / better estimation
;
While working on such things, I can focus my eyes on the formulas,
all local values are visible in one place, and I don't have to
worry about tracking the data stack(s) for lost/accumulated items.
As I said, it is nothing spectacular, just helpful. And to my own
eyes, it looks neater. ;-)
And before dxf yowls again: it is still Forth. :o)
On 23-06-2025 23:03, minforth wrote:
On Mon, 23 Jun 2025 5:18:34 +0000, Anton Ertl wrote:
minforth@gmx.net (minforth) writes:
So, I made me a small extension to the locals word set. Using your
example SPIN (abc — cba), I can define it as follows:
: SPIN { a b c == c b a } ; \ no need for additional code before ;
What is the advantage of using this extension over the Forth-2012:
: spin {: a b c :} c b a ;
?
Obviously, there is no advantage for such small definitions.
For me, the small syntax extension is a convenience when working
with longer definitions. A bit contrived (:= synonym for TO):
: SOME-APP { a f: b c | temp == n: flag z: freq }
\ inputs: integer a, floats b c
\ uninitialized: float temp
\ outputs: integer flag, complex freq
<: FUNC < ... calc function ... > ;>
\ emulated embedded function using { | xt: func }
< ... calc something ... > := temp
< ... calc other things ... > := freq / basic formula
< ... calc other things ... > := flag
< ... calc correction ... > := freq / better estimation
;
While working on such things, I can focus my eyes on the formulas,
all local values are visible in one place, and I don't have to
worry about tracking the data stack(s) for lost/accumulated items.
As I said, it is nothing spectacular, just helpful. And to my own
eyes, it looks neater. ;-)
And before dxf yowls again: it is still Forth. :o)
Well.. Technically everything written in Forth is Forth. But it is not canonical Forth - because if it were canonical Forth, we would have
covered locals in "Starting Forth" - and we didn't.
Now, let's assume we found we were wrong. But there was a chapter in "Thinking Forth" called "The stylish stack" - not "The stylish locals".
As a matter of fact, it states that "the stack is not an array" -
meaning: not randomly accessible. And what are locals? Right. Randomly accessible.
So, what is this? It's a feeble imitation of C. It's not part of the
original design. Because if it were part of the original design, you
would find out what it means to think differently. This is merely C
thinking. Nothing else. Certainly not Forth thinking.
On Mon, 23 Jun 2025 21:20:46 +0000, Hans Bezemer wrote:
On 23-06-2025 23:03, minforth wrote:
On Mon, 23 Jun 2025 5:18:34 +0000, Anton Ertl wrote:
minforth@gmx.net (minforth) writes:
So, I made me a small extension to the locals word set. Using your
example SPIN (abc — cba), I can define it as follows:
: SPIN { a b c == c b a } ; \ no need for additional code before ;
What is the advantage of using this extension over the Forth-2012:
: spin {: a b c :} c b a ;
?
Obviously, there is no advantage for such small definitions.
For me, the small syntax extension is a convenience when working
with longer definitions. A bit contrived (:= synonym for TO):
: SOME-APP { a f: b c | temp == n: flag z: freq }
\ inputs: integer a, floats b c
\ uninitialized: float temp
\ outputs: integer flag, complex freq
<: FUNC < ... calc function ... > ;>
\ emulated embedded function using { | xt: func }
< ... calc something ... > := temp
< ... calc other things ... > := freq / basic formula
< ... calc other things ... > := flag
< ... calc correction ... > := freq / better estimation
;
While working on such things, I can focus my eyes on the formulas,
all local values are visible in one place, and I don't have to
worry about tracking the data stack(s) for lost/accumulated items.
As I said, it is nothing spectacular, just helpful. And to my own
eyes, it looks neater. ;-)
And before dxf yowls again: it is still Forth. :o)
Well.. Technically everything written in Forth is Forth. But it is not
canonical Forth - because if it were canonical Forth, we would have
covered locals in "Starting Forth" - and we didn't.
Now, let's assume we found we were wrong. But there was a chapter in
"Thinking Forth" called "The stylish stack" - not "The stylish locals".
As a matter of fact, it states that "the stack is not an array" -
meaning: not randomly accessible. And what are locals? Right. Randomly
accessible.
So, what is this? It's a feeble imitation of C. It's not part of the
original design. Because if it were part of the original design, you
would find out what it means to think differently. This is merely C
thinking. Nothing else. Certainly not Forth thinking.
LOL ... I admit being a very non-canonical old guy :O)
OK, have another song: Mr. FIFO stating that
"arrays aren't variables" (maybe need a link?).
Where did they taught you that? At that 'college'
of yours, "elite programmer"? :D
On 23-06-2025 20:48, LIT wrote:
OK, have another song: Mr. FIFO stating that
"arrays aren't variables" (maybe need a link?).
Where did they taught you that? At that 'college'
of yours, "elite programmer"? :D
Oh honey, you don't understand? That's not a problem, hon. Go to mummy,
she will explain it to you. But daddy doesn't have time for you. He's talking to the grown ups. Go play with your dolls and be a good girl!
On 24/06/2025 10:42 am, minforth wrote:
On Mon, 23 Jun 2025 21:20:46 +0000, Hans Bezemer wrote:
On 23-06-2025 23:03, minforth wrote:
On Mon, 23 Jun 2025 5:18:34 +0000, Anton Ertl wrote:
minforth@gmx.net (minforth) writes:
So, I made me a small extension to the locals word set. Using your >>>>>> example SPIN (abc — cba), I can define it as follows:
: SPIN { a b c == c b a } ; \ no need for additional code before ;
What is the advantage of using this extension over the Forth-2012:
: spin {: a b c :} c b a ;
?
Obviously, there is no advantage for such small definitions.
For me, the small syntax extension is a convenience when working
with longer definitions. A bit contrived (:= synonym for TO):
: SOME-APP { a f: b c | temp == n: flag z: freq }
\ inputs: integer a, floats b c
\ uninitialized: float temp
\ outputs: integer flag, complex freq
<: FUNC < ... calc function ... > ;>
\ emulated embedded function using { | xt: func }
< ... calc something ... > := temp
< ... calc other things ... > := freq / basic formula
< ... calc other things ... > := flag
< ... calc correction ... > := freq / better estimation
;
While working on such things, I can focus my eyes on the formulas,
all local values are visible in one place, and I don't have to
worry about tracking the data stack(s) for lost/accumulated items.
As I said, it is nothing spectacular, just helpful. And to my own
eyes, it looks neater. ;-)
And before dxf yowls again: it is still Forth. :o)
Well.. Technically everything written in Forth is Forth. But it is not
canonical Forth - because if it were canonical Forth, we would have
covered locals in "Starting Forth" - and we didn't.
Now, let's assume we found we were wrong. But there was a chapter in
"Thinking Forth" called "The stylish stack" - not "The stylish locals".
As a matter of fact, it states that "the stack is not an array" -
meaning: not randomly accessible. And what are locals? Right. Randomly
accessible.
So, what is this? It's a feeble imitation of C. It's not part of the
original design. Because if it were part of the original design, you
would find out what it means to think differently. This is merely C
thinking. Nothing else. Certainly not Forth thinking.
LOL ... I admit being a very non-canonical old guy :O)
'Look, Ma - I've solved Forth's biggest problem.' ;-)
'Look, Ma - I've solved Forth's biggest problem.' ;-)
No really, I'm not kidding. When done properly Forth actually changes
the way you work. Fundamentally. I explained the sensation at the end of
"Why Choose Forth". I've been able to tackle things I would never have
been to tackle with a C mindset. ( https://youtu.be/MXKZPGzlx14 )
Like I always wanted to do a real programming language - no matter how primitive. Now I've done at least a dozen - and that particular trick
seems to get easier by the day.
And IMHO a lot can be traced back to the very simple principles Forth is based upon - like a stack. Or the triad "Execute-Number-Error". Or the dictionary. But also the lessons from ThinkForth.
You'll also find it in my C work. There are a lot more "small functions"
than in your average C program. It works for me like an "inner API". Not
to mention uBasic/4tH - There are plenty of "one-liners" in my
uBasic/4tH programs.
But that train of thought needs to be maintained - and it can only be maintained by submitting to the very philosophy Forth was built upon. I
feel like if I would give in to locals, I'd be back to being an average
C programmer.
I still do C from time to time - but it's not my prime language. For
this reason - and because I'm often just plain faster when using Forth.
It just results in a better program.
The only thing I can say is, "it works for me". And when I sometimes
view the works of others - especially when resorting to a C style - I
feel like it could work for you as well.
Nine times out of ten one doesn't need the amount of locals which are applied. One doesn't need a 16 line word - at least not when you
actually want to maintain the darn thing. One could tackle the problem
much more elegant.
It's that feeling..
On Tue, 24 Jun 2025 11:12:00 +0200
Hans Bezemer <the.beez.speaks@gmail.com> wrote:
On 23-06-2025 20:48, LIT wrote:Please, give up the insults; let your coding skills do the talking.
OK, have another song: Mr. FIFO stating that
"arrays aren't variables" (maybe need a link?).
Where did they taught you that? At that 'college'
of yours, "elite programmer"? :D
Oh honey, you don't understand? That's not a problem, hon. Go to mummy,
she will explain it to you. But daddy doesn't have time for you. He's
talking to the grown ups. Go play with your dolls and be a good girl!
On Tue, 24 Jun 2025 9:30:35 +0000, Hans Bezemer wrote:
'Look, Ma - I've solved Forth's biggest problem.' ;-)
No really, I'm not kidding. When done properly Forth actually changes
the way you work. Fundamentally. I explained the sensation at the end of
"Why Choose Forth". I've been able to tackle things I would never have
been to tackle with a C mindset. ( https://youtu.be/MXKZPGzlx14 )
Like I always wanted to do a real programming language - no matter how
primitive. Now I've done at least a dozen - and that particular trick
seems to get easier by the day.
And IMHO a lot can be traced back to the very simple principles Forth is
based upon - like a stack. Or the triad "Execute-Number-Error". Or the
dictionary. But also the lessons from ThinkForth.
You'll also find it in my C work. There are a lot more "small functions"
than in your average C program. It works for me like an "inner API". Not
to mention uBasic/4tH - There are plenty of "one-liners" in my
uBasic/4tH programs.
But that train of thought needs to be maintained - and it can only be
maintained by submitting to the very philosophy Forth was built upon. I
feel like if I would give in to locals, I'd be back to being an average
C programmer.
I still do C from time to time - but it's not my prime language. For
this reason - and because I'm often just plain faster when using Forth.
It just results in a better program.
The only thing I can say is, "it works for me". And when I sometimes
view the works of others - especially when resorting to a C style - I
feel like it could work for you as well.
Nine times out of ten one doesn't need the amount of locals which are
applied. One doesn't need a 16 line word - at least not when you
actually want to maintain the darn thing. One could tackle the problem
much more elegant.
It's that feeling..
Why make everything so complicated? An electrician's toolbox looks
different from a horse smith's toolbox. You sound like a horse smith
who frowns at the electrician's toolbox and the electrician's “philosophy”.
But to each his own - make love not war. :o)
I'm also puzzled why there is always so emphasis on the "speed" issue. I >mean - if you want speed, do your program in C -O3 so to say. It'll blow
any Forth out of the water.
On Mon, 23 Jun 2025 5:18:34 +0000, Anton Ertl wrote:
minforth@gmx.net (minforth) writes:
So, I made me a small extension to the locals word set. Using your >>>example SPIN (abc — cba), I can define it as follows:
: SPIN { a b c == c b a } ; \ no need for additional code before ;
What is the advantage of using this extension over the Forth-2012:
: spin {: a b c :} c b a ;
?
Obviously, there is no advantage for such small definitions.
For me, the small syntax extension is a convenience when working
with longer definitions. A bit contrived (:= synonym for TO):
: SOME-APP { a f: b c | temp == n: flag z: freq }
\ inputs: integer a, floats b c
\ uninitialized: float temp
\ outputs: integer flag, complex freq
<: FUNC < ... calc function ... > ;>
\ emulated embedded function using { | xt: func }
< ... calc something ... > := temp
< ... calc other things ... > := freq / basic formula
< ... calc other things ... > := flag
< ... calc correction ... > := freq / better estimation
;
...
You'll also find it in my C work. There are a lot more "small functions" than in your average C program. It works for me like an "inner API". Not to mention uBasic/4tH - There are plenty of "one-liners" in my uBasic/4tH programs.
But that train of thought needs to be maintained - and it can only be maintained by submitting to the very philosophy Forth was built upon. I feel like if I would give in to locals, I'd be back to being an average C programmer.
...
Nine times out of ten one doesn't need the amount of locals which are applied. One doesn't need a 16 line word - at least not when you actually want to maintain the darn thing. One could tackle the problem much more elegant.
It's that feeling..
Forth forces an average programmer to adopt a level of organisation
sooner than a locals- based language. I suspect forthers that promote
locals are well aware forth is readable and maintainable but are
pursuing personal agendas of style which requires implying the
opposite.
Fundamentally. I explained the sensation at the end
of "Why Choose Forth". I've been able to tackle things I would never
have been to tackle with a C mindset. ( https://youtu.be/MXKZPGzlx14 )
On 24-06-2025 13:50, minforth wrote:
On Tue, 24 Jun 2025 9:30:35 +0000, Hans Bezemer wrote:
'Look, Ma - I've solved Forth's biggest problem.' ;-)
No really, I'm not kidding. When done properly Forth actually changes
the way you work. Fundamentally. I explained the sensation at the end of >>> "Why Choose Forth". I've been able to tackle things I would never have
been to tackle with a C mindset. ( https://youtu.be/MXKZPGzlx14 )
Like I always wanted to do a real programming language - no matter how
primitive. Now I've done at least a dozen - and that particular trick
seems to get easier by the day.
And IMHO a lot can be traced back to the very simple principles Forth is >>> based upon - like a stack. Or the triad "Execute-Number-Error". Or the
dictionary. But also the lessons from ThinkForth.
You'll also find it in my C work. There are a lot more "small functions" >>> than in your average C program. It works for me like an "inner API". Not >>> to mention uBasic/4tH - There are plenty of "one-liners" in my
uBasic/4tH programs.
But that train of thought needs to be maintained - and it can only be
maintained by submitting to the very philosophy Forth was built upon. I
feel like if I would give in to locals, I'd be back to being an average
C programmer.
I still do C from time to time - but it's not my prime language. For
this reason - and because I'm often just plain faster when using Forth.
It just results in a better program.
The only thing I can say is, "it works for me". And when I sometimes
view the works of others - especially when resorting to a C style - I
feel like it could work for you as well.
Nine times out of ten one doesn't need the amount of locals which are
applied. One doesn't need a 16 line word - at least not when you
actually want to maintain the darn thing. One could tackle the problem
much more elegant.
It's that feeling..
Why make everything so complicated? An electrician's toolbox looks
different from a horse smith's toolbox. You sound like a horse smith
who frowns at the electrician's toolbox and the electrician's
“philosophy”.
Yes, that's why we have languages like Forth and C - each with its own >philosophy.
The point is, I think there is not enough respect for the Forth
philosophy. It's even publicly denied there is such a philosophy at all.
Or that there are actual, measurable benefits to that philosophy.
And that's the core of the issue, I think.
I'm also puzzled why there is always so emphasis on the "speed" issue. I
mean - if you want speed, do your program in C -O3 so to say. It'll blow
any Forth out of the water. Now I know that there are people concerned
with optimizing Python, but I frown on that as well.
I don't mean to say you should close every window to optimization, but
at least admit to one another - it's a secondary problem with Forth.
But to each his own - make love not war. :o)
I'm all for a civil discussion, fully agreed! ;-)
Hans Bezemer
dxf <dxforth@gmail.com> writes:
Forth forces an average programmer to adopt a level of organisation
sooner than a locals- based language. I suspect forthers that promote
locals are well aware forth is readable and maintainable but are
pursuing personal agendas of style which requires implying the
opposite.
IDK, I've seen some unreadable Forth code that was written by experts. >Whether locals could have helped, I don't know.
with. And ditch github (USA) in favour of gitee (Chinese).
USA is circling the drain.
Maybe you are mistaken. I have seen unmaintainable code written by
some self-proclaimed experts.
I have not seen unmaintainable code written by real experts.
dxf <dxforth@gmail.com> writes:
Forth forces an average programmer to adopt a level of organisation
sooner than a locals- based language. I suspect forthers that promote
locals are well aware forth is readable and maintainable but are
pursuing personal agendas of style which requires implying the
opposite.
IDK, I've seen some unreadable Forth code that was written by experts. Whether locals could have helped, I don't know.
No really, I'm not kidding. When done properly Forth actually changes
the way you work. Fundamentally. I explained the sensation at the end of "Why Choose Forth". I've been able to tackle things I would never have
been to tackle with a C mindset. ( https://youtu.be/MXKZPGzlx14 )
Like I always wanted to do a real programming language - no matter how primitive. Now I've done at least a dozen - and that particular trick
seems to get easier by the day.
And IMHO a lot can be traced back to the very simple principles Forth is based upon - like a stack. Or the triad "Execute-Number-Error". Or the dictionary. But also the lessons from ThinkForth.
You'll also find it in my C work. There are a lot more "small functions" than in your average C program. It works for me like an "inner API". Not
to mention uBasic/4tH - There are plenty of "one-liners" in my
uBasic/4tH programs.
But that train of thought needs to be maintained - and it can only be maintained by submitting to the very philosophy Forth was built upon. I
feel like if I would give in to locals, I'd be back to being an average
C programmer.
I still do C from time to time - but it's not my prime language. For
this reason - and because I'm often just plain faster when using Forth.
It just results in a better program.
I still do C from time to time - but it's not my prime language. For
this reason - and because I'm often just plain faster when using Forth.
It just results in a better program.
The only thing I can say is, "it works for me". And when I sometimes
view the works of others - especially when resorting to a C style - I
feel like it could work for you as well.
Nine times out of ten one doesn't need the amount of locals which are applied. One doesn't need a 16 line word - at least not when you
actually want to maintain the darn thing. One could tackle the problem
much more elegant.
Define 'unreadable'. In general I don't need to understand the nitty
gritty of a routine. But should I and no stack commentary exists, I've
no objections to creating it. It's par for the course in Forth. If it bugged me I wouldn't be doing Forth.
My philosophy for developing programs is "follow the problem".
That is we a problem to solve (task to do). We need to
understand it, introduce some data structures and specify
needed computation. This is mostly independent from programming
language. When problem is not well understood we need
to do some research. In this experiments may help a lot
and having interactive programming language in useful
(so this is plus of Forth compared to C). Once we have
data structures and know what computation is needed we
need to encode (represent) this in choosen language.
I would say that large scale structure of the program
will be mostly independent of programming language.
There will be differences at small scale, as different
languages have different idioms. "Builtin" features of
language or "standard" libraries may do significant
part of work. Effort of coding may vary widely,
depending how much is supported by the language and
surroundig ecosystem and how much must be newly
coded. Also, debugging features of programming
system affect speed of coding.
Frankly, I do not see how missing language features
can improve design. I mean, there are people who
try to use fancy features when thay are not needed.
But large scale structure of a program should not be
affected by this. And at smaller scale with some
experience it is not hard to avoid unneeded features.
I would say that there are natural way to approach
given problem and usually best program is one that
follows natural way. Now, if problem naturally needs
several interdependent attributes we need to represnt
them in some way. If dependence is naturaly in stack
way, than stack is a good fit. If dependence is not
naturaly in a stack way, using stack may be possible
after some reorganisation. But may experience is
that if a given structure does not naturally appear
after some research, than reorganisation is not
very likely to lead to such structure. And even if
one mananges to tweak program to such structure, it
is not clear if it is a gain. Anyway, there is substantial
number of problem where stack is unlikely to work in
natural way. So how to represnt attributes? If they
are needed only inside a single function, than natural
way is using local variables. One can use globals, but
for variables that are not needed outside a function
this in unnatural. One can use stack juggling, this
works, but IMO is unnatural. One can collect attributes
in a single structure dynamically allocated at
function entry and freed at exit. This works, but
again is unnatural and needs extra code.
You have some point about length of functions. While
pretty small functions using locals are possible, I
have a few longer functions where main reason for keeping
code in one function is because various parts need access
to the same local variables. But I doubt that eliminating
locals and splitting such functions leads to better code:
we get a cluster of function which depend via common
attibutes.
These are my observations as well. It all depends on the problem
that you are facing. Now there are some guys who behave
like self-declared Forth mullahs who shout heresy against
those who don't DUP ROT enough.
These are my observations as well. It all depends on the problem
that you are facing. Now there are some guys who behave
like self-declared Forth mullahs who shout heresy against
those who don't DUP ROT enough.
I realizad that (fortunately) long ago - actually
Stephen Pelc made me realized that (thanks) - see
the old thread "Vector additon" here:
https://groups.google.com/g/comp.lang.forth/c/m9xy5k5BfkY/m/qoq664B9IygJ
...and in particular this message:
https://groups.google.com/g/comp.lang.forth/c/m9xy5k5BfkY/m/-SIr9AqdiRsJ
dxf <dxforth@gmail.com> writes:
Define 'unreadable'. In general I don't need to understand the nitty
gritty of a routine. But should I and no stack commentary exists, I've
no objections to creating it. It's par for the course in Forth. If it
bugged me I wouldn't be doing Forth.
Unreadable = I look at the code and have no idea what it's doing. The
logic is often obscured by stack manipulation. The values in the stack
are meaningful to the program's operation, but what is the meaning? In
most languages, meaningful values have names, and the names convey the meaning. In Forth, you can write comments for that purpose. Years
after cmForth was published, someone wrote a set of shadow screens for
it, and that helped a lot.
With no named values and no explanatory comments, the program becomes
opaque.
Yet forthers have no problem with this. Take the SwiftForth source code.
At best you'll get a general comment as to what a function does. How do
they maintain it - the same way anyone proficient in C maintains C code.
On 26/06/2025 5:12 pm, Paul Rubin wrote:
dxf <dxforth@gmail.com> writes:
Define 'unreadable'. In general I don't need to understand the nitty
gritty of a routine. But should I and no stack commentary exists, I've
no objections to creating it. It's par for the course in Forth. If it
bugged me I wouldn't be doing Forth.
Unreadable = I look at the code and have no idea what it's doing. The
logic is often obscured by stack manipulation. The values in the stack
are meaningful to the program's operation, but what is the meaning? In
most languages, meaningful values have names, and the names convey the
meaning. In Forth, you can write comments for that purpose. Years
after cmForth was published, someone wrote a set of shadow screens for
it, and that helped a lot.
With no named values and no explanatory comments, the program becomes
opaque.
Yet forthers have no problem with this. Take the SwiftForth source code.
At best you'll get a general comment as to what a function does. How do
they maintain it - the same way anyone proficient in C maintains C code. >Albert is correct. Familiarity is key to readability. That's not to say >code deserving documentation shouldn't have it. OTOH one shouldn't be >expecting documentation (including stack commentary) for what's an everyday >affair in Forth.
Now there are some guys who behave
like self-declared Forth mullahs who shout heresy against
those who don't DUP ROT enough.
Is theirs the Forth philosophy?? Really?? I thought the main
Forth principle was "keep it simple". When stack reordering
is the easier way, do it. When using locals is the easier way,
do it.
The more common complaint is that you use some feature they dislike (typically locals) when you would otherwise DUP ROT instead.
The more common complaint is that you use some feature they dislike
(typically locals) when you would otherwise DUP ROT instead.
But aren't 'locals' actually PICK/ROLL in disguise?
The more common complaint is that you use some feature they dislike
(typically locals) when you would otherwise DUP ROT instead.
But aren't 'locals' actually PICK/ROLL in disguise?
Do PICK/ROLL skim all the values off the stack and stuff them in
variables to be later popped on and off the stack like a yo-yo?
At this point they'll say they've optimized all that away (hopefully
their compiler did). Moore will then tell them it makes no sense to
optimize bad practice when one can employ good practice at the start.
But hey, that'd be interfering with a person's freedom to do as he
pleased.
The more common complaint is that you use some feature they dislike
(typically locals) when you would otherwise DUP ROT instead.
But aren't 'locals' actually PICK/ROLL in disguise?
But aren't 'locals' actually PICK/ROLL in disguise?Do PICK/ROLL skim all the values off the stack and stuff them in
variables to be later popped on and off the stack like a yo-yo?
The more common complaint is that you use some feature they dislike
(typically locals) when you would otherwise DUP ROT instead.
But aren't 'locals' actually PICK/ROLL in disguise?
The more common complaint is that you use some feature they dislike
(typically locals) when you would otherwise DUP ROT instead.
But aren't 'locals' actually PICK/ROLL in disguise?
Do PICK/ROLL skim all the values off the stack and stuff them in
variables to be later popped on and off the stack like a yo-yo?
At this point they'll say they've optimized all that away (hopefully
their compiler did). Moore will then tell them it makes no sense to
optimize bad practice when one can employ good practice at the start.
But hey, that'd be interfering with a person's freedom to do as he
pleased.
This isn't an answer to my question. My question was (and is):
Aren't 'locals' actually PICK/ROLL in disguise?
I'm not against solving problems/tasks the way
the particular programmer prefers. While I'm not
using locals, still I'm not trying to tell anyone
what to do (unless asked about this ofc.).
dxf <dxforth@gmail.com> writes:
But aren't 'locals' actually PICK/ROLL in disguise?Do PICK/ROLL skim all the values off the stack and stuff them in
variables to be later popped on and off the stack like a yo-yo?
Locals can be (and I thought usually are) implemented with the
equivalent of PICK and POST, on either the R stack or a separate L
stack. ROLL is different, "n ROLL" actually shuffles n items around and
in most situations seems kind of nuts.
What is POST ?
I explained how locals differ from PICK/ROLL. In addition there's
no evidence PICK/ROLL is used frequently or held up as a solution.
As you appear to see a connection, I was hoping you would explain it.
I explained how locals differ from PICK/ROLL. In addition there's
no evidence PICK/ROLL is used frequently or held up as a solution.
As you appear to see a connection, I was hoping you would explain it.
Everyone is free to solve their problems
the way they're most comfortable with - be
it bezemerish string of DUP-SWAP-ROTs or
PICK/ROLL.
As for me, I'm convinced by this recommedation:
"Pick and Roll are the generic operators which
treat the data stack as an array. If you find
you need to use them, you are probably doing
it wrong. Look for ways to refactor your code
to be simpler instead."
In a way this statement can be extended to the
use of local variables. Still, as I said, it's
allowed. No need for "too ideological" approach
if that works for the programmer, neither for
insisting on "canonical" ways etc.
In a way this statement can be extended to the
use of local variables. Still, as I said, it's
allowed. No need for "too ideological" approach
if that works for the programmer, neither for
insisting on "canonical" ways etc.
While many a stack op can be simulated using PICK and ROLL (hence
CS-PICK
CS-ROLL) I'm unaware of anyone using them in place of. If stack ops are "canonical" it's because nobody has found a more efficient way of
programming
a stack computer. Recognizing this as the fact and the reality, a
programmer
that's serious will deal with reality and not chase after ideology which
is
the province of thought.
"Pick and Roll are the generic operators which treat the data stack as
an array. If you find you need to use them, you are probably doing it
wrong. Look for ways to refactor your code to be simpler instead."
zbigniew2011@gmail.com (LIT) writes:
"Pick and Roll are the generic operators which treat the data stack as
an array. If you find you need to use them, you are probably doing it
wrong. Look for ways to refactor your code to be simpler instead."
What is the origin of that quote?
[..]
: 3DUP ( a b c -- a b c a b c ) 3 PICK 3 PICK 3 PICK ;
Seems clearer than some mess of ROT and return stack temporaries.
: 3DUP ( a b c -- a b c a b c ) 3 PICK 3 PICK 3 PICK ;
: 3DUP ( a b c -- a b c a b c ) 3 PICK 3 PICK 3 PICK ;
I think it must be 2 in lieu of 3, like this:
: 3DUP ( a b c -- a b c a b c ) 2 PICK 2 PICK 2 PICK ;
Aren't 'locals' actually PICK/ROLL in disguise?
In a way this statement can be extended to the
use of local variables. Still, as I said, it's
allowed. No need for "too ideological" approach
if that works for the programmer, neither for
insisting on "canonical" ways etc.
While many a stack op can be simulated using PICK and ROLL (hence
CS-PICK
CS-ROLL) I'm unaware of anyone using them in place of. If stack ops are
"canonical" it's because nobody has found a more efficient way of
programming
a stack computer. Recognizing this as the fact and the reality, a
programmer
that's serious will deal with reality and not chase after ideology which
is
the province of thought.
The stack ops THEMSELVES may be, in a way,
"canonical" — but not solving "each and every"
programming task using them "no-matter-what", IMHO.
The stack ops THEMSELVES may be, in a way,
"canonical" — but not solving "each and every"
programming task using them "no-matter-what", IMHO.
But such would indicate a deficiency in Forth. Do C programmers reach a point at which they can't go forward?
Hans Bezemer <the.beez.speaks@gmail.com> writes:
I'm also puzzled why there is always so emphasis on the "speed" issue. I
mean - if you want speed, do your program in C -O3 so to say. It'll blow
any Forth out of the water.
Take a look at the bubble benchmark in Figure 1 of <https://www.complang.tuwien.ac.at/papers/ertl24-interpreter-speed.pdf>. SwiftForth, VFX, and Gforth with all optimizations (the baseline) are
faster than gcc-12 -O3. The reason for that is:
|For bubble, gcc -O3 auto-vectorizes, and the result is that there is |partial overlap between a store and a following load, which results
|in the hardware taking a slow path rather than performing one of its |store-to-load forwarding optimizations.
- anton
Hans Bezemer <the.beez.speaks@gmail.com> writes:
Fundamentally. I explained the sensation at the end
of "Why Choose Forth". I've been able to tackle things I would never
have been to tackle with a C mindset. ( https://youtu.be/MXKZPGzlx14 )
I just watched this video and enjoyed it, but I don't understand how a C mindset is different. In C you pass stuff as function parameters
instead of on the stack: what's the big deal? And particularly, the
video said nothing about the burning question of locals ;).
It seems to me all the examples mentioned in the video (parsing CSV
files or floating point numerals) are what someone called
micro-problems. Today they much easier with languages like Python, and
back in Forth's heyday there was Lisp, which occupied a mindspace like
Python does now.
I agree that Thinking Forth is a great book.
albert@spenarnc.xs4all.nl writes:
with. And ditch github (USA) in favour of gitee (Chinese).
USA is circling the drain.
Try codeberg.org (Germany) maybe.
On 25-06-2025 09:21, Paul Rubin wrote:
Hans Bezemer <the.beez.speaks@gmail.com> writes:
Fundamentally. I explained the sensation at the end
of "Why Choose Forth". I've been able to tackle things I would never
have been to tackle with a C mindset. ( https://youtu.be/MXKZPGzlx14 )
I just watched this video and enjoyed it, but I don't understand how a C
mindset is different. In C you pass stuff as function parameters
instead of on the stack: what's the big deal? And particularly, the
video said nothing about the burning question of locals ;).
It seems to me all the examples mentioned in the video (parsing CSV
files or floating point numerals) are what someone called
micro-problems. Today they much easier with languages like Python, and
back in Forth's heyday there was Lisp, which occupied a mindspace like
Python does now.
I agree that Thinking Forth is a great book.
It's hard to illustrate things with a multi-KLOC program IMHO. You can
only illustrate principles by using examples that are "contained" in a way.
But I'll try to illustrate a thing or two. Let's say you want to tackle
a problem. And it doesn't go your way. You have to add this thing and
that thing - and hold on to that value. You know what I mean.
about to add. Take a look at getopt() - I think that's a good example.
You can almost see how it grew almost organically by the authors hand.
He never seemed to think "Hmm, maybe I'll make a separate function of it".
The stack ops THEMSELVES may be, in a way, "canonical" — but not
solving "each and every" programming task using them
"no-matter-what", IMHO.
But such would indicate a deficiency in Forth. Do C programmers reach a point at which they can't go forward?
The PDP-11 and 8086 had 8 registers and
programmers found that to be painful.
The stack ops THEMSELVES may be, in a way,
"canonical" — but not solving "each and every"
programming task using them "no-matter-what", IMHO.
But such would indicate a deficiency in Forth. Do C programmers reach a
point at which they can't go forward?
You may want to raise your question on comp.lang.c
Yet forthers have no problem with this. Take the SwiftForth source code.
At best you'll get a general comment as to what a function does. How do
they maintain it - the same way anyone proficient in C maintains C code. Albert is correct. Familiarity is key to readability. That's not to say code deserving documentation shouldn't have it. OTOH one shouldn't be expecting documentation (including stack commentary) for what's an everyday affair in Forth.
On 24-06-2025 18:23, Anton Ertl wrote:
Hans Bezemer <the.beez.speaks@gmail.com> writes:
I'm also puzzled why there is always so emphasis on the "speed" issue. I >>> mean - if you want speed, do your program in C -O3 so to say. It'll blow >>> any Forth out of the water.
Still, in general - GCC beats Forth. Although I have to admit I've got a renewed respect for VFX Forth! Kudos!
In article <nnd$7a5cfad1$76518ba7@2a4e6190d58511e2>,
Hans Bezemer <the.beez.speaks@gmail.com> wrote:
On 25-06-2025 09:21, Paul Rubin wrote:
Hans Bezemer <the.beez.speaks@gmail.com> writes:
Fundamentally. I explained the sensation at the end
of "Why Choose Forth". I've been able to tackle things I would never
have been to tackle with a C mindset. ( https://youtu.be/MXKZPGzlx14 )
I just watched this video and enjoyed it, but I don't understand how a C >>> mindset is different. In C you pass stuff as function parameters
instead of on the stack: what's the big deal? And particularly, the
video said nothing about the burning question of locals ;).
It seems to me all the examples mentioned in the video (parsing CSV
files or floating point numerals) are what someone called
micro-problems. Today they much easier with languages like Python, and
back in Forth's heyday there was Lisp, which occupied a mindspace like
Python does now.
I agree that Thinking Forth is a great book.
It's hard to illustrate things with a multi-KLOC program IMHO. You can
only illustrate principles by using examples that are "contained" in a way. >>
But I'll try to illustrate a thing or two. Let's say you want to tackle
a problem. And it doesn't go your way. You have to add this thing and
that thing - and hold on to that value. You know what I mean.
about to add. Take a look at getopt() - I think that's a good example.
You can almost see how it grew almost organically by the authors hand.
He never seemed to think "Hmm, maybe I'll make a separate function of it".
getopt is a design error in Forth filosofy. You are writing an interpreter and Forth is the only interpreter, first commandment.
dxf <dxforth@gmail.com> writes:
The stack ops THEMSELVES may be, in a way, "canonical" — but not
solving "each and every" programming task using them
"no-matter-what", IMHO.
But such would indicate a deficiency in Forth. Do C programmers reach a
point at which they can't go forward?
Assembly language programmers reach a point where they run out of
machine registers and have to do clumsy things to swap stuff between registers and memory. C compilers automate that process. Every C
compiler with register allocation has to deal with register spilling.
The programmer doesn't have to deal with it, but it's similar clumsy
assembly code coming out of the compiler.
In Forth without using locals, "register allocation" (deciding what is
in each stack slot) is manual and there are fewer "registers" to begin
with (basically TOS, NOS, TOR, and the 3rd stack element that you can
reach with ROT). Modern CPUs by comparison generally have 16 or more addressible registers. The PDP-11 and 8086 had 8 registers and
programmers found that to be painful.
zbigniew2011@gmail.com (LIT) writes:
"Pick and Roll are the generic operators which treat the data stack as
an array. If you find you need to use them, you are probably doing it
wrong. Look for ways to refactor your code to be simpler instead."
What is the origin of that quote? PICK treats the stack like an array,
but ROLL treats it more as a circular shift register.
Most CPUs these days have a register file, which is essentially an array
with only immediate-like addressing mode. Presumably that design
evolved because programmers found it useful.
PICK afaict is mostly used with literal offsets as well. Having a
variable offset is suspicious.
: 3DUP ( a b c -- a b c a b c ) 3 PICK 3 PICK 3 PICK ;
Seems clearer than some mess of ROT and return stack temporaries.
r over over r@ rot rot r>$
On 30 Jun 2025 at 15:57:28 CEST, "Hans Bezemer" <the.beez.speaks@gmail.com> wrote:
On 24-06-2025 18:23, Anton Ertl wrote:
Hans Bezemer <the.beez.speaks@gmail.com> writes:
I'm also puzzled why there is always so emphasis on the "speed" issue. I >>>> mean - if you want speed, do your program in C -O3 so to say. It'll blow >>>> any Forth out of the water.
One of our clients makes a construction estimating package
https://www.rib-software.com/en/rib-candy
When ported from MPE's last threaded-code Forth to the early VFX Forth, screen redraw for the plan of part of a very large building improved by a factor
of ten. The client was very pleased - this was a visible change for their users.
I have also found that fast code enables me not to use progrmming tricks, but to code for readability and the maintenance programmer. I'm still maintaining code I first saw 40 years ago - not much of it, but always a pain to maintain.
Still, in general - GCC beats Forth. Although I have to admit I've got a
renewed respect for VFX Forth! Kudos!
Thanks ... blush.
Compiler output performance depends very much on the amunt of time and
money spent on developing it. The VFX code generator was part of the
planned output of an EU ESPRIT project.
Stephen
I just watched this video and enjoyed it, but I don't understand how a C mindset is different. In C you pass stuff as function parameters
instead of on the stack: what's the big deal? And particularly, the
video said nothing about the burning question of locals ;).
Frankly, I do not see how missing language features
can improve design.
Another great argument to leave Forth and embrace C! Why painfullyBut such would indicate a deficiency in Forth. Do C programmers reach a >>> point at which they can't go forward? ...
create kludge to cram into a language that was clearly not created for
that when you have a language available that was actually DESIGNED
with those requirements in mind?!
In a traditional Forth with locals, the locals are stack allocated so accessing them usually costs a memory reference. The programmer gets
the same convenience as a C programmer. The runtime takes a slowdown compared to code from a register-allocating compiler, but such a
slowdown is already present in a threaded interpreter, so it's fine.
Nobody seems to care about that time. Instead, the focus seems to be primarily on code runtime, even though the difference is only
microseconds or less.
The PDP-11 and 8086 had 8 registers and
programmers found that to be painful.
Eight registers "painful"? Then how would you
describe 6502 and its one plus two half-registers?
:D
----
Hans Bezemer <the.beez.speaks@gmail.com> writes:
Another great argument to leave Forth and embrace C! Why painfullyBut such would indicate a deficiency in Forth. Do C programmers
reach a point at which they can't go forward? ...
create kludge to cram into a language that was clearly not created
for that when you have a language available that was actually
DESIGNED with those requirements in mind?!
I'm not sure what you're getting at here, though I see the sarcasm.
Is the kludge locals? They don't seem that kludgy to me.
Implementing them in Forth is straightforward and lots of people have
done it.
The point where one can't go forward is basically "running out of
registers". In assembly language those are the machine registers, and
in Forth they're the top few stack slots. In both cases, when you run
out, you have to resort to contorted code.
In C that isn't a problem for the programmer. You can use as many
variables as you like, and if the compiler runs out of registers and
has to make contorted assembly code, it does so without your having
to care.
In a traditional Forth with locals, the locals are stack allocated so accessing them usually costs a memory reference. The programmer gets
the same convenience as a C programmer. The runtime takes a slowdown compared to code from a register-allocating compiler, but such a
slowdown is already present in a threaded interpreter, so it's fine.
Finally, a fancy enough Forth compiler can do the same things that a C compiler does. Those compilers are difficult to write, but they exist
(VFX, lxf, etc.). I don't know if locals make writing the compiler
more difficult. But the user shouldn't have to care.
On Tue, 01 Jul 2025 11:40:38 -0700
Paul Rubin <no.email@nospam.invalid> wrote:
Hans Bezemer <the.beez.speaks@gmail.com> writes:
Another great argument to leave Forth and embrace C! Why painfullyBut such would indicate a deficiency in Forth. Do C programmers
reach a point at which they can't go forward? ...
create kludge to cram into a language that was clearly not created
for that when you have a language available that was actually
DESIGNED with those requirements in mind?!
I'm not sure what you're getting at here, though I see the sarcasm.
Is the kludge locals? They don't seem that kludgy to me.
Implementing them in Forth is straightforward and lots of people have
done it.
Finally, a fancy enough Forth compiler can do the same things that a C
compiler does. Those compilers are difficult to write, but they exist
(VFX, lxf, etc.). I don't know if locals make writing the compiler
more difficult. But the user shouldn't have to care.
The code generator in lxf has no knowledge of what a local is.
locals are conceptually placed on the return stack. lxf is as smart
about the return stack as the data stack. that is why it can produce
very efficient code for simple examples like 3DUP. The actual
implementation of local in the interpreter is just a few lines of code.
The difference with locals will be seen when you have a boundary block,
IF statement, a call etc that require a known state of the stacks.
The real problem for me with locals is that their scope is to the end
of the definition. With the stack you end the scope of an item with a
drop and extend it with a dup, very elegant!
A multipass compiler can of course find the scope of each local but at
the cost of more complexity.
In lxf64 I have introduced a local stack with the same capabilities as
the data and return stack. I am not sure yet if this is better.
The nice thing is that I now have >ls ls> and ls@. Compared with the
return stack this also works across words. One word can put stuff on
the localstack and another retrieve it. This is sometimes very useful.
minforth@gmx.net (minforth) writes:
Nobody seems to care about that time. Instead, the focus seems to be
primarily on code runtime, even though the difference is only
microseconds or less.
I think in the Moore era, you got two speedups: 1) interpreted Forth was
10x faster than its main competitor, interpreted BASIC; and 2) if your
Forth program was still too slow, you'd identify a few hot spots and
rewrite those in assembler.
Today instead of BASIC we have Python, and interpreted Forth is still a
lot faster than Python. That speed is sufficient for most things, like
it always was, but even more so on modern hardware.
On 27-06-2025 03:39, dxf wrote:
Yet forthers have no problem with this. Take the SwiftForth source code. >> At best you'll get a general comment as to what a function does. How do
they maintain it - the same way anyone proficient in C maintains C code.
Albert is correct. Familiarity is key to readability. That's not to say >> code deserving documentation shouldn't have it. OTOH one shouldn't be
expecting documentation (including stack commentary) for what's an everyday >> affair in Forth.
I think you and Albert are on the right track here. Familiarity is a large part of this "readability" thingy. There are a few notes I want to add, though:
1. "Infix notation" is part of this familiarity. I know I've commented every single expression in TEONW, since I understand those "infix" expressions much better than all those RPN thingies - and you got something to check your code against;
2. Intentionality. I do this a LOT. E.g. if you find OVER OVER in my code, you may be certain those two items have nothing to do with each other. If you find 2DUP it's a string, a double number or another "addr/count" array. CHOP replaces 1 /STRING. Also: stack patterns can be codified like SPIN or STOW;
3. Brevity. Short definitions are easier to understand. If you can abstract it, put a name of it can spare the performance - split it up.
4. Naming. I give this a LOT of thought. I prefer reading a name and having a pretty good idea of what that code does (especially in the context of a library or a program). See: https://sourceforge.net/p/forth-4th/wiki/What%27s%20in%20a%20name%3F/
Feel free to disagree. It may not work for you, but at least it works for me.
Am 01.07.2025 um 21:56 schrieb Paul Rubin:
minforth@gmx.net (minforth) writes:
Nobody seems to care about that time. Instead, the focus seems to be
primarily on code runtime, even though the difference is only
microseconds or less.
I think in the Moore era, you got two speedups: 1) interpreted Forth was
10x faster than its main competitor, interpreted BASIC; and 2) if your
Forth program was still too slow, you'd identify a few hot spots and
rewrite those in assembler.
Today instead of BASIC we have Python, and interpreted Forth is still a
lot faster than Python. That speed is sufficient for most things, like
it always was, but even more so on modern hardware.
Today, you could go insane if you had to write assembler code
with SSE1/2/3/4/AVX/AES etc. extended CPU commands (or take GPU programming...)
Even chip manufacturers provide C libraries with built-ins and
intrinsics to handle this complexity, and optimising C compilers
for selecting the best operations.
IMO assembler programming in Forth is mostly for retro enthusiasts
Am 01.07.2025 um 21:56 schrieb Paul Rubin:
minforth@gmx.net (minforth) writes:
Nobody seems to care about that time. Instead, the focus seems to be
primarily on code runtime, even though the difference is only
microseconds or less.
I think in the Moore era, you got two speedups: 1) interpreted Forth was
10x faster than its main competitor, interpreted BASIC; and 2) if your
Forth program was still too slow, you'd identify a few hot spots and
rewrite those in assembler.
Today instead of BASIC we have Python, and interpreted Forth is still a
lot faster than Python. That speed is sufficient for most things, like
it always was, but even more so on modern hardware.
Today, you could go insane if you had to write assembler code
with SSE1/2/3/4/AVX/AES etc. extended CPU commands (or take GPU programming...)
Even chip manufacturers provide C libraries with built-ins and
intrinsics to handle this complexity, and optimising C compilers
for selecting the best operations.
IMO assembler programming in Forth is mostly for retro enthusiasts
For me, the small syntax extension is a convenience when working
with longer definitions. A bit contrived (:= synonym for TO):
: SOME-APP { a f: b c | temp == n: flag z: freq }
\ inputs: integer a, floats b c
\ uninitialized: float temp
\ outputs: integer flag, complex freq
<: FUNC < ... calc function ... > ;>
\ emulated embedded function using { | xt: func }
< ... calc something ... > := temp
< ... calc other things ... > := freq / basic formula
< ... calc other things ... > := flag
< ... calc correction ... > := freq / better estimation
;
On 2025-06-24 01:03, minforth wrote:Are you sure? gforth test:
[...]
For me, the small syntax extension is a convenience when working
with longer definitions. A bit contrived (:= synonym for TO):
: SOME-APP { a f: b c | temp == n: flag z: freq }
\ inputs: integer a, floats b c
\ uninitialized: float temp
\ outputs: integer flag, complex freq
<: FUNC < ... calc function ... > ;>
BTW, why do you prefer the special syntax `<: ... ;>`
over an extension to the existing words `:` and `;`
: SOME-APP
[ : FUNC < ... calc function ... > ; ]
< ... >
;
In this approach the word `:` knows that it's a nested definition and behaves accordingly.
Backtrace:app<<<
On 1/07/2025 10:22 pm, Hans Bezemer wrote:
On 27-06-2025 03:39, dxf wrote:
Yet forthers have no problem with this. Take the SwiftForth source code. >>> At best you'll get a general comment as to what a function does. How do >>> they maintain it - the same way anyone proficient in C maintains C code. >>> Albert is correct. Familiarity is key to readability. That's not to say >>> code deserving documentation shouldn't have it. OTOH one shouldn't be
expecting documentation (including stack commentary) for what's an everyday >>> affair in Forth.
I think you and Albert are on the right track here. Familiarity is a large >> part of this "readability" thingy. There are a few notes I want to add,
though:
1. "Infix notation" is part of this familiarity. I know I've commented every >> single expression in TEONW, since I understand those "infix" expressions much
better than all those RPN thingies - and you got something to check your code
against;
2. Intentionality. I do this a LOT. E.g. if you find OVER OVER in my code, >> you may be certain those two items have nothing to do with each other. If you
find 2DUP it's a string, a double number or another "addr/count" array. CHOP >> replaces 1 /STRING. Also: stack patterns can be codified like SPIN or STOW; >>
3. Brevity. Short definitions are easier to understand. If you can abstract >> it, put a name of it can spare the performance - split it up.
4. Naming. I give this a LOT of thought. I prefer reading a name and having a
pretty good idea of what that code does (especially in the context of a
library or a program). See:
https://sourceforge.net/p/forth-4th/wiki/What%27s%20in%20a%20name%3F/
Feel free to disagree. It may not work for you, but at least it works for me.
Recently someone told me about Christianity - how it wasn't meant to be easy -
supposed to be, among other things, a denial of the senses. I'm hearing much the same in Forth. That it's a celibate practice in which one denies everyday
sensory pleasures including readability and maintainability in order to achieve
programming nirvana. Heck, if that's how folks see Forth then perhaps they should stop before the cognitive dissonance sends them crazy or they pop a cork.
I had a beef with Andrew Tanenbaum, stating that it is hard
to write a c-compiler for the 6502. In reality the 6502
is a brilliant design.
Am 02.07.2025 um 10:53 schrieb Ruvim:
On 2025-06-24 01:03, minforth wrote:Are you sure? gforth test:
[...]
For me, the small syntax extension is a convenience when working
with longer definitions. A bit contrived (:= synonym for TO):
: SOME-APP { a f: b c | temp == n: flag z: freq }
\ inputs: integer a, floats b c
\ uninitialized: float temp
\ outputs: integer flag, complex freq
<: FUNC < ... calc function ... > ;>
BTW, why do you prefer the special syntax `<: ... ;>`
over an extension to the existing words `:` and `;`
: SOME-APP
[ : FUNC < ... calc function ... > ; ]
< ... >
;
In this approach the word `:` knows that it's a nested definition and
behaves accordingly.
: APP 1 [ : func 2 ; ] func ; ok
app
*the terminal*:2:1: error: Invalid memory address
Hans Bezemer <the.beez.speaks@gmail.com> writes:
I'm not sure what you're getting at here, though I see the sarcasm.
Is the kludge locals? They don't seem that kludgy to me. Implementing
them in Forth is straightforward and lots of people have done it.
The point where one can't go forward is basically "running out of
registers". In assembly language those are the machine registers, and
in Forth they're the top few stack slots. In both cases, when you run
out, you have to resort to contorted code.
In C that isn't a problem for the programmer. You can use as many
variables as you like, and if the compiler runs out of registers and has
to make contorted assembly code, it does so without your having to care.
In a traditional Forth with locals, the locals are stack allocated so accessing them usually costs a memory reference. The programmer gets
the same convenience as a C programmer. The runtime takes a slowdown compared to code from a register-allocating compiler, but such a
slowdown is already present in a threaded interpreter, so it's fine.
Finally, a fancy enough Forth compiler can do the same things that a C compiler does. Those compilers are difficult to write, but they exist
(VFX, lxf, etc.). I don't know if locals make writing the compiler more difficult. But the user shouldn't have to care.
On Tue, 1 Jul 2025 18:40:38 +0000, Paul Rubin wrote:
In a traditional Forth with locals, the locals are stack allocated so
accessing them usually costs a memory reference. The programmer gets
the same convenience as a C programmer. The runtime takes a slowdown
compared to code from a register-allocating compiler, but such a
slowdown is already present in a threaded interpreter, so it's fine.
In all this strange discussion about the ôpure and trueö Forth
philosophy (on which even Charles Moore once went his own way), the
human cost of programming time never comes up.
Nobody seems to care about that time. Instead, the focus seems to be primarily on code runtime, even though the difference is only
microseconds or less.
This is completely nonsensical for 99% of all cases. Some people seem
to prefer ôon principleö to help the computer with human work through ôpremature optimizationö to get its stack elements in the right order,
and to factorize the programming task into digestible chunks, whether
it is natural for the task or not. In a professional environment, this stubborn attitude is completely uneconomical.
In my world, using locals in appropriate cases gets me done much faster, error-free, and the code is self-documenting. This time gain is
astronomical when you put it in relation to microseconds of runtime difference.
--
On 2025-06-24 01:03, minforth wrote:
[...]
For me, the small syntax extension is a convenience when working
with longer definitions. A bit contrived (:= synonym for TO):
: SOME-APP { a f: b c | temp == n: flag z: freq }
\ inputs: integer a, floats b c
\ uninitialized: float temp
\ outputs: integer flag, complex freq
<: FUNC < ... calc function ... > ;>
BTW, why do you prefer the special syntax `<: ... ;>`
over an extension to the existing words `:` and `;`
: SOME-APP
[ : FUNC < ... calc function ... > ; ]
< ... >
;
In this approach the word `:` knows that it's a nested definition and
behaves accordingly.
Ruvim
In article <1042s2o$3d58h$1@dont-email.me>,
Ruvim <ruvim.pinka@gmail.com> wrote:
On 2025-06-24 01:03, minforth wrote:
[...]
For me, the small syntax extension is a convenience when working
with longer definitions. A bit contrived (:= synonym for TO):
: SOME-APP { a f: b c | temp == n: flag z: freq }
\ inputs: integer a, floats b c
\ uninitialized: float temp
\ outputs: integer flag, complex freq
<: FUNC < ... calc function ... > ;>
BTW, why do you prefer the special syntax `<: ... ;>`
over an extension to the existing words `:` and `;`
: SOME-APP
[ : FUNC < ... calc function ... > ; ]
< ... >
;
In this approach the word `:` knows that it's a nested definition and
behaves accordingly.
Or it has not even know it, if [ is smart enough to compile a jump to
after ].
On 2025-07-02 15:37, albert@spenarnc.xs4all.nl wrote:
Or it has not even know it, if [ is smart enough to compile a jump to
after ].
This can be tricky because the following should work:
create foo [ 123 , ] [ 456 ,
: bar [ ' foo compile, 123 lit, ] ;
And just before you're done you put your stuff on the stack and like a
tiny assembly line it is transported to the next thing. This means that
the function call overhead is MINIMAL - much less than C.
And that's not the solution - it's the PROBLEM. You can add loads of >complexity without much (immediate) penalty. You're not compelled to
study - or even *think* about your algorithm. You most probably will end
up with code that works - without you understanding why.
And that will either bite you later, or limit your capability to expend
on that code.
Today, you could go insane if you had to write assembler code
with SSE1/2/3/4/AVX/AES etc. extended CPU commands (or take GPU >programming...)
Even chip manufacturers provide C libraries with built-ins and
intrinsics to handle this complexity, and optimising C compilers
for selecting the best operations.
On 2025-07-02 13:02, minforth wrote:
Am 02.07.2025 um 10:53 schrieb Ruvim:
On 2025-06-24 01:03, minforth wrote:Are you sure? gforth test:
[...]
For me, the small syntax extension is a convenience when working
with longer definitions. A bit contrived (:= synonym for TO):
: SOME-APP { a f: b c | temp == n: flag z: freq }
\ inputs: integer a, floats b c
\ uninitialized: float temp
\ outputs: integer flag, complex freq
<: FUNC < ... calc function ... > ;>
BTW, why do you prefer the special syntax `<: ... ;>`
over an extension to the existing words `:` and `;`
: SOME-APP
[ : FUNC < ... calc function ... > ; ]
< ... >
;
In this approach the word `:` knows that it's a nested definition and
behaves accordingly.
: APP 1 [ : func 2 ; ] func ; ok
app
*the terminal*:2:1: error: Invalid memory address
This is not standard, just like `<: ;>`
My question is why did you introduce `<:` and `;>` instead of extending
the `:` and `;` behavior?
Ruvim <ruvim.pinka@gmail.com> writes:
On 2025-07-02 15:37, albert@spenarnc.xs4all.nl wrote:
Or it has not even know it, if [ is smart enough to compile a jump to
after ].
This can be tricky because the following should work:
create foo [ 123 , ] [ 456 ,
: bar [ ' foo compile, 123 lit, ] ;
The benefit of defining a normal colon definition inside another colon definition eludes me, however. Maybe mutual recursion, but the need
is rare and deferred words handle that well.
Am 01.07.2025 um 23:47 schrieb peter:
In lxf64 I have introduced a local stack with the same capabilities as
the data and return stack. I am not sure yet if this is better.
The nice thing is that I now have >ls ls> and ls@. Compared with the
return stack this also works across words. One word can put stuff on
the localstack and another retrieve it. This is sometimes very useful.
In a sense, such locals become global. I am not sure if this opens the
way inadvertently for hard-to-detect bugs.
Only one useful application comes to my mind: sharing locals between >quotation and its parent function, i.e. for creating closures.
But who
needs thema anyway?
Ruvim <ruvim.pinka@gmail.com> writes:
On 2025-07-02 15:37, albert@spenarnc.xs4all.nl wrote:
Or it has not even know it, if [ is smart enough to compile a jump to
after ].
This can be tricky because the following should work:
create foo [ 123 , ] [ 456 ,
: bar [ ' foo compile, 123 lit, ] ;
Or something. Anyway, [ and ] are used for a variety of purposes and
trying to smarten them seems fraught with pitfalls. If one really
wants to have
: foo ... [ : bar ... ; ] ... ;
work, it may be better to put the smarts into : and ;. E.g., on a
system with sections, they could switch to another section and back.
The benefit of defining a normal colon definition inside another colon definition eludes me, however. Maybe mutual recursion, but the need
is rare and deferred words handle that well.
Forth was designed for threaded interpreter implementation and the whole >notion of an optimizing Forth compiler is at best an abstraction
inversion.
But, supposedly, VFX compiler output runs 10x as fast as
the same code under an interpreter.
I think in the Moore era, you got two speedups: 1) interpreted Forth was
10x faster than its main competitor, interpreted BASIC
So I don't see much legitimate complaint about slowdowns due to Forth
locals. The objection is based on other considerations, either
legitimate ones that I don't yet understand, or essentially bogus ones
that I don't completely see through.
And that's not the solution - it's the PROBLEM. You can add loads of
complexity without much (immediate) penalty. You're not compelled to
study - or even *think* about your algorithm. You most probably will end
up with code that works - without you understanding why.
And that will either bite you later, or limit your capability to expend
on that code.
Yes, you can expend a lot of effort on code that's hard to write and
hard to understand, but that's not limited to Forth.
If you mean that, by making code hard to write, Forth without locals
makes it easier to extend the code, I very much doubt it. In some
cases it may not be harder, but in others (where the extension
requires, e.g., dealing with additional data in existing colon
definitions) it is harder.
I had a beef with Andrew Tanenbaum, stating that it is hard to write a c-compiler for the 6502. In reality the 6502 is a brilliant
design. You must realize that the 6502 has 128 16 bit registers on the
zero page.
The nice thing is that I now have >ls ls> and ls@. Compared with the
return stack this also works across words. One word can put stuff on
the localstack and another retrieve it. This is sometimes very useful.
Those who have a Forth system that implements locals don't object toOooh - I've seen a *LOT* of bad and ill-informed arguments on c.l.f.
the use of locals, those whose Forth system does not implement them,
do. Looks like the objections are sour-grapes arguments.
1. Adding general locals is trivial. It takes just one single line of
Forth.
Hans Bezemer <the.beez.speaks@gmail.com> writes:
1. Adding general locals is trivial. It takes just one single line of
Forth.
I don't see how to do it in one line, and trivial is a subjective term.
I'd say in any case that it's not too difficult, but one line seems overoptimistic. Particularly, you need something like (LOCAL) in the
VM. The rest is just some extensions to the colon compiler. Your
mention of it taking 3-4 screens sounded within reason to me, and I
don't consider that to be a lot of code.
On 2025-07-02 19:12, Anton Ertl wrote:
The benefit of defining a normal colon definition inside another colon
definition eludes me, however. Maybe mutual recursion, but the need
is rare and deferred words handle that well.
As I can see, the idea is that the name of a nested definition has the >limited scope — the same as a local variable, and it is not visible >outside of the containing definition.
Backtrace:foo1<<<
1. Adding general locals is trivial. It takes just one single line of
Forth. Sure, you don't got the badly designed and much too heavy
Forth-2012 implementation,
4tH v3.64.2 will even support a *MUCH* lighter, but
fully conformant Forth-2012 LOCALS implementation.
If anything, yours is a prime
example of a "sour grape argument".
Hans Bezemer <the.beez.speaks@gmail.com> writes:
1. Adding general locals is trivial. It takes just one single line of
Forth. Sure, you don't got the badly designed and much too heavy
Forth-2012 implementation,
There is no Forth-2012 implementation of locals. The proposal
includes a referece implementation, but that is based on a
non-standard word BUILDLV and is therefore not included in <http://www.forth200x.org/reference-implementations/>;
instead, you
find there two implementations written in Forth-94:
http://www.forth200x.org/reference-implementations/locals.fs http://www.forth200x.org/reference-implementations/extended-locals.fs
Of these two the locals.fs implementation is the shorter and nicer
one. You can read about these two implementations in <2021Mar3.171350@mips.complang.tuwien.ac.at>.
However, looking at
<https://forth-standard.org/standard/locals/bColon>, it seems that the
editor included a variation of extensed-locals.fs.
4tH v3.64.2 will even support a *MUCH* lighter, but
fully conformant Forth-2012 LOCALS implementation.
Great! How good that Forth-2012 is not an implementation standard.
If anything, yours is a prime
example of a "sour grape argument".
Which grapes do you suppose that I am unable to reach?
Hans Bezemer <the.beez.speaks@gmail.com> writes:
1. Adding general locals is trivial. It takes just one single line of
Forth.
I don't see how to do it in one line, and trivial is a subjective term.
I'd say in any case that it's not too difficult, but one line seems overoptimistic. Particularly, you need something like (LOCAL) in the
VM. The rest is just some extensions to the colon compiler. Your
mention of it taking 3-4 screens sounded within reason to me, and I
don't consider that to be a lot of code.
Hans Bezemer <the.beez.speaks@gmail.com> writes:
1. Adding general locals is trivial. It takes just one single line of
Forth.
I don't see how to do it in one line, and trivial is a subjective term.
I'd say in any case that it's not too difficult, but one line seems >overoptimistic. Particularly, you need something like (LOCAL) in the
VM. The rest is just some extensions to the colon compiler. Your
mention of it taking 3-4 screens sounded within reason to me, and I
don't consider that to be a lot of code.
Am 03.07.2025 um 01:59 schrieb Paul Rubin:
Hans Bezemer <the.beez.speaks@gmail.com> writes:
1. Adding general locals is trivial. It takes just one single line of
Forth.
I don't see how to do it in one line, and trivial is a subjective term.
I'd say in any case that it's not too difficult, but one line seems
overoptimistic. Particularly, you need something like (LOCAL) in the
VM. The rest is just some extensions to the colon compiler. Your
mention of it taking 3-4 screens sounded within reason to me, and I
don't consider that to be a lot of code.
I would not implement locals for simple integers only. Forth has enough
stack gymnastics words for that.
IMO locals only make sense if you can at least additionally handle
floats and dynamic strings, preferably also structs and arrays.
Such an implementation is certainly not trivial.
On 03-07-2025 01:59, Paul Rubin wrote:
Hans Bezemer <the.beez.speaks@gmail.com> writes:
1. Adding general locals is trivial. It takes just one single line of
Forth.
I don't see how to do it in one line, and trivial is a subjective term.
I'd say in any case that it's not too difficult, but one line seems
overoptimistic. Particularly, you need something like (LOCAL) in the
VM. The rest is just some extensions to the colon compiler. Your
mention of it taking 3-4 screens sounded within reason to me, and I
don't consider that to be a lot of code.
"Short" in my dictionary is. One. Single. Screen. No more. No less (pun >intended).
And this one is one single screen. Even with the dependencies. >https://youtu.be/FH4tWf9vPrA
Typical use:
variable a
variable b
: divide
local a
local b
b ! a ! a @ b @ / ;
Does recursion, the whole enchilada. One line.
Thanks to Fred Behringer - and Albert, who condensed it to a single
single line definition. Praise is where praise is due.
Hans Bezemer
peter <peter.noreply@tin.it> writes:
The nice thing is that I now have >ls ls> and ls@. Compared with the
return stack this also works across words. One word can put stuff on
the localstack and another retrieve it. This is sometimes very useful.
As I remember, Flashforth also has a 3rd stack like that, without having >locals. It's called P so you have >P etc.
S S> S@ (apart from LOCAL stacks).
On 02-07-2025 18:41, Anton Ertl wrote:
Those who have a Forth system that implements locals don't object toOooh - I've seen a *LOT* of bad and ill-informed arguments on c.l.f. but this most certainly makes the top 10! :)
the use of locals, those whose Forth system does not implement them,
do. Looks like the objections are sour-grapes arguments.
1. Adding general locals is trivial. It takes just one single line of Forth. Sure, you don't got the badly designed and much too heavy Forth-2012 implementation, but it works just as well. It also proves that IF Chuck had wanted locals, that it would be a trivial addition.
2. It also means the resistance is *NOT* due to the difficulty of implementation. 4tH v3.64.2 will even support a *MUCH* lighter, but fully conformant Forth-2012 LOCALS implementation. And if I can do it, so can others I suppose (Forth-2012 or not). So that argument is moot.
3. "Looks like the objections are sour-grapes arguments." No, I have given far more arguments than you have. I'm not gonna repeat them in a forum that has already archived them. If anything, yours is a prime example of a "sour grape argument".
Your turn!
Hans Bezemer <the.beez.speaks@gmail.com> writes:
And just before you're done you put your stuff on the stack and like a
tiny assembly line it is transported to the next thing. This means that
the function call overhead is MINIMAL - much less than C.
Oh, really? Wasn't it you who wrote ><nnd$34fd6cd6$25a88dac@ac6bb1addf3a4136>:
|if you want speed, do your program in C -O3 so to say. It'll blow
|any Forth out of the water.
And if we look at the results for fib (a benchmark that performs lots
of calls) inf Figure 1 of ><https://www.complang.tuwien.ac.at/papers/ertl24-interpreter-speed.pdf>,
gcc -O3 outperforms the fastest Forth system, and gcc -O1 outperforms
the fastest Forth system by even more.
And that's not the solution - it's the PROBLEM. You can add loads of >>complexity without much (immediate) penalty. You're not compelled to
study - or even *think* about your algorithm. You most probably will end
up with code that works - without you understanding why.
And that will either bite you later, or limit your capability to expend
on that code.
Yes, you can expend a lot of effort on code that's hard to write and
hard to understand, but that's not limited to Forth.
If you mean that, by making code hard to write, Forth without locals
makes it easier to extend the code, I very much doubt it. In some
cases it may not be harder, but in others (where the extension
requires, e.g., dealing with additional data in existing colon
definitions) it is harder.
- anton
On 2025-07-02 15:37, albert@spenarnc.xs4all.nl wrote:
In article <1042s2o$3d58h$1@dont-email.me>,
Ruvim <ruvim.pinka@gmail.com> wrote:
On 2025-06-24 01:03, minforth wrote:
[...]
For me, the small syntax extension is a convenience when working
with longer definitions. A bit contrived (:= synonym for TO):
: SOME-APP { a f: b c | temp == n: flag z: freq }
\ inputs: integer a, floats b c
\ uninitialized: float temp
\ outputs: integer flag, complex freq
<: FUNC < ... calc function ... > ;>
BTW, why do you prefer the special syntax `<: ... ;>`
over an extension to the existing words `:` and `;`
: SOME-APP
[ : FUNC < ... calc function ... > ; ]
< ... >
;
In this approach the word `:` knows that it's a nested definition and
behaves accordingly.
Or it has not even know it, if [ is smart enough to compile a jump to
after ].
This can be tricky because the following should work:
create foo [ 123 , ] [ 456 ,
: bar [ ' foo compile, 123 lit, ] ;
--
Ruvim
In article <1043831$3ggg9$1@dont-email.me>,
Ruvim <ruvim.pinka@gmail.com> wrote:
On 2025-07-02 15:37, albert@spenarnc.xs4all.nl wrote:
In article <1042s2o$3d58h$1@dont-email.me>,
Ruvim <ruvim.pinka@gmail.com> wrote:
On 2025-06-24 01:03, minforth wrote:
[...]
For me, the small syntax extension is a convenience when working
with longer definitions. A bit contrived (:= synonym for TO):
: SOME-APP { a f: b c | temp == n: flag z: freq }
\ inputs: integer a, floats b c
\ uninitialized: float temp
\ outputs: integer flag, complex freq
<: FUNC < ... calc function ... > ;>
BTW, why do you prefer the special syntax `<: ... ;>`
over an extension to the existing words `:` and `;`
: SOME-APP
[ : FUNC < ... calc function ... > ; ]
< ... >
;
In this approach the word `:` knows that it's a nested definition and
behaves accordingly.
Or it has not even know it, if [ is smart enough to compile a jump to
after ].
This can be tricky because the following should work:
create foo [ 123 , ] [ 456 ,
: bar [ ' foo compile, 123 lit, ] ;
If this bothers you, rename it in [[ ]].
Once we enhance [ ] to do things prohibited by the standard,
(adding nested definitions) I can't be bothered with this too much.
On 2025-07-03 17:11, albert@spenarnc.xs4all.nl wrote:
In article <1043831$3ggg9$1@dont-email.me>,
Ruvim <ruvim.pinka@gmail.com> wrote:
On 2025-07-02 15:37, albert@spenarnc.xs4all.nl wrote:
In article <1042s2o$3d58h$1@dont-email.me>,
Ruvim <ruvim.pinka@gmail.com> wrote:
On 2025-06-24 01:03, minforth wrote:
[...]
For me, the small syntax extension is a convenience when working
with longer definitions. A bit contrived (:= synonym for TO):
: SOME-APP { a f: b c | temp == n: flag z: freq }
\ inputs: integer a, floats b c
\ uninitialized: float temp
\ outputs: integer flag, complex freq
<: FUNC < ... calc function ... > ;>
BTW, why do you prefer the special syntax `<: ... ;>`
over an extension to the existing words `:` and `;`
: SOME-APP
[ : FUNC < ... calc function ... > ; ]
< ... >
;
In this approach the word `:` knows that it's a nested
definition and behaves accordingly.
Or it has not even know it, if [ is smart enough to compile a
jump to after ].
This can be tricky because the following should work:
create foo [ 123 , ] [ 456 ,
: bar [ ' foo compile, 123 lit, ] ;
If this bothers you, rename it in [[ ]].
Once we enhance [ ] to do things prohibited by the standard,
(adding nested definitions) I can't be bothered with this too much.
The standard does not prohibit a system from supporting nested
definitions in whichever way that does not violate the standard
behavior.
Yes, something like "private[ ... ]private" is a possible approach,
and its implementation seems simpler than adding the smarts to `:`
and `;` (and other defining words, if any).
The advantage of this approach over "<: ... ;>" is that you can
define not only colon-definitions, but also constants, variables,
immediate words, one-time macros, etc.
: foo ( F: r.coefficient -- r.result )
private[
variable cnt
0e fvalue k
: [x] ... ; immediate
]private
to k 0 cnt !
...
;
It's also possible to associated the word list of private words with
the containing word xt for debugging purposes.
--
Ruvim
In lxf I have module, private, public, end-module
your example would be
module
private
variable cnt
0e fvalue k
: [x] ... ; immediate
public
: foo ( F: r.coefficient -- r.result )
to k 0 cnt !
...
;
end-module
end-module will remove all headers from the private words in the module
I am not fond of mixing definitions inside others.
The standard does not prohibit a system from supporting nested
definitions in whichever way that does not violate the standard behavior.
It's also possible to associated the word list of private words with the >containing word xt for debugging purposes.
--
Ruvim
On Thu, 3 Jul 2025 17:42:02 +0000, peter wrote:[...]
I am not fond of mixing definitions inside others.
Section 3.4.5 of the standard document is very clear on this.
On the other hand, quotations do not fall under this verdict
because neither namespace nor search order management is
required.
My <: func .. ;> emulation uses quotations.
No big deal.
I find it useful every now and then.
On Thu, 3 Jul 2025 20:47:07 +0400
Ruvim <ruvim.pinka@gmail.com> wrote:
Yes, something like "private[ ... ]private" is a possible approach,
and its implementation seems simpler than adding the smarts to `:`
and `;` (and other defining words, if any).
The advantage of this approach over "<: ... ;>" is that you can
define not only colon-definitions, but also constants, variables,
immediate words, one-time macros, etc.
: foo ( F: r.coefficient -- r.result )
private[
variable cnt
0e fvalue k
: [x] ... ; immediate
]private
to k 0 cnt !
...
;
It's also possible to associated the word list of private words with
the containing word xt for debugging purposes.
In lxf I have module, private, public, end-module
your example would be
module
private
variable cnt
0e fvalue k
: [x] ... ; immediate
public
: foo ( F: r.coefficient -- r.result )
to k 0 cnt !
...
;
end-module
end-module will remove all headers from the private words in the module
I am not found of mixing definitions inside others.
On 2 Jul 2025 at 05:39:52 CEST, "dxf" <dxforth@gmail.com> wrote:
On 1/07/2025 10:22 pm, Hans Bezemer wrote:
On 27-06-2025 03:39, dxf wrote:
Yet forthers have no problem with this. Take the SwiftForth source code. >>>> At best you'll get a general comment as to what a function does. How do >>>> they maintain it - the same way anyone proficient in C maintains C code. >>>> Albert is correct. Familiarity is key to readability. That's not to say >>>> code deserving documentation shouldn't have it. OTOH one shouldn't be >>>> expecting documentation (including stack commentary) for what's an everyday
affair in Forth.
I think you and Albert are on the right track here. Familiarity is a large >>> part of this "readability" thingy. There are a few notes I want to add,
though:
1. "Infix notation" is part of this familiarity. I know I've commented every
single expression in TEONW, since I understand those "infix" expressions much
better than all those RPN thingies - and you got something to check your code
against;
2. Intentionality. I do this a LOT. E.g. if you find OVER OVER in my code, >>> you may be certain those two items have nothing to do with each other. If you
find 2DUP it's a string, a double number or another "addr/count" array. CHOP
replaces 1 /STRING. Also: stack patterns can be codified like SPIN or STOW; >>>
3. Brevity. Short definitions are easier to understand. If you can abstract >>> it, put a name of it can spare the performance - split it up.
4. Naming. I give this a LOT of thought. I prefer reading a name and having a
pretty good idea of what that code does (especially in the context of a
library or a program). See:
https://sourceforge.net/p/forth-4th/wiki/What%27s%20in%20a%20name%3F/
Feel free to disagree. It may not work for you, but at least it works for me.
Recently someone told me about Christianity - how it wasn't meant to be easy -
supposed to be, among other things, a denial of the senses. I'm hearing much
the same in Forth. That it's a celibate practice in which one denies everyday
sensory pleasures including readability and maintainability in order to achieve
programming nirvana. Heck, if that's how folks see Forth then perhaps they >> should stop before the cognitive dissonance sends them crazy or they pop a >> cork.
IMHO religious belief is not a denial of the senses but a retraining. That does not mean that the retraining leads to anything valuable, but it can
do depending very much on the trainer and trainee.
...
I like to remind of the youtube FORTH2020 of Wagner. This concerns
motions of aircraft, position speed, pitch roll and yaw etc.
Terribly complicated, no LOCAL's. There was a question whether LOCAL's
could have made Wagners code easier.
He stated the ideal (paraphrased by me) that "code is its own comment"
Hans Bezemer <the.beez.speaks@gmail.com> writes:
1. Adding general locals is trivial. It takes just one single line of
Forth.
I don't see how to do it in one line, and trivial is a subjective term.
I'd say in any case that it's not too difficult, but one line seems overoptimistic. Particularly, you need something like (LOCAL) in the
VM. The rest is just some extensions to the colon compiler. Your
mention of it taking 3-4 screens sounded within reason to me, and I
don't consider that to be a lot of code.
He said he uses the hardware fp stack for speed. Is he really only
using 8 levels of stack?
On 3/07/2025 10:51 pm, albert@spenarnc.xs4all.nl wrote:
...
I like to remind of the youtube FORTH2020 of Wagner. This concerns
motions of aircraft, position speed, pitch roll and yaw etc.
Terribly complicated, no LOCAL's. There was a question whether LOCAL's
could have made Wagners code easier.
He stated the ideal (paraphrased by me) that "code is its own comment"
That was an interesting video even if more a rundown of his (long) history
as a professional forth programmer. Here's the link for anyone curious:
https://youtu.be/V9ES9UZHaag
He said he uses the hardware fp stack for speed. Is he really only
using 8 levels of stack?
In article <300ba9a1581bea9a01ab85d5d361e6eaeedbf23a@i2pn2.org>,
dxf <dxforth@gmail.com> wrote:
On 3/07/2025 10:51 pm, albert@spenarnc.xs4all.nl wrote:
...
I like to remind of the youtube FORTH2020 of Wagner. This concerns
motions of aircraft, position speed, pitch roll and yaw etc.
Terribly complicated, no LOCAL's. There was a question whether LOCAL's
could have made Wagners code easier.
He stated the ideal (paraphrased by me) that "code is its own comment"
That was an interesting video even if more a rundown of his (long) history >> as a professional forth programmer. Here's the link for anyone curious:
https://youtu.be/V9ES9UZHaag
He said he uses the hardware fp stack for speed. Is he really only
using 8 levels of stack?
8 level is plenty as long as you refrain from recursion that in
Wagners context would be not even remotely useful.
In article <nnd$57e17bcd$463b2e07@d86e5bbc05746f06>,
Hans Bezemer <the.beez.speaks@gmail.com> wrote:
On 03-07-2025 01:59, Paul Rubin wrote:
Hans Bezemer <the.beez.speaks@gmail.com> writes:
1. Adding general locals is trivial. It takes just one single line of
Forth.
I don't see how to do it in one line, and trivial is a subjective term.
I'd say in any case that it's not too difficult, but one line seems
overoptimistic. Particularly, you need something like (LOCAL) in the
VM. The rest is just some extensions to the colon compiler. Your
mention of it taking 3-4 screens sounded within reason to me, and I
don't consider that to be a lot of code.
"Short" in my dictionary is. One. Single. Screen. No more. No less (pun
intended).
And this one is one single screen. Even with the dependencies.
https://youtu.be/FH4tWf9vPrA
Typical use:
variable a
variable b
: divide
local a
local b
b ! a ! a @ b @ / ;
Does recursion, the whole enchilada. One line.
Thanks to Fred Behringer - and Albert, who condensed it to a single
single line definition. Praise is where praise is due.
Although 'local variables' like this are much preferred (superior) ,
LOCAL (value) is what is asked for.
If you don't have the akward, forward parsing TO already defined, you
are bound to do more work.
Puzzling because of a thread here not long ago in which scientific users >appear to suggest the opposite. Such concerns have apparently been around
a long time:
https://groups.google.com/g/comp.lang.forth/c/CApt6AiFkxo/m/wwZmc_Tr1PcJ
One interesting aspect is that VFX 5.x finally includes an FP package
by default, and it started by including an SSE2-based FP package which >supports a deep FP stack. However, MPE received customer complaints
about the lower number of significant digits in SSE2 (binary64)
vs. 387 (80-bit FP values), so they switched the default to the
387-based FP package that only has 8 FP stack items. Apparently no
MPE customer complains about that limitation.
OTOH, iForth-5.1-mini uses the 387 instructions, but stores FP stack
items in memory at least on call boundaries. Maybe Marcel Hendrix can
give some insight into what made him take this additional
implementation effort.
- anton--
I investigated the instruction set, and I found no way to detect
if the 8 registers stack is full.
This would offer the possibility to spill registers to memory only
if it is needed.
Am 05.07.2025 um 14:21 schrieb albert@spenarnc.xs4all.nl:
I investigated the instruction set, and I found no way to detect
if the 8 registers stack is full.
This would offer the possibility to spill registers to memory only
if it is needed.
IIRC signaling and handling fp-stack overflow is not an easy task.
At most, the computer would crash.
IOW, spilling makes sense.
Am 05.07.2025 um 14:21 schrieb albert@spenarnc.xs4all.nl:
I investigated the instruction set, and I found no way to detect
if the 8 registers stack is full.
This would offer the possibility to spill registers to memory only
if it is needed.
IIRC signaling and handling fp-stack overflow is not an easy task.
dxf <dxforth@gmail.com> writes:
[8 stack items on the FP stack]
Puzzling because of a thread here not long ago in which scientific users
appear to suggest the opposite. Such concerns have apparently been around >> a long time:
https://groups.google.com/g/comp.lang.forth/c/CApt6AiFkxo/m/wwZmc_Tr1PcJ
I have read through the thread. It's unclear to me which scientific
users you have in mind. My impression is that 8 stack items was
deemed sufficient by many, and preferable (on 387) for efficiency
reasons.
Certainly, of the two points this thread is about, there was a
Forth200x proposal for standardizing a separate FP stack, and this
proposal was accepted. There was no proposal for increasing the
minimum size of the FP stack; Forth-2012 still says:
|The size of a floating-point stack shall be at least 6 items.
One interesting aspect is that VFX 5.x finally includes an FP package
by default, and it started by including an SSE2-based FP package which supports a deep FP stack. However, MPE received customer complaints
about the lower number of significant digits in SSE2 (binary64)
vs. 387 (80-bit FP values), so they switched the default to the
387-based FP package that only has 8 FP stack items. Apparently no
MPE customer complains about that limitation.
...
Recently someone told me about Christianity - how it wasn't meant to be
easy - supposed to be, among other things, a denial of the senses.
On 5/07/2025 6:49 pm, Anton Ertl wrote:
dxf <dxforth@gmail.com> writes:
[8 stack items on the FP stack]
Puzzling because of a thread here not long ago in which scientific users >>> appear to suggest the opposite. Such concerns have apparently been around >>> a long time:
https://groups.google.com/g/comp.lang.forth/c/CApt6AiFkxo/m/wwZmc_Tr1PcJ
I have read through the thread. It's unclear to me which scientific
users you have in mind. My impression is that 8 stack items was
deemed sufficient by many, and preferable (on 387) for efficiency
reasons.
AFAICS both Skip Carter (proponent) and Julian Noble were suggesting the
6 level minimum were inadequate. A similar sentiment was expressed here
only several months ago. AFAIK all major forths supporting x87 hardware offer software stack options.
Certainly, of the two points this thread is about, there was a
Forth200x proposal for standardizing a separate FP stack, and this
proposal was accepted. There was no proposal for increasing the
minimum size of the FP stack; Forth-2012 still says:
|The size of a floating-point stack shall be at least 6 items.
Only because nothing further was heard. What became of the review
Elizabeth announced I've no idea.
One interesting aspect is that VFX 5.x finally includes an FP package
by default, and it started by including an SSE2-based FP package which
supports a deep FP stack. However, MPE received customer complaints
about the lower number of significant digits in SSE2 (binary64)
vs. 387 (80-bit FP values), so they switched the default to the
387-based FP package that only has 8 FP stack items. Apparently no
MPE customer complains about that limitation.
...
AFAIK x87 hardware stack was always MPE's main and best supported FP
package. As for SSE2 it wouldn't exist if industry didn't consider double-precision adequate. My impression of MPE's SSE2 implementation
is that it's 'a work in progress'. The basic precision is there but transcendentals appear to be limited to single-precision. That'd be
the reason I'd stick with MPE's x87 package. Other reason is it's now
quite difficult and error-prone to switch FP packages as it involves rebuilding the system. The old scheme was simpler and idiot-proof.
On 6 Jul 2025 at 04:52:37 CEST, "dxf" <dxforth@gmail.com> wrote:
On 5/07/2025 6:49 pm, Anton Ertl wrote:
...
One interesting aspect is that VFX 5.x finally includes an FP package
by default, and it started by including an SSE2-based FP package which
supports a deep FP stack. However, MPE received customer complaints
about the lower number of significant digits in SSE2 (binary64)
vs. 387 (80-bit FP values), so they switched the default to the
387-based FP package that only has 8 FP stack items. Apparently no
MPE customer complains about that limitation.
...
AFAIK x87 hardware stack was always MPE's main and best supported FP
package. As for SSE2 it wouldn't exist if industry didn't consider
double-precision adequate. My impression of MPE's SSE2 implementation
is that it's 'a work in progress'. The basic precision is there but
transcendentals appear to be limited to single-precision. That'd be
the reason I'd stick with MPE's x87 package. Other reason is it's now
quite difficult and error-prone to switch FP packages as it involves
rebuilding the system. The old scheme was simpler and idiot-proof.
You do not have to rebuild the system to switch. Just read the manual.
Recently someone told me about Christianity - how it wasn't meant to be...
easy - supposed to be, among other things, a denial of the senses.
On 5/07/2025 6:49 pm, Anton Ertl wrote:
dxf <dxforth@gmail.com> writes:
[8 stack items on the FP stack]
Puzzling because of a thread here not long ago in which scientific users >>> appear to suggest the opposite. Such concerns have apparently been around >>> a long time:
https://groups.google.com/g/comp.lang.forth/c/CApt6AiFkxo/m/wwZmc_Tr1PcJ
I have read through the thread. It's unclear to me which scientific
users you have in mind. My impression is that 8 stack items was
deemed sufficient by many, and preferable (on 387) for efficiency
reasons.
AFAICS both Skip Carter (proponent) and Julian Noble were suggesting the
6 level minimum were inadequate.
AFAIK all major forths supporting x87 hardware
offer software stack options.
Certainly, of the two points this thread is about, there was a
Forth200x proposal for standardizing a separate FP stack, and this
proposal was accepted. There was no proposal for increasing the
minimum size of the FP stack; Forth-2012 still says:
|The size of a floating-point stack shall be at least 6 items.
Only because nothing further was heard. What became of the review
Elizabeth announced I've no idea.
The old scheme was simpler and idiot-proof.
Am 05.07.2025 um 14:41 schrieb minforth:
Am 05.07.2025 um 14:21 schrieb albert@spenarnc.xs4all.nl:
I investigated the instruction set, and I found no way to detect
if the 8 registers stack is full.
This would offer the possibility to spill registers to memory only
if it is needed.
IIRC signaling and handling fp-stack overflow is not an easy task.
At most, the computer would crash.
IOW, spilling makes sense.
A deep dive into the manual
... the C1 condition code flag is used for a variety of functions.
When both the IE and SF flags in the x87 FPU status word are set,
indicating a stack overflow or underflow exception (#IS), the C1
flag distinguishes between overflow (C1=1) and underflow (C1=0).
dxf <dxforth@gmail.com> writes:
On 5/07/2025 6:49 pm, Anton Ertl wrote:
dxf <dxforth@gmail.com> writes:
[8 stack items on the FP stack]
Puzzling because of a thread here not long ago in which scientific users >>>> appear to suggest the opposite. Such concerns have apparently been around >>>> a long time:I have read through the thread. It's unclear to me which scientific
https://groups.google.com/g/comp.lang.forth/c/CApt6AiFkxo/m/wwZmc_Tr1PcJ >>>
users you have in mind. My impression is that 8 stack items was
deemed sufficient by many, and preferable (on 387) for efficiency
reasons.
AFAICS both Skip Carter (proponent) and Julian Noble were suggesting the
6 level minimum were inadequate.
Skip Carter did not post in this thread, but given that he proposed
the change, he probably found 6 to be too few; or maybe it was just a phenomenon that we also see elsewhere as range anxiety. In any case,
he made no such proposal to Forth-200x, so apparently the need was not pressing.
Julian Noble ignored the FP stack size issue in his first posting in
this thread, unlike the separate FP stack size issue, which he
supported. So it seems that he did not care about a larger FP stack
size. In the other posting he endorsed moving FP stack items to the
data stack, but he did not write why; for all we know he might have
wanted that as a first step for getting the mantissa, exponent and
sign of the FP value as integer (and the other direction for
synthesizing FP numbers from these parts).
AFAIK all major forths supporting x87 hardware
offer software stack options.
Certainly on SwiftForth-4.0 I find no such option, it apparently
proved unnecessary. The manual mentions fpconfig.f, but no such file
exists in a SwiftForth-4.0 directory in the versions I have installed.
There exists such a file on various SwiftForth-3.x versions, but on
most of our machines SwiftForth-3.x segfaults (I have not investigated
why; it used to work). Ok, so I found an old system where it does not segfault, but trying to load FP on that system produced no joy:
...
...
If I want to switch from the default FP package to a different
package, I essentially have to take the same steps, I only have to add
two additional commands before including the FP package; the last
command for including the SSE implementation becomes:
vfx64 "integers remove-FP-pack include /nfs/nfstmp/anton/VfxForth64Lin-5.43/Lib/x64/FPSSE64S.fth"
(A special twist here is that the documentation says that the file is
called FPSSE64.fth (with only 2 S characters), so I needed a few more
locate invocations to find the right one).
If you find the former simple, why not the latter (apart from the documentation mistake)?
In any case, in almost all cases I use the default FP pack, and here
the VFX-5 and SwiftForth-4 approach is unbeatable in simplicity.
Instead of performing the sequence of commands shown above, I just
start the Forth system, and FP words are ready.
Skip Carter did not post in this thread, but given that he proposed
the change, he probably found 6 to be too few; or maybe it was just a >phenomenon that we also see elsewhere as range anxiety. In any case,
he made no such proposal to Forth-200x, so apparently the need was not >pressing.
In any case, in almost all cases I use the default FP pack, and here
the VFX-5 and SwiftForth-4 approach is unbeatable in simplicity.
Instead of performing the sequence of commands shown above, I just
start the Forth system, and FP words are ready.
- anton
On 6/07/2025 9:30 pm, Anton Ertl wrote:
dxf <dxforth@gmail.com> writes:
On 5/07/2025 6:49 pm, Anton Ertl wrote:
dxf <dxforth@gmail.com> writes:
[8 stack items on the FP stack]
Puzzling because of a thread here not long ago in which scientific users >>>>> appear to suggest the opposite. Such concerns have apparently been aroundI have read through the thread. It's unclear to me which scientific
a long time:
https://groups.google.com/g/comp.lang.forth/c/CApt6AiFkxo/m/wwZmc_Tr1PcJ >>>>
users you have in mind. My impression is that 8 stack items was
deemed sufficient by many, and preferable (on 387) for efficiency
reasons.
AFAICS both Skip Carter (proponent) and Julian Noble were suggesting the >>> 6 level minimum were inadequate.
Skip Carter did not post in this thread, but given that he proposed
the change, he probably found 6 to be too few; or maybe it was just a
phenomenon that we also see elsewhere as range anxiety. In any case,
he made no such proposal to Forth-200x, so apparently the need was not
pressing.
Julian Noble ignored the FP stack size issue in his first posting in
this thread, unlike the separate FP stack size issue, which he
supported. So it seems that he did not care about a larger FP stack
size. In the other posting he endorsed moving FP stack items to the
data stack, but he did not write why; for all we know he might have
wanted that as a first step for getting the mantissa, exponent and
sign of the FP value as integer (and the other direction for
synthesizing FP numbers from these parts).
He appears to dislike the idea of standard-imposed minimums (e.g. Carter's suggestion of 16) but suggested:
a) the user can offload to memory if necessary from
fpu hardware;
b) an ANS FLOATING and FLOATING EXT wordset includes
the necessary hooks to extend the fp stack.
On 2025-07-03 17:11, albert@spenarnc.xs4all.nl wrote:
In article <1043831$3ggg9$1@dont-email.me>,
Ruvim <ruvim.pinka@gmail.com> wrote:
On 2025-07-02 15:37, albert@spenarnc.xs4all.nl wrote:
In article <1042s2o$3d58h$1@dont-email.me>,
Ruvim <ruvim.pinka@gmail.com> wrote:
On 2025-06-24 01:03, minforth wrote:
[...]
For me, the small syntax extension is a convenience when working
with longer definitions. A bit contrived (:= synonym for TO):
: SOME-APP { a f: b c | temp == n: flag z: freq }
\ inputs: integer a, floats b c
\ uninitialized: float temp
\ outputs: integer flag, complex freq
<: FUNC < ... calc function ... > ;>
BTW, why do you prefer the special syntax `<: ... ;>`
over an extension to the existing words `:` and `;`
: SOME-APP
[ : FUNC < ... calc function ... > ; ]
< ... >
;
In this approach the word `:` knows that it's a nested definition and >>>>> behaves accordingly.
Or it has not even know it, if [ is smart enough to compile a jump to
after ].
This can be tricky because the following should work:
create foo [ 123 , ] [ 456 ,
: bar [ ' foo compile, 123 lit, ] ;
If this bothers you, rename it in [[ ]].
Once we enhance [ ] to do things prohibited by the standard,
(adding nested definitions) I can't be bothered with this too much.
The standard does not prohibit a system from supporting nested
definitions in whichever way that does not violate the standard behavior.
Yes, something like "private[ ... ]private" is a possible approach, and
its implementation seems simpler than adding the smarts to `:` and `;`
(and other defining words, if any).
The advantage of this approach over "<: ... ;>" is that you can define
not only colon-definitions, but also constants, variables, immediate
words, one-time macros, etc.
: foo ( F: r.coefficient -- r.result )
private[
variable cnt
0e fvalue k
: [x] ... ; immediate
]private
to k 0 cnt !
...
;
It's also possible to associated the word list of private words with the containing word xt for debugging purposes.
On 07-07-2025 05:48, dxf wrote:
...
He appears to dislike the idea of standard-imposed minimums (e.g. Carter's >> suggestion of 16) but suggested:
a) the user can offload to memory if necessary from
fpu hardware;
b) an ANS FLOATING and FLOATING EXT wordset includes
the necessary hooks to extend the fp stack.
In 4tH, there are two (highlevel) FP-systems - with 6 predetermined configurations. Configs number 0-2 don't have an FP stack, they use the datastack. 3-5 have a separate FP stack - and double the precision. The standard FP stacksize is 16, you can extend it by defining a constant before including the FP libs.
As for SSE2 it wouldn't exist if industry didn't consider
double-precision adequate.
dxf <dxforth@gmail.com> writes:
As for SSE2 it wouldn't exist if industry didn't consider
double-precision adequate.
SSE2 is/was first and foremost a vectorizing extension, and it has been superseded quite a few times, indicating it was never all that
adequate. I don't know whether any of its successors support extended precision though.
dxf <dxforth@gmail.com> writes:
As for SSE2 it wouldn't exist if industry didn't consider
double-precision adequate.
SSE2 is/was first and foremost a vectorizing extension, and it has been superseded quite a few times, indicating it was never all that
adequate. I don't know whether any of its successors support extended precision though.
W. Kahan was a big believer in extended precision (that's why the 8087
had it from the start). I believes IEEE specifies both 80 bit and 128
bit formats in addition to 64 bit. The RISC-V spec includes encodings
for 128 bit IEEE but I don't know if any RISC-V hardware actually
implements it. I think there are some IBM mainframe CPUs that have it.
You don't need 64-bit doubles for signal or image processing.
Most vector/matrix operations on streaming data don't require
them either. Whether SSE2 is adequate or not to handle such data
depends on the application.
"Industry" can manage well with 32-bit floats or even smaller with non-standard number formats.
I suspect IEEE simply standardized what had become common practice among implementers.
What little I know about SSE2 it's not as well thought out or organized
as Intel's original effort. E.g. doing something as simple as changing
sign of an fp number is a pain when NANs are factored in.
minforth <minforth@gmx.net> writes:
You don't need 64-bit doubles for signal or image processing.
Most vector/matrix operations on streaming data don't require
them either. Whether SSE2 is adequate or not to handle such data
depends on the application.
Sure, and for that matter, AI inference uses 8 bit and even 4 bit
floating point.
Kahan on the other hand was interested in engineering
and scientific applications like PDE solvers (airfoils, fluid dynamics,
FEM, etc.). That's an area where roundoff error builds up after many iterations, thus extended precision.
dxf <dxforth@gmail.com> writes:
I suspect IEEE simply standardized what had become common practice among
implementers.
No, it was really new and interesting. https://people.eecs.berkeley.edu/~wkahan/ieee754status/754story.html
What little I know about SSE2 it's not as well thought out or organized
as Intel's original effort. E.g. doing something as simple as changing
sign of an fp number is a pain when NANs are factored in.
I wonder if later SSE/AVX/whatever versions fixed this stuff.
I don't do parallelization, but I was still surprised by the good
results using FMA. In other words, increasing floating-point number
size is not always the way to go.
Anyhow, first step is to select the best fp rounding method ....
dxf <dxforth@gmail.com> writes:
As for SSE2 it wouldn't exist if industry didn't consider
double-precision adequate.
SSE2 is/was first and foremost a vectorizing extension, and it has been >superseded quite a few times, indicating it was never all that
adequate.
I don't know whether any of its successors support extended
precision though.
W. Kahan was a big believer in extended precision (that's why the 8087
had it from the start). I believes IEEE specifies both 80 bit and 128
bit formats in addition to 64 bit.
I suspect IEEE simply standardized what had become common practice among >implementers.
By using 80 bits /internally/ Intel went a long way to
achieving IEEE's spec for double precision.
E.g. doing something as simple as changing
sign of an fp number is a pain when NANs are factored in.
The catch with SSE is there's nothing like FCHS or FABS
so depending on how one implements them, results vary across implementations.
"Industry" can manage well with 32-bit
floats or even smaller with non-standard number formats.
On 10 Jul 2025 at 02:18:50 CEST, "minforth" <minforth@gmx.net> wrote:
"Industry" can manage well with 32-bit
floats or even smaller with non-standard number formats.
My customers beg to differ and some use 128 bit numbers for
their work. In a construction estimate for one runway for the
new Hong Kong airport, the cost difference between a 64 bit FP
calculation and the integer calculation was US 10 million dollars.
This was for pile capping which involves a large quantity of relatively
small differences.
dxf <dxforth@gmail.com> writes:
The catch with SSE is there's nothing like FCHS or FABS
so depending on how one implements them, results vary across implementations.
You can see in Gforth how to implement FNEGATE and FABS with SSE2:
see fnegate
Code fnegate
0x000055e6a78a8274: add $0x8,%rbx
0x000055e6a78a8278: xorpd 0x24d8f(%rip),%xmm15 # 0x55e6a78cd010
0x000055e6a78a8281: mov %r15,%r9
0x000055e6a78a8284: mov (%rbx),%rax
0x000055e6a78a8287: jmp *%rax
end-code
ok
0x55e6a78cd010 16 dump
55E6A78CD010: 00 00 00 00 00 00 00 80 - 00 00 00 00 00 00 00 00
ok
see fabs
Code fabs
0x000055e6a78a84fe: add $0x8,%rbx
0x000055e6a78a8502: andpd 0x24b15(%rip),%xmm15 # 0x55e6a78cd020
0x000055e6a78a850b: mov %r15,%r9
0x000055e6a78a850e: mov (%rbx),%rax
0x000055e6a78a8511: jmp *%rax
end-code
ok
0x55e6a78cd020 16 dump
55E6A78CD020: FF FF FF FF FF FF FF 7F - 00 00 00 00 00 00 00 00
The actual implementation is the xorpd instruction for FNEGATE, and in
the andpd instruction for FABS. The memory locations contain masks:
for FNEGATE only the sign bit is set, for FABS everything but the sign
bit is set.
Sure you can implement FNEGATE and FABS in more complicated ways, but
you can also implement them in more complicated ways if you use the
387 instruction set. Here's an example of more complicated
implementations:
see fnegate
FNEGATE
( 004C4010 4833C0 ) XOR RAX, RAX
( 004C4013 F34D0F7EC8 ) MOVQ XMM9, XMM8
( 004C4018 664C0F6EC0 ) MOVQ XMM8, RAX
( 004C401D F2450F5CC1 ) SUBSD XMM8, XMM9
( 004C4022 C3 ) RET/NEXT
( 19 bytes, 5 instructions )
ok
see fabs
FABS
( 004C40B0 E8FBEFFFFF ) CALL 004C30B0 FS@
( 004C40B5 4885DB ) TEST RBX, RBX
( 004C40B8 488B5D00 ) MOV RBX, [RBP]
( 004C40BC 488D6D08 ) LEA RBP, [RBP+08]
( 004C40C0 0F8D05000000 ) JNL/GE 004C40CB
( 004C40C6 E845FFFFFF ) CALL 004C4010 FNEGATE
( 004C40CB C3 ) RET/NEXT
( 28 bytes, 7 instructions )
I believes IEEE specifies both 80 bit and 128 bit formats in additionNot 80-bit format. binary128 and binary256 are specified.
to 64 bit.
anton@mips.complang.tuwien.ac.at (Anton Ertl) writes:
I believes IEEE specifies both 80 bit and 128 bit formats in additionNot 80-bit format. binary128 and binary256 are specified.
to 64 bit.
I see, 80 bits is considered double-extended. "The x87 and Motorola
68881 80-bit formats meet the requirements of the IEEE 754-1985 double extended format,[12] as does the IEEE 754 128-bit binary format." (https://en.wikipedia.org/wiki/Extended_precision)
Interestingly, Kahan's 1997 report on IEEE 754's status does say 80 bit
is specified. But it sounds like that omits some nuance.
https://people.eecs.berkeley.edu/~wkahan/ieee754status/IEEE754.PDF
Kahan was also overly critical of dynamic Unum/Posit formats.
Time has shown that he was partially wrong: https://spectrum.ieee.org/floating-point-numbers-posits-processor
Am 10.07.2025 um 21:33 schrieb Paul Rubin:
anton@mips.complang.tuwien.ac.at (Anton Ertl) writes:
I believes IEEE specifies both 80 bit and 128 bit formats in additionNot 80-bit format. binary128 and binary256 are specified.
to 64 bit.
I see, 80 bits is considered double-extended. "The x87 and Motorola
68881 80-bit formats meet the requirements of the IEEE 754-1985 double
extended format,[12] as does the IEEE 754 128-bit binary format."
(https://en.wikipedia.org/wiki/Extended_precision)
Interestingly, Kahan's 1997 report on IEEE 754's status does say 80 bit
is specified. But it sounds like that omits some nuance.
https://people.eecs.berkeley.edu/~wkahan/ieee754status/IEEE754.PDF
Kahan was also overly critical of dynamic Unum/Posit formats.
Time has shown that he was partially wrong: https://spectrum.ieee.org/floating-point-numbers-posits-processor
minforth <minforth@gmx.net> writes:
Kahan was also overly critical of dynamic Unum/Posit formats.
Time has shown that he was partially wrong:
https://spectrum.ieee.org/floating-point-numbers-posits-processor
I don't feel qualified to draw a conclusion from this. I wonder what
the numerics community thinks, if there is any consensus. I remember
being dubious of posits when I first heard of them, though Kahan
probably influenced that. I do know that IEEE 754 took a lot of trouble
to avoid undesirable behaviours that never would have occurred to most
of us. No idea how well posits do at that. I guess though, given the continued attention they get, they must be more interesting than I had thought.
I saw one of the posit articles criticizing IEEE 754 because IEEE 754 addition is not always associative. But that is inherent in how
floating point arithmetic works, and I don't see how posit addition can
avoid it. Let a = 1e100, b = -1e100, and c=1. So mathematically,
a+b+c=1. You should get that from (a+b)+c in your favorite floating
point format. But a+(b+c) will almost certainly be 0, without very high precision (300+ bits).
When someone begins with the line it rarely ends well:
"Twenty years ago anarchy threatened floating-point arithmetic."
One floating-point to rule them all.
dxf <dxforth@gmail.com> writes:
When someone begins with the line it rarely ends well:
"Twenty years ago anarchy threatened floating-point arithmetic."
One floating-point to rule them all.
This gives a good perspective on posits:
https://people.eecs.berkeley.edu/~demmel/ma221_Fall20/Dinechin_etal_2019.pdf
Floating point arithmetic in the 1960s (before my time) was really in a terrible state. Kahan has written about it. Apparently IBM 360
floating point arithmetic had to be redesigned after the fact, because
the original version had such weird anomalies.
dxf <dxforth@gmail.com> writes:
When someone begins with the line it rarely ends well:
"Twenty years ago anarchy threatened floating-point arithmetic."
One floating-point to rule them all.
This gives a good perspective on posits:
https://people.eecs.berkeley.edu/~demmel/ma221_Fall20/Dinechin_etal_2019.pdf
Am 10.07.2025 um 21:33 schrieb Paul Rubin:
Kahan was also overly critical of dynamic Unum/Posit formats.
Time has shown that he was partially wrong: >https://spectrum.ieee.org/floating-point-numbers-posits-processor
But was it the case by the mid/late 70's - or certain individuals saw an opportunity to influence the burgeoning microprocessor market? Notions of single and double precision already existed in software floating point -
I have looked at a (IIRC) slide deck by Kahan where he shows examples
where the altenarnative by Gustafson (don't remember which one he
looked at in that slide deck) fails and traditional FP numbers work.
I guess though, given the
continued attention they get, they must be more interesting than I had >thought.
I saw one of the posit articles criticizing IEEE 754 because IEEE 754 >addition is not always associative. But that is inherent in how
floating point arithmetic works, and I don't see how posit addition can
avoid it.
dxf <dxforth@gmail.com> writes:
But was it the case by the mid/late 70's - or certain individuals saw an
opportunity to influence the burgeoning microprocessor market? Notions
of
single and double precision already existed in software floating point -
Hardware floating point also had single and double precision. The
really awful 1960s systems were gone by the mid 70s. But there were a
lot of competing formats, ranging from bad to mostly-ok. VAX floating
point was mostly ok, DEC wanted IEEE to adopt it, Kahan was ok with
that, but Intel thought "go for the best possible". Kahan's
retrospectives on this stuff are good reading:
On 11/07/2025 1:17 pm, Paul Rubin wrote:
This gives a good perspective on posits:
https://people.eecs.berkeley.edu/~demmel/ma221_Fall20/Dinechin_etal_2019.pdf
Floating point arithmetic in the 1960s (before my time) was really in a
terrible state. Kahan has written about it. Apparently IBM 360
floating point arithmetic had to be redesigned after the fact, because
the original version had such weird anomalies.
But was it the case by the mid/late 70's - or certain individuals saw an >opportunity to influence the burgeoning microprocessor market?
anton@mips.complang.tuwien.ac.at (Anton Ertl) writes:
I have looked at a (IIRC) slide deck by Kahan where he shows examples
where the altenarnative by Gustafson (don't remember which one he
looked at in that slide deck) fails and traditional FP numbers work.
Maybe this: http://people.eecs.berkeley.edu/~wkahan/UnumSORN.pdf
What is there not to like with the FPU? It provides 80 bits, which
is in itself a useful additional format, and should never have problems
with single and double-precision edge cases.
The only problem is that some languages and companies find it necessary
to boycott FPU use.
mhx@iae.nl (mhx) writes:
What is there not to like with the FPU? It provides 80 bits, which
is in itself a useful additional format, and should never have problems
with single and double-precision edge cases.
If you want to do double precision, using the 387 stack has the double-rounding problem <https://en.wikipedia.org/wiki/Rounding#Double_rounding>. Even if you
limit the mantissa to 53 bits, you still get double rounding when you
deal with numbers that are denormal numbers in binary64
representation. Java wanted to give the same results, bit for bit, on
all hardware, and ran afoul of this until they could switch to SSE2.
The only problem is that some languages and companies find it necessary
to boycott FPU use.
The rest of the industry has standardized on binary64 and binary32,
and they prefer bit-equivalent results for ease of testing. So as
soon as SSE2 gave that to them, they flocked to SSE2.
...
In any case, FP numbers are used in very diverse ways. Not everybody
needs all the features, and even fewer features are consciously
needed, but that's the usual case with things that are not
custom-taylored for your application.
On 11/07/2025 8:22 pm, Anton Ertl wrote:
The rest of the industry has standardized on binary64 and binary32,
and they prefer bit-equivalent results for ease of testing. So as
soon as SSE2 gave that to them, they flocked to SSE2.
...
I wonder how much of this is academic or trend inspired?
AFAICS Forth
clients haven't flocked to it else vendors would have SSE2 offerings at
the same level as their x387 packs.
...
For Forth, Inc. and MPE AFAIK their respective IA-32 Forth system was
the only one with hardware FP for many years, so there probably was
little pressure from users for bit-identical results with, say, SPARC, because they did not have a Forth system that ran on SPARC.
...
And as long as customers did not ask for bit-identical results to
those on, say, a Raspi, there was little reason to reimplement FP with
SSE2. I wonder if the development of the SSE2 package for VFX was
influenced by the availability of VFX for the Raspi.
These Forth systems also don't do global register allocation or auto-vectorization, so two other reasons why, e.g., C compilers chose
to use SSE2 on AMD64 (where SSE2 was guaranteed to be available) don't
exist for them.
- anton
On 13/07/2025 7:01 pm, Anton Ertl wrote:
...
For Forth, Inc. and MPE AFAIK their respective IA-32 Forth system was
the only one with hardware FP for many years, so there probably was
little pressure from users for bit-identical results with, say, SPARC,
because they did not have a Forth system that ran on SPARC.
What do you mean by "bit-identical results"? Since SSE2 comes without >transcendentals (or basics such as FABS and FNEGATE) and implementers
are expected to supply their own, if anything, I expect results across >platforms and compilers to vary.
dxf <dxforth@gmail.com> writes:
On 13/07/2025 7:01 pm, Anton Ertl wrote:
...
For Forth, Inc. and MPE AFAIK their respective IA-32 Forth system
was the only one with hardware FP for many years, so there
probably was little pressure from users for bit-identical results
with, say, SPARC, because they did not have a Forth system that
ran on SPARC.
What do you mean by "bit-identical results"? Since SSE2 comes
without transcendentals (or basics such as FABS and FNEGATE) and >implementers are expected to supply their own, if anything, I expect >results across platforms and compilers to vary.
There are operations for which IEEE 754 specifies the result to the
last bit (except that AFAIK the representation of NaNs is not
specified exactly), among them F+ F- F* F/ FSQRT, probably also
FNEGATE and FABS. It does not specify the exact result for
transcendental functions, but if your implementation performs the same bit-exact operations for computing a transcendental function on two
IEEE 754 compliant platforms, the result will be bit-identical (if it
is a number). So just use the same implementations of transcentental functions, and your results will be bit-identical; concerning the
NaNs, if you find a difference, check if the involved values are NaNs.
- anton
[..] if your implementation performs the same
bit-exact operations for computing a transcendental function on two
IEEE 754 compliant platforms, the result will be bit-identical (if it
is a number). So just use the same implementations of transcentental functions, and your results will be bit-identical; concerning the
NaNs, if you find a difference, check if the involved values are NaNs.
So just use the same implementations of transcentental functions, and
your results will be bit-identical
On Mon, 14 Jul 2025 6:04:13 +0000, Anton Ertl wrote:
[..] if your implementation performs the same
bit-exact operations for computing a transcendental function on two
IEEE 754 compliant platforms, the result will be bit-identical (if it
is a number). So just use the same implementations of transcentental
functions, and your results will be bit-identical; concerning the
NaNs, if you find a difference, check if the involved values are NaNs.
When e.g. summing the elements of a DP vector, it is hard to see why
that couldn't be done on the FPU stack (with 80 bits) before (possibly) >storing the result to a DP variable in memory. I am not sure that Forth
users would be able to resist that approach.
anton@mips.complang.tuwien.ac.at (Anton Ertl) writes:
So just use the same implementations of transcentental functions, and
your results will be bit-identical
Same implementations = same FP operations in the exact same order?
That
seems hard to ensure, if the functions are implemented in a language
that leaves anything up to a compiler.
Also, in the early implementations x87, 68881, NS320something(?), >transcententals were included in the coprocessor and the workings
weren't visible.
mhx@iae.nl (mhx) writes:[..]
On Mon, 14 Jul 2025 6:04:13 +0000, Anton Ertl wrote:
The question is: What properties do you want your computation to have?[..]
2) A more accurate result? How much more accuracy?
3) More performance?
C) Perform tree addition
a) Using 80-bit addition. This will be faster than sequential
addition because in many cases several additions can run in
parallel. It will also be quite accurate because it uses 80-bit
addition, and because the addition chains are reduced to
ld(length(vector)).
So, as you can see, depending on your objectives there may be more
attractive ways to add a vector than what you suggested. Your
suggestion actually looks pretty unattractive, except if your
objectives are "ease of implementation" and "more accuracy than the
naive approach".
This looks very interesting. I can find Kahan and Neumaier, but
"tree addition" didn't turn up (There is a suspicious looking
reliability paper about the approach which surely is not what
you meant). Or is it pairwise addition what I should look for?
Now riscv is the future.
Now riscv is the future.
I don't know. From what I learned, RISC-V
is strongly compiler-oriented. They wrote,
for example, that it lacks any condition codes.
Only conditional branches are predicated on
examining the contents of registers at the time
of the branch. No "add with carry" nor "subtract
with carry". From an assembly point of view, the
lack of a carry flag is a PITA if you desire to
do multi-word mathematical manipulation of numbers.
So it seems, that the RISC-V architecture is intended
to be used by compilers generating code from high level
languages.
Am 15.07.2025 um 17:25 schrieb LIT:
Now riscv is the future.
I don't know. From what I learned, RISC-V
is strongly compiler-oriented. They wrote,
for example, that it lacks any condition codes.
Only conditional branches are predicated on
examining the contents of registers at the time
of the branch. No "add with carry" nor "subtract
with carry". From an assembly point of view, the
lack of a carry flag is a PITA if you desire to
do multi-word mathematical manipulation of numbers.
So it seems, that the RISC-V architecture is intended
to be used by compilers generating code from high level
languages.
I read somewhere:
The standard is now managed by RISC-V International, which
has more than 3,000 members and which reported that more
than 10 billion chips containing RISC-V cores had shipped
by the end of 2022. Many implementations of RISC-V are
available, both as open-source cores and as commercial
IP products.
You call that compiler-oriented???
On 16/07/2025 12:09 pm, minforth wrote:
Am 15.07.2025 um 17:25 schrieb LIT:
Now riscv is the future.
I don't know. From what I learned, RISC-V
is strongly compiler-oriented. They wrote,
for example, that it lacks any condition codes.
Only conditional branches are predicated on
examining the contents of registers at the time
of the branch. No "add with carry" nor "subtract
with carry". From an assembly point of view, the
lack of a carry flag is a PITA if you desire to
do multi-word mathematical manipulation of numbers.
So it seems, that the RISC-V architecture is intended
to be used by compilers generating code from high level
languages.
I read somewhere:
The standard is now managed by RISC-V International, which
has more than 3,000 members and which reported that more
than 10 billion chips containing RISC-V cores had shipped
by the end of 2022. Many implementations of RISC-V are
available, both as open-source cores and as commercial
IP products.
You call that compiler-oriented???
It depends on how many are being programmed by the likes of GCC.
When ATMEL hit the market the manufacturer claimed their chips
were designed with compilers in mind. Do Arduino users program
in hand-coded assembler? Do you? It's no longer just the chip's
features and theoretical performance one has to worry about but
the compilers too.
Now riscv is the future.
I don't know. From what I learned, RISC-V
is strongly compiler-oriented. They wrote,
for example, that it lacks any condition codes.
Only conditional branches are predicated on
examining the contents of registers at the time
of the branch. No "add with carry" nor "subtract
with carry". From an assembly point of view, the
lack of a carry flag is a PITA if you desire to
do multi-word mathematical manipulation of numbers.
So it seems, that the RISC-V architecture is intended
to be used by compilers generating code from high level
languages.
I read somewhere:
The standard is now managed by RISC-V International, which
has more than 3,000 members and which reported that more
than 10 billion chips containing RISC-V cores had shipped
by the end of 2022. Many implementations of RISC-V are
available, both as open-source cores and as commercial
IP products.
You call that compiler-oriented???
It depends on how many are being programmed by the likes of GCC.
When ATMEL hit the market the manufacturer claimed their chips
were designed with compilers in mind. Do Arduino users program
in hand-coded assembler? Do you? It's no longer just the chip's
features and theoretical performance one has to worry about but
the compilers too.
On Mon, 14 Jul 2025 7:50:04 +0000, Anton Ertl wrote:
C) Perform tree addition
a) Using 80-bit addition. This will be faster than sequential
addition because in many cases several additions can run in
parallel. It will also be quite accurate because it uses 80-bit
addition, and because the addition chains are reduced to
ld(length(vector)).
This looks very interesting. I can find Kahan and Neumaier, but
"tree addition" didn't turn up (There is a suspicious looking
reliability paper about the approach which surely is not what
you meant). Or is it pairwise addition what I should look for?
I did not do any accuracy measurements, but I did performance
measurements on a Ryzen 5800X:
cycles:u
gforth-fast iforth lxf SwiftForth VFX 3_057_979_501 6_482_017_334 6_087_130_593 6_021_777_424 6_034_560_441 NAI 6_601_284_920 6_452_716_125 7_001_806_497 6_606_674_147 6_713_703_069 UNR 3_787_327_724 2_949_273_264 1_641_710_689 7_437_654_901 1_298_257_315 REC 9_150_679_812 14_634_786_781 SR
cycles:u
gforth-fast iforth lxf SwiftForth VFX
13_113_842_702 6_264_132_870 9_011_308_923 11_011_828_048 8_072_637_768 NAI
6_802_702_884 2_553_418_501 4_238_099_417 11_277_658_203 3_244_590_981 UNR 9_370_432_755 4_489_562_792 4_955_679_285 12_283_918_226 3_915_367_813 REC
51_113_853_111 29_264_267_850 SR
I did not do any accuracy measurements, but I did performanceYMMV but "fast but wrong" would not be my goal. ;-)
measurements
But I decided to use a recursive approach (recursive-sum, REC) that
uses the largest 2^k<n as the left child and the rest as the right
child, and as base cases for the recursion use a straight-line
balanced-tree evaluation for 2^k with k<=7 (and combine these for n
that are not 2^k). For systems with tiny FP stacks, I added the
option to save intermediate results on a software stack in the
recursive word. Concerning the straight-line code, it turned out that
the highest k I could use on sf64 and vfx64 is 5 (corresponding to 6
FP stack items); it's not clear to me why; on lxf I can use k=7 (and
it uses the 387 stack, too).
Am 16.07.2025 um 13:25 schrieb Anton Ertl:
I did not do any accuracy measurements, but I did performanceYMMV but "fast but wrong" would not be my goal. ;-)
measurements
minforth <minforth@gmx.net> writes:
Am 16.07.2025 um 13:25 schrieb Anton Ertl:
I did not do any accuracy measurements, but I did performanceYMMV but "fast but wrong" would not be my goal. ;-)
measurements
I did test correctness with cases where roundoff errors do not play a
role.
As mentioned, the RECursive balanced-tree sum (which is also the
fastest on several systems and absolutely) is expected to be more
accurate in those cases where roundoff errors do play a role. But if
you care about that, better design a test and test it yourself. It
will be interesting to see how you find out which result is more
accurate when they differ.
It depends on how many are being programmed by the likes of GCC.
When ATMEL hit the market the manufacturer claimed their chips
were designed with compilers in mind. Do Arduino users program
in hand-coded assembler? Do you? It's no longer just the chip's
features and theoretical performance one has to worry about but
the compilers too.
Regarding features it's worth to mention
that ATMELs actually are quite nice to
program them in ML. Even, if they were
designed "with compilers in mind".
...
anton@mips.complang.tuwien.ac.at (Anton Ertl) writes:I have run this test now on my Ryzen 9950X for lxf, lxf64 ans a snapshot of gforth
I did not do any accuracy measurements, but I did performance
measurements on a Ryzen 5800X:
cycles:u
gforth-fast iforth lxf SwiftForth VFX 3_057_979_501 6_482_017_334 6_087_130_593 6_021_777_424 6_034_560_441 NAI
6_601_284_920 6_452_716_125 7_001_806_497 6_606_674_147 6_713_703_069 UNR
3_787_327_724 2_949_273_264 1_641_710_689 7_437_654_901 1_298_257_315 REC
9_150_679_812 14_634_786_781 SR
cycles:u
This second table is about instructions:u
gforth-fast iforth lxf SwiftForth VFX
13_113_842_702 6_264_132_870 9_011_308_923 11_011_828_048 8_072_637_768 NAI
6_802_702_884 2_553_418_501 4_238_099_417 11_277_658_203 3_244_590_981 UNR
9_370_432_755 4_489_562_792 4_955_679_285 12_283_918_226 3_915_367_813 REC
51_113_853_111 29_264_267_850 SR
- anton
Reminds me of the 6502 for some reason. But it's the 'skip next
instruction on bit in register' that throws me.
Didn't get that in the good old days as products were expected to
have a reasonable lifetime. Today CPU designs are as 'throw away'
as everything else. No reason to believe RISC-V will be different.
Only thing distinguishing it are the years of hype and promise.
Well, that is strange ...
Results with the current iForth are quite different:
FORTH> bench ( see file quoted above + usual iForth timing words )
\ 7963 times
\ naive-sum : 0.999 seconds elapsed. ( 4968257259 )
\ unrolled-sum : 1.004 seconds elapsed. ( 4968257259 )
\ recursive-sum : 0.443 seconds elapsed. ( 4968257259 )
\ shift-reduce-sum : 2.324 seconds elapsed. ( 4968257259 ) ok
Ryzen 9950X
lxf64
5,010,566,495 NAI cycles:u
2,011,359,782 UNR cycles:u
646,926,001 REC cycles:u
3,589,863,082 SR cycles:u
lxf64 =20
7,019,247,519 NAI instructions:u =20
4,128,689,843 UNR instructions:u =20
4,643,499,656 REC instructions:u=20
25,019,182,759 SR instructions:u=20
gforth-fast 20250219
2,048,316,578 NAI cycles:u
7,157,520,448 UNR cycles:u
3,589,638,677 REC cycles:u
17,199,889,916 SR cycles:u
gforth-fast 20250219
13,107,999,739 NAI instructions:u=20
6,789,041,049 UNR instructions:u
9,348,969,966 REC instructions:u=20
50,108,032,223 SR instructions:u=20
lxf
6,005,617,374 NAI cycles:u
6,004,157,635 UNR cycles:u
1,303,627,835 REC cycles:u
9,187,422,499 SR cycles:u
lxf
9,010,888,196 NAI instructions:u
4,237,679,129 UNR instructions:u=20
4,955,258,040 REC instructions:u=20
26,018,680,499 SR instructions:u
lxf uses the x87 builtin fp stack, lxf64 uses sse4 and a large fp stack=20
Meanwhile many years ago, comparative tests were carried out with a
couple of representative archived serial data (~50k samples)
Ultimately, Kahan summation
was the winner. It is slow, but there were no in-the-loop
requirements, so for a background task, Kahan was fast enough.
minforth <minforth@gmx.net> writes:
Meanwhile many years ago, comparative tests were carried out with a
couple of representative archived serial data (~50k samples)
Representative of what? Serial: what series?
peter <peter.noreply@tin.it> writes:
Ryzen 9950X
lxf64
5,010,566,495 NAI cycles:u
2,011,359,782 UNR cycles:u
646,926,001 REC cycles:u
3,589,863,082 SR cycles:u
lxf64 =20
7,019,247,519 NAI instructions:u =20
4,128,689,843 UNR instructions:u =20
4,643,499,656 REC instructions:u=20
25,019,182,759 SR instructions:u=20
gforth-fast 20250219
2,048,316,578 NAI cycles:u
7,157,520,448 UNR cycles:u
3,589,638,677 REC cycles:u
17,199,889,916 SR cycles:u
gforth-fast 20250219
13,107,999,739 NAI instructions:u=20
6,789,041,049 UNR instructions:u
9,348,969,966 REC instructions:u=20
50,108,032,223 SR instructions:u=20
lxf
6,005,617,374 NAI cycles:u
6,004,157,635 UNR cycles:u
1,303,627,835 REC cycles:u
9,187,422,499 SR cycles:u
lxf
9,010,888,196 NAI instructions:u
4,237,679,129 UNR instructions:u=20
4,955,258,040 REC instructions:u=20
26,018,680,499 SR instructions:u
lxf uses the x87 builtin fp stack, lxf64 uses sse4 and a large fp stack=20
Apparently the latency of ADDSD (SSE2) is down to 2 cycles on Zen5
(visible in lxf64 UNR and gforth-fast NAI) while the latency of FADD
(387) is still 6 cycles (lxf NAI and UNR). I have no explanation why
on lxf64 NAI performs so much worse than UNR, and in gforth-fast UNR
so much worse than NAI.
For REC the latency should not play a role. There lxf64 performs at
7.2IPC and 1.55 F+/cycle, whereas lxf performs only at 3.8IPC and 0.77 F+/cycle. My guess is that FADD can only be performed by one FPU, and
that's connected to one dispatch port, and other instructions also
need or are at least assigned to this dispatch port.
- anton
mhx@iae.nl (mhx) writes:[..]
Well, that is strange ...
The output should be the approximate number of seconds. Here's what I
get from the cycle:u numbers for iForth 5.1-mini given in the earlier postings:
\ ------------ input ---------- | output
6_482_017_334 scale 7 5 3 f.rdp 1.07534 ok
6_452_716_125 scale 7 5 3 f.rdp 1.07048 ok
2_949_273_264 scale 7 5 3 f.rdp 0.48927 ok
14_634_786_781 scale 7 5 3 f.rdp 2.42785 ok
The resulting numbers are not very different from those you show. My measurements include iForth's startup overhead, which may be one
explanation why they are a little higher.
dxf <dxforth@gmail.com> writes:
On 13/07/2025 7:01 pm, Anton Ertl wrote:
...
For Forth, Inc. and MPE AFAIK their respective IA-32 Forth system was
the only one with hardware FP for many years, so there probably was
little pressure from users for bit-identical results with, say, SPARC,
because they did not have a Forth system that ran on SPARC.
What do you mean by "bit-identical results"? Since SSE2 comes without
transcendentals (or basics such as FABS and FNEGATE) and implementers
are expected to supply their own, if anything, I expect results across
platforms and compilers to vary.
There are operations for which IEEE 754 specifies the result to the
last bit (except that AFAIK the representation of NaNs is not
specified exactly), among them F+ F- F* F/ FSQRT, probably also
FNEGATE and FABS. It does not specify the exact result for
transcendental functions, but if your implementation performs the same bit-exact operations for computing a transcendental function on two
IEEE 754 compliant platforms, the result will be bit-identical (if it
is a number). So just use the same implementations of transcentental functions, and your results will be bit-identical; concerning the
NaNs, if you find a difference, check if the involved values are NaNs.
So in mandating bit-identical results, not only in calculations but also >input/output
IEEE 754 is all about giving the illusion of truth in
floating-point when, if anything, they should be warning users don't be >fooled.
I did a test coding the sum128 as a code word with avx-512 instructions
and got the following results
285,584,376 cycles:u
941,856,077 instructions:u
timing was
timer-reset ' recursive-sum bench .elapsed 51 ms elapsed
so half the time of the original recursive.
with 32 zmm registers I could have done a sum256 also
; Horizontal sum of zmm0
vextractf64x4 ymm1, zmm0, 1
vaddpd ymm2, ymm1, ymm0
vextractf64x2 xmm3, ymm2, 1
vaddpd ymm4, ymm3, ymm2
vhaddpd xmm0, xmm4, xmm4
One way to deal with all that would be to have a long-vector stack and
have something like my vector wordset
<https://github.com/AntonErtl/vectors>, where the sum of a vector
would be a word that is implemented in some lower-level way (e.g.,
assembly language); the sum of a vector is actually a planned, but not
yet existing feature of this wordset.
peter <peter.noreply@tin.it> writes:
I did a test coding the sum128 as a code word with avx-512 instructions
and got the following results
285,584,376 cycles:u
941,856,077 instructions:u
timing was
timer-reset ' recursive-sum bench .elapsed 51 ms elapsed
so half the time of the original recursive.
with 32 zmm registers I could have done a sum256 also
One could do sum128 with just 8 registers by performing the adds ASAP,
i.e., for sum32
vmovapd zmm0, [rbx]
vmovapd zmm1, [rbx+64]
vaddpd zmm0, zmm0, zmm1
vmovapd zmm1, [rbx+128]
vmovapd zmm2, [rbx+192]
vaddpd zmm1, zmm1, zmm2
vaddpd zmm0, zmm0, zmm1
; and then the Horizontal sum
And you can code this as:
vmovapd zmm0, [rbx]
vaddpd zmm0, zmm0, [rbx+64]
vmovapd zmm1, [rbx+128]
vaddpd zmm1, zmm1, [rbx+192]
vaddpd zmm0, zmm0, zmm1
; and then the Horizontal sum
; Horizontal sum of zmm0
vextractf64x4 ymm1, zmm0, 1
vaddpd ymm2, ymm1, ymm0
vextractf64x2 xmm3, ymm2, 1
vaddpd ymm4, ymm3, ymm2
vhaddpd xmm0, xmm4, xmm4
Instead of doing the horizontal sum once for every sum128, it might be
more efficient (assuming the whole thing is not
cache-bandwidth-limited) to have the result of sum128 be a full SIMD
width, and then add them up with vaddpd instead of addsd, and do the horizontal sum once in the end.
But if the recursive part is to be programmed in Forth, we would need
a way to represent a SIMD width of data in Forth, maybe with a SIMD
stack. I see a few problems there:
* What to do about the mask registers of AVX-512? In the RISC-V
vector extension masks are stored in regular SIMD registers.
* There is a trend visible in ARM SVE and the RISC-V Vector extension
to have support for dealing with loops across longer vectors. Do we
also need to support something like that.
For the RISC-V vector extension, see <https://riscv.org/wp-content/uploads/2024/12/15.20-15.55-18.05.06.VEXT-bcn-v1.pdf>
One way to deal with all that would be to have a long-vector stack and
have something like my vector wordset
<https://github.com/AntonErtl/vectors>, where the sum of a vector
would be a word that is implemented in some lower-level way (e.g.,
assembly language); the sum of a vector is actually a planned, but not
yet existing feature of this wordset.
An advantage of having a (short) SIMD stack would be that one could
use SIMD operations for other uses where the long-vector wordset looks
too heavy-weight (or would need optimizations to get rid of the
long-vector overhead). The question is if enough such uses exist to
justify adding such a stack.
- anton
On Sat, 19 Jul 2025 10:18:15 GMT[sum32][
anton@mips.complang.tuwien.ac.at (Anton Ertl) wrote:
vmovapd zmm0, [rbx]
vaddpd zmm0, zmm0, [rbx+64]
vmovapd zmm1, [rbx+128]
vaddpd zmm1, zmm1, [rbx+192]
vaddpd zmm0, zmm0, zmm1
; and then the Horizontal sum
; Horizontal sum of zmm0
vextractf64x4 ymm1, zmm0, 1
vaddpd ymm2, ymm1, ymm0
vextractf64x2 xmm3, ymm2, 1
vaddpd ymm4, ymm3, ymm2
vhaddpd xmm0, xmm4, xmm4
the simd instructions does also take a memory operand
I can du sum128 as
code asum128b
movsd [r13-0x8], xmm0
lea r13, [r13-0x8]
vmovapd zmm0, [rbx]
vaddpd zmm0, zmm0, [rbx+64]
vaddpd zmm0, zmm0, [rbx+128]
vaddpd zmm0, zmm0, [rbx+192]
vaddpd zmm0, zmm0, [rbx+256]
vaddpd zmm0, zmm0, [rbx+320]
vaddpd zmm0, zmm0, [rbx+384]
vaddpd zmm0, zmm0, [rbx+448]
vaddpd zmm0, zmm0, [rbx+512]
vaddpd zmm0, zmm0, [rbx+576]
vaddpd zmm0, zmm0, [rbx+640]
vaddpd zmm0, zmm0, [rbx+704]
vaddpd zmm0, zmm0, [rbx+768]
vaddpd zmm0, zmm0, [rbx+832]
vaddpd zmm0, zmm0, [rbx+896]
vaddpd zmm0, zmm0, [rbx+960]
Am 19.07.2025 um 12:18 schrieb Anton Ertl:
One way to deal with all that would be to have a long-vector stack and
have something like my vector wordset
<https://github.com/AntonErtl/vectors>, where the sum of a vector
would be a word that is implemented in some lower-level way (e.g.,
assembly language); the sum of a vector is actually a planned, but not
yet existing feature of this wordset.
Not wanting to sound negative, but who in practice adds up long
vectors, apart from testing compilers and fp-arithmetic?
Dot products, on the other hand, are fundamental for many linear
algebra algorithms, eg. matrix multiplication and AI.
dxf <dxforth@gmail.com> writes:
So in mandating bit-identical results, not only in calculations but also
input/output
I don't think that IEEE 754 specifies I/O, but I could be wrong.
IEEE 754 is all about giving the illusion of truth in
floating-point when, if anything, they should be warning users don't be
fooled.
I don't think that IEEE 754 mentions truth. It does, however, specify
the inexact "exception" (actually a flag), which allows you to find
out if the results of the computations are exact or if some rounding
was involved.
minforth <minforth@gmx.net> writes:
Am 19.07.2025 um 12:18 schrieb Anton Ertl:
One way to deal with all that would be to have a long-vector stack and
have something like my vector wordset
<https://github.com/AntonErtl/vectors>, where the sum of a vector
would be a word that is implemented in some lower-level way (e.g.,
assembly language); the sum of a vector is actually a planned, but not
yet existing feature of this wordset.
Not wanting to sound negative, but who in practice adds up long
vectors, apart from testing compilers and fp-arithmetic?
Everyone who does dot-products.
Dot products, on the other hand, are fundamental for many linear
algebra algorithms, eg. matrix multiplication and AI.
If I add a vector-sum word
df+red ( dfv -- r )
\ r is the sum of the elements of dfv
to the vector wordset, then the dot-product is:
: dot-product ( dfv1 dfv2 -- r )
df*v df+red ;
Concerning matrix multiplication, while you can use the dot-product
for it, there are many other ways to do it, and some are more
efficient (although, admittedly, I have not used pairwise addition for
these ways).
AFAICS IEEE 754 offers nothing particularly useful for the end-user.
Either one's fp application works - or it doesn't. IEEE hasn't
changed that.
IEEE's relevance is that it spurred Intel into making an FPU which in
turn made implementing fp easy.
dxf <dxforth@gmail.com> writes:
AFAICS IEEE 754 offers nothing particularly useful for the end-user.
Either one's fp application works - or it doesn't. IEEE hasn't
changed that.
The purpose of IEEE FP was to improve the numerical accuracy of
applications that used it as opposed to other formats.
IEEE's relevance is that it spurred Intel into making an FPU which in
turn made implementing fp easy.
Exactly the opposite, Intel decided that it wanted to make an FPU and it wanted the FPU to have the best FP arithmetic possible. So it
commissioned Kahan (a renowned FP expert) to design the FP format.
Kahan said "Why not use the VAX format? It is pretty good". Intel said
it didn't want pretty good, it wanted the best, so Kahan said "ok" and designed the 8087 format.
The IEEE standardization process happened AFTER the 8087 was already in progress. Other manufacturers signed onto it, some of them overcoming initial resistance, after becoming convinced that it was the right
thing.
http://people.eecs.berkeley.edu/~wkahan/ieee754status/754story.html
mhx wrote:
On Sun, 6 Oct 2024 7:51:31 +0000, dxf wrote:
Is there an easier way of doing this? End goal is a double number representing centi-secs.
empty decimal
: SPLIT ( a u c -- a2 u2 a3 u3 ) >r 2dup r> scan 2swap 2 pick - ;
: >INT ( adr len -- u ) 0 0 2swap >number 2drop drop ;
: /T ( a u -- $hour $min $sec )
2 0 do [char] : split 2swap dup if 1 /string then loop
2 0 do dup 0= if 2rot 2rot then loop ;
: .T 2swap 2rot cr >int . ." hr " >int . ." min " >int . ." sec " ;
s" 1:2:3" /t .t
s" 02:03" /t .t
s" 03" /t .t
s" 23:59:59" /t .t
s" 0:00:03" /t .t
Why don't you use the fact that >NUMBER returns the given
string starting with the first unconverted character?
SPLIT should be redundant.
-marcel
: CHAR-NUMERIC? 48 58 WITHIN ;
: SKIP-NON-NUMERIC ( adr u -- adr2 u2)
BEGIN
DUP IF OVER C@ CHAR-NUMERIC? NOT ELSE 0 THEN
WHILE
1 /STRING
REPEAT ;
: SCAN-NEXT-NUMBER ( n adr len -- n2 adr2 len2)
2>R 60 * 0. 2R> >NUMBER
2>R D>S + 2R> ;
: PARSE-TIME ( adr len -- seconds)
0 -ROT
BEGIN
SKIP-NON-NUMERIC
DUP
WHILE
SCAN-NEXT-NUMBER
REPEAT
2DROP ;
S" hello 1::36 world" PARSE-TIME CR .
96 ok
: get-number ( accum adr len -- accum' adr' len' )
{ adr len }
0. adr len >number { adr' len' }
len len' =
if
2drop adr len 1 /string
else
d>s swap 60 * +
adr' len'
then ;
: parse-time ( adr len -- seconds)
0 -rot
begin
dup
while
get-number
repeat
2drop ;
s" foo-bar" parse-time . 0
s" foo55bar" parse-time . 55
s" foo 1 bar 55 zoo" parse-time . 155
...
: get-number ( accum adr len -- accum' adr' len' )
{ adr len }
0. adr len >number { adr' len' }
len len' =
if
2drop adr len 1 /string
else
d>s swap 60 * +
adr' len'
then ;
: parse-time ( adr len -- seconds)
0 -rot
begin
dup
while
get-number
repeat
2drop ;
s" foo-bar" parse-time . 0
s" foo55bar" parse-time . 55
s" foo 1 bar 55 zoo" parse-time . 155
s" and9foo 1 bar 55 zoo" parse-time . 32515
Sysop: | DaiTengu |
---|---|
Location: | Appleton, WI |
Users: | 1,064 |
Nodes: | 10 (0 / 10) |
Uptime: | 153:17:52 |
Calls: | 13,691 |
Calls today: | 1 |
Files: | 186,936 |
D/L today: |
2,526 files (731M bytes) |
Messages: | 2,411,054 |