• MU*

    From dxforth@21:1/5 to All on Thu Jul 6 14:10:52 2023
    Many forths define MU/MOD or equivalent:

    : MU/MOD ( ud u -- urem udquot )
    tuck 0 swap um/mod >r swap um/mod r> ;

    Chances are if an app needs divide it will also need multiply. With no logical equivalent most use M*/ to do the multiply. However it's a beast of a function.
    Preferring something leaner I defined:

    \ Multiply double by single leaving double.
    : MU* ( ud1 u -- ud2 )
    tuck * >r um* r> + ;

    AFAIK MU* also works with signed inputs which is a bonus. I never cared for the name MU/MOD but with the addition of companion function MU* I'm warming
    to it.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Anton Ertl@21:1/5 to dxforth on Thu Jul 6 05:45:56 2023
    dxforth <dxforth@gmail.com> writes:
    Many forths define MU/MOD or equivalent:

    : MU/MOD ( ud u -- urem udquot )
    tuck 0 swap um/mod >r swap um/mod r> ;

    Chances are if an app needs divide it will also need multiply.

    double/single->double division exists for one reason: to serve as
    factor of #. E.g., in Gforth:

    : # ( ud1 -- ud2 ) \ core number-sign
    \G Used between @code{<<#} and @code{#>}. Prepend the
    \G least-significant digit (according to @code{base}) of @var{ud1}
    \G to the pictured numeric output string. @var{ud2} is
    \G @var{ud1/base}, i.e., the number representing the remaining
    \G digits.
    \ special-casing base=#10 does not pay off:
    \ <2022Mar11.130937@mips.complang.tuwien.ac.at>
    base @ ud/mod rot dup 9 u>
    [ char A char 9 1+ - ] Literal and +
    '0' + hold ;

    Unsurprisingly, >NUMBER needs a double-by-single->double
    multiplication.


    With no logical
    equivalent most use M*/ to do the multiply.

    I would find that surprising. Gforth uses a >NUMBER factor called
    ACCUMULATE:

    : accumulate ( +d0 addr digit - +d1 addr )
    swap >r swap base @ um* drop rot base @ um* d+ r> ;

    SwiftForth puts the following line directly in >NUMBER:

    SWAP >R 2SWAP BASE @ * SWAP BASE @ UM* D+ ROT 1+

    So what makes you think that "most use M*/"?

    However it's a beast of a function.
    Preferring something leaner I defined:

    \ Multiply double by single leaving double.
    : MU* ( ud1 u -- ud2 )
    tuck * >r um* r> + ;

    AFAIK MU* also works with signed inputs which is a bonus.

    Maybe it works with a signed double operand, but certainly not with a
    signed single operand:

    #-5. 3 mu* d. -15 ok
    #5. -3 mu* d. 92233720368547758065 ok
    #-5. -3 mu* d. -92233720368547758065 ok

    - anton
    --
    M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
    comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
    New standard: https://forth-standard.org/
    EuroForth 2023: https://euro.theforth.net/2023

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From dxforth@21:1/5 to Anton Ertl on Thu Jul 6 17:40:32 2023
    On 6/07/2023 3:45 pm, Anton Ertl wrote:
    dxforth <dxforth@gmail.com> writes:
    Many forths define MU/MOD or equivalent:

    : MU/MOD ( ud u -- urem udquot )
    tuck 0 swap um/mod >r swap um/mod r> ;

    Chances are if an app needs divide it will also need multiply.

    double/single->double division exists for one reason: to serve as
    factor of #. E.g., in Gforth:

    : # ( ud1 -- ud2 ) \ core number-sign
    \G Used between @code{<<#} and @code{#>}. Prepend the
    \G least-significant digit (according to @code{base}) of @var{ud1}
    \G to the pictured numeric output string. @var{ud2} is
    \G @var{ud1/base}, i.e., the number representing the remaining
    \G digits.
    \ special-casing base=#10 does not pay off:
    \ <2022Mar11.130937@mips.complang.tuwien.ac.at>
    base @ ud/mod rot dup 9 u>
    [ char A char 9 1+ - ] Literal and +
    '0' + hold ;

    Unsurprisingly, >NUMBER needs a double-by-single->double
    multiplication.


    With no logical
    equivalent most use M*/ to do the multiply.

    I would find that surprising. Gforth uses a >NUMBER factor called ACCUMULATE:

    : accumulate ( +d0 addr digit - +d1 addr )
    swap >r swap base @ um* drop rot base @ um* d+ r> ;

    SwiftForth puts the following line directly in >NUMBER:

    SWAP >R 2SWAP BASE @ * SWAP BASE @ UM* D+ ROT 1+

    So what makes you think that "most use M*/"?

    The Standard specifies it? Not every forth factored out MU/MOD and even
    less so for MU*.

    However it's a beast of a function.
    Preferring something leaner I defined:

    \ Multiply double by single leaving double.
    : MU* ( ud1 u -- ud2 )
    tuck * >r um* r> + ;

    AFAIK MU* also works with signed inputs which is a bonus.

    Maybe it works with a signed double operand, but certainly not with a
    signed single operand:

    #-5. 3 mu* d. -15 ok
    #5. -3 mu* d. 92233720368547758065 ok
    #-5. -3 mu* d. -92233720368547758065 ok

    Good to remember. Luckily negative multipliers like negative divisors
    tend to be rare.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From none) (albert@21:1/5 to Anton Ertl on Thu Jul 6 09:59:05 2023
    In article <2023Jul6.074556@mips.complang.tuwien.ac.at>,
    Anton Ertl <anton@mips.complang.tuwien.ac.at> wrote:
    dxforth <dxforth@gmail.com> writes:
    <SNIP>
    AFAIK MU* also works with signed inputs which is a bonus.

    Maybe it works with a signed double operand, but certainly not with a
    signed single operand:

    #-5. 3 mu* d. -15 ok
    #5. -3 mu* d. 92233720368547758065 ok
    #-5. -3 mu* d. -92233720368547758065 ok

    Maybe it happens to work with *his* implementation/ Forth du jour.
    This stresses the need for a specification of mu* and to check
    each use against the specification.
    If it is true what he says, he is well advised to change the name into
    mu|s* or some such.
    Alternatively he can do in a separate porting.frt file
    "
    'mu* ALIAS ms* \ WARNING: this only works on xxxforth.
    "
    and only use ms* exclusively on signed values.

    Otherwise this is a recipe for disaster.


    - anton

    Groetjes Albert
    --
    Don't praise the day before the evening. One swallow doesn't make spring.
    You must not say "hey" before you have crossed the bridge. Don't sell the
    hide of the bear until you shot it. Better one bird in the hand than ten in
    the air. First gain is a cat spinning. - the Wise from Antrim -

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Heinrich Hohl@21:1/5 to dxforth on Thu Jul 6 02:55:31 2023
    On Thursday, July 6, 2023 at 6:10:55 AM UTC+2, dxforth wrote:
    \ Multiply double by single leaving double.
    : MU* ( ud1 u -- ud2 )
    tuck * >r um* r> + ;

    AFAIK MU* also works with signed inputs which is a bonus. I never cared for the name MU/MOD but with the addition of companion function MU* I'm warming to it.

    As others have pointed out already, your MU* routine is for unsigned numbers. Your stack comment is correct.

    I am using the following definitions for this kind of multiplication:

    : UD* ( ud1 u -- ud2)
    TUCK * >R UM* R> + ;
    \ unsigned multiplication with truncated result

    : D* ( d1 n -- d2)
    DUP>R ABS UD* R> 0< IF DNEGATE THEN ;
    \ signed multiplication with truncated result

    Henry

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Anton Ertl@21:1/5 to albert@cherry. on Thu Jul 6 10:06:15 2023
    albert@cherry.(none) (albert) writes:
    In article <2023Jul6.074556@mips.complang.tuwien.ac.at>,
    Anton Ertl <anton@mips.complang.tuwien.ac.at> wrote:
    dxforth <dxforth@gmail.com> writes:
    <SNIP>

    unsnip:

    : MU* ( ud1 u -- ud2 )
    tuck * >r um* r> + ;
    ...
    AFAIK MU* also works with signed inputs which is a bonus.

    Maybe it works with a signed double operand, but certainly not with a >>signed single operand:

    #-5. 3 mu* d. -15 ok
    #5. -3 mu* d. 92233720368547758065 ok
    #-5. -3 mu* d. -92233720368547758065 ok

    Maybe it happens to work with *his* implementation/ Forth du jour.

    Not if the words he uses for defining MU* work correctly. All the
    words that he uses are total (defined for all inputs) on
    twos-complement Forth systems, and therefore work the same way.

    This stresses the need for a specification of mu* and to check
    each use against the specification.

    I unsnipped his specification. Of course, a user-oriented
    specification would also be a good idea, and once you have that, it
    has to be checked against the implementation specification.

    - anton
    --
    M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
    comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
    New standard: https://forth-standard.org/
    EuroForth 2023: https://euro.theforth.net/2023

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From dxforth@21:1/5 to Heinrich Hohl on Thu Jul 6 20:32:30 2023
    On 6/07/2023 7:55 pm, Heinrich Hohl wrote:
    On Thursday, July 6, 2023 at 6:10:55 AM UTC+2, dxforth wrote:
    \ Multiply double by single leaving double.
    : MU* ( ud1 u -- ud2 )
    tuck * >r um* r> + ;

    AFAIK MU* also works with signed inputs which is a bonus. I never cared for >> the name MU/MOD but with the addition of companion function MU* I'm warming >> to it.

    As others have pointed out already, your MU* routine is for unsigned numbers. Your stack comment is correct.

    I am using the following definitions for this kind of multiplication:

    : UD* ( ud1 u -- ud2)
    TUCK * >R UM* R> + ;
    \ unsigned multiplication with truncated result

    Would like to see evidence signed multiplicand doesn't work before ruling it out.
    200x has mandated 2's complement arithmetic. It would be amiss not to exploit it.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Heinrich Hohl@21:1/5 to dxforth on Thu Jul 6 07:53:27 2023
    On Thursday, July 6, 2023 at 12:32:32 PM UTC+2, dxforth wrote:
    On 6/07/2023 7:55 pm, Heinrich Hohl wrote:

    : UD* ( ud1 u -- ud2)
    TUCK * >R UM* R> + ;
    \ unsigned multiplication with truncated result
    Would like to see evidence signed multiplicand doesn't work before ruling it out.
    200x has mandated 2's complement arithmetic. It would be amiss not to exploit it.

    Note that this is multiplication with a truncated result. This means that we may lose
    sign information of the input arguments partly or fully.

    The UD* routine works correctly with the full range of unsigned numbers.

    Due to truncation and loss of sign information, it also happens to work with a limited range
    of signed numbers. This means that the following stack comment is also correct:

    : UD* ( d1 n+ -- d2)
    TUCK * >R UM* R> + ;

    In this case, d1 and d2 have full range, but n is limited to 0...$7FFF (in 16 bits),
    i.e. it must be non-negative. It makes little sense to call such a routine "signed",
    because not all arguments may use the full range of signed numbers.

    With my example for D* shown above, all signed numbers may use the full range. Results will be correct, but of course they will be truncated if we exceed the permitted
    range for d2.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to Heinrich Hohl on Thu Jul 6 13:50:40 2023
    Heinrich Hohl schrieb am Donnerstag, 6. Juli 2023 um 16:53:30 UTC+2:
    On Thursday, July 6, 2023 at 12:32:32 PM UTC+2, dxforth wrote:
    On 6/07/2023 7:55 pm, Heinrich Hohl wrote:

    : UD* ( ud1 u -- ud2)
    TUCK * >R UM* R> + ;
    \ unsigned multiplication with truncated result
    Would like to see evidence signed multiplicand doesn't work before ruling it out.
    200x has mandated 2's complement arithmetic. It would be amiss not to exploit it.
    Note that this is multiplication with a truncated result. This means that we may lose
    sign information of the input arguments partly or fully.

    I am curious about which CPU made this convolution necessary.
    Most main stream CPUs nowadays already support mixed integer multiplication and division.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From dxforth@21:1/5 to Heinrich Hohl on Fri Jul 7 12:55:58 2023
    On 7/07/2023 12:53 am, Heinrich Hohl wrote:
    On Thursday, July 6, 2023 at 12:32:32 PM UTC+2, dxforth wrote:
    On 6/07/2023 7:55 pm, Heinrich Hohl wrote:

    : UD* ( ud1 u -- ud2)
    TUCK * >R UM* R> + ;
    \ unsigned multiplication with truncated result
    Would like to see evidence signed multiplicand doesn't work before ruling it out.
    200x has mandated 2's complement arithmetic. It would be amiss not to exploit it.

    Note that this is multiplication with a truncated result. This means that we may lose
    sign information of the input arguments partly or fully.

    The UD* routine works correctly with the full range of unsigned numbers.

    Due to truncation and loss of sign information, it also happens to work with a limited range
    of signed numbers. This means that the following stack comment is also correct:

    : UD* ( d1 n+ -- d2)
    TUCK * >R UM* R> + ;

    In this case, d1 and d2 have full range, but n is limited to 0...$7FFF (in 16 bits),
    i.e. it must be non-negative. It makes little sense to call such a routine "signed",
    because not all arguments may use the full range of signed numbers.

    I believe the correct stack comment is:

    UD* ( d|ud1 u -- d|ud2)

    I consider a routine 'signed' if any of the inputs in use are explicitly negative.
    By your definition M*/ would not be signed.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Heinrich Hohl@21:1/5 to dxforth on Fri Jul 7 00:55:06 2023
    On Friday, July 7, 2023 at 4:56:00 AM UTC+2, dxforth wrote:
    I believe the correct stack comment is:

    UD* ( d|ud1 u -- d|ud2)

    No. Try a few examples, e.g.
    3. 5 UD* UD. ---> 15, correct result
    -3. 5 UD* D. ---> -15, correct result
    but
    3. -5 UD* gives wrong result, with UD. as well as with D.
    -3. -5 UD* gives wrong result, with UD. as well as with D.

    So the correct stack comments are
    UD* (ud1 u -- ud2) or (d1 n+ -- d2)

    Note that "n+" is not the same as "u"; n+ is the lower 50% of range
    of an unsigned number.

    I consider a routine 'signed' if any of the inputs in use are explicitly negative.
    By your definition M*/ would not be signed.

    This is in the eye of the beholder. In my eyes M*/ is not a fully signed routine.

    In M*/ ( d1 n1 +n2 -- d2), the divisor n2 must not be negative.
    The Forth Standard allows this restriction because negative n2
    does not seem to be required in typical applications.

    I can live with that, although I would have preferred the requirement
    ( d1 n1 n2 -- d2) for reasons of consistency. Much easier to memorize.

    My own rule for math operators is:
    A leading "U" signals that all parameter (input and output) are unsigned
    with full range of unsigned numbers.
    It there is no leading "U", this signals that all parameters (input and output) are signed with full range of signed numbers.

    This is how I defined my own library of math operators, and the related
    stack effects are very easy to memorize. I generally start with the unsigned operator. Then I define the related signed operator by calculating the overall sign of the result first, then forming absolute values of the input parameters and forwarding these to the unsigned routine. In the last step the overall sign is applied to the unsigned result.

    Examples: UD* ( ud1 u -- ud2 ) and D* ( d1 n -- d2) as shown in my fist post
    of this thread.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From dxforth@21:1/5 to Heinrich Hohl on Fri Jul 7 20:03:50 2023
    On 7/07/2023 5:55 pm, Heinrich Hohl wrote:
    On Friday, July 7, 2023 at 4:56:00 AM UTC+2, dxforth wrote:
    I believe the correct stack comment is:

    UD* ( d|ud1 u -- d|ud2)

    No. Try a few examples, e.g.
    3. 5 UD* UD. ---> 15, correct result
    -3. 5 UD* D. ---> -15, correct result
    but
    3. -5 UD* gives wrong result, with UD. as well as with D.
    -3. -5 UD* gives wrong result, with UD. as well as with D.

    I'm not seeing any wrong results. -5 isn't in the range of an unsigned
    number, but we'll use it as there exists a 2's complement unsigned
    equivalent. Using a 16-bit forth...

    : UD. <# #s #> type space ;
    : .EV cr 2dup type evaluate ;

    3. 2constant A
    -5 constant B
    -3. 2constant C

    : t1 s" A d. B u. A B ud* d. " .ev ;
    : t2 s" C d. B u. C B ud* d. " .ev ;
    : t3 s" A ud. B u. A B ud* ud. " .ev ;
    : t4 s" C ud. B u. C B ud* ud. " .ev ;

    : test cr t1 t2 cr t3 t4 ;

    test

    A d. B u. A B ud* d. 3 65531 196593
    C d. B u. C B ud* d. -3 65531 -196593

    A ud. B u. A B ud* ud. 3 65531 196593
    C ud. B u. C B ud* ud. 4294967293 65531 4294770703

    The last fails due to user error (overflow) otherwise the results are per
    the stack spec I gave.

    I consider a routine 'signed' if any of the inputs in use are explicitly negative.
    By your definition M*/ would not be signed.

    This is in the eye of the beholder. In my eyes M*/ is not a fully signed routine.

    In M*/ ( d1 n1 +n2 -- d2), the divisor n2 must not be negative.
    The Forth Standard allows this restriction because negative n2
    does not seem to be required in typical applications.

    I can live with that, although I would have preferred the requirement
    ( d1 n1 n2 -- d2) for reasons of consistency. Much easier to memorize.

    My own rule for math operators is:
    A leading "U" signals that all parameter (input and output) are unsigned
    with full range of unsigned numbers.
    It there is no leading "U", this signals that all parameters (input and output)
    are signed with full range of signed numbers.

    This is how I defined my own library of math operators, and the related
    stack effects are very easy to memorize. I generally start with the unsigned operator. Then I define the related signed operator by calculating the overall
    sign of the result first, then forming absolute values of the input parameters
    and forwarding these to the unsigned routine. In the last step the overall sign
    is applied to the unsigned result.

    Examples: UD* ( ud1 u -- ud2 ) and D* ( d1 n -- d2) as shown in my fist post
    of this thread.

    I'm not sure there exists a naming system that covers every possibility - in particular words such as UD* which exhibit useful side behaviours. I accepted MU/MOD as it was historic and in common use. When it became plain I should have a corresponding word for multiply I named it MU* for sake of mnemonic consistency.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From none) (albert@21:1/5 to hheinrich.hohl@gmail.com on Fri Jul 7 14:35:22 2023
    In article <ecafaa3e-d359-46b5-8dc0-e996e44cbd3dn@googlegroups.com>,
    Heinrich Hohl <hheinrich.hohl@gmail.com> wrote:
    On Friday, July 7, 2023 at 4:56:00 AM UTC+2, dxforth wrote:
    I believe the correct stack comment is:

    UD* ( d|ud1 u -- d|ud2)

    No. Try a few examples, e.g.
    3. 5 UD* UD. ---> 15, correct result
    -3. 5 UD* D. ---> -15, correct result
    but
    3. -5 UD* gives wrong result, with UD. as well as with D.
    -3. -5 UD* gives wrong result, with UD. as well as with D.

    So the correct stack comments are
    UD* (ud1 u -- ud2) or (d1 n+ -- d2)

    Note that "n+" is not the same as "u"; n+ is the lower 50% of range
    of an unsigned number.

    I consider a routine 'signed' if any of the inputs in use are
    explicitly negative.
    By your definition M*/ would not be signed.

    This is in the eye of the beholder. In my eyes M*/ is not a fully signed >routine.

    In M*/ ( d1 n1 +n2 -- d2), the divisor n2 must not be negative.
    The Forth Standard allows this restriction because negative n2
    does not seem to be required in typical applications.

    I can live with that, although I would have preferred the requirement
    ( d1 n1 n2 -- d2) for reasons of consistency. Much easier to memorize.

    My own rule for math operators is:
    A leading "U" signals that all parameter (input and output) are unsigned
    with full range of unsigned numbers.
    It there is no leading "U", this signals that all parameters (input and output)
    are signed with full range of signed numbers.

    This is how I defined my own library of math operators, and the related
    stack effects are very easy to memorize. I generally start with the unsigned >operator. Then I define the related signed operator by calculating the overall >sign of the result first, then forming absolute values of the input parameters >and forwarding these to the unsigned routine. In the last step the overall sign
    is applied to the unsigned result.

    Examples: UD* ( ud1 u -- ud2 ) and D* ( d1 n -- d2) as shown in
    my fist post
    of this thread.

    My take on this is as follows: defaults signed unless a prefix U.
    Prefix U then all operands all unsigned.
    A prefix M means mixed mode
    * single to double (what else)
    / double single (what else)
    result to single single.

    Inserting a D in front op de operator:
    For multiplication this make only sense for inputs, one operand goes
    to double.
    For division this make only sense for outputs,
    as this makes no sense for the remainder as the divisor is single.
    It applies to the quotient,

    So at last I can remember everything.
    The FIGForth M/MOD has to be renamed to UDM/MOD .

    I have no patience for mixing unsigned and signed.
    This is a recipe for disaster. If it were to be applicable,
    I'd define a separate word in the preamble of the program,
    with a ton of comment.

    Groetjes Albert
    --
    Don't praise the day before the evening. One swallow doesn't make spring.
    You must not say "hey" before you have crossed the bridge. Don't sell the
    hide of the bear until you shot it. Better one bird in the hand than ten in
    the air. First gain is a cat spinning. - the Wise from Antrim -

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From minforth@21:1/5 to none albert on Fri Jul 7 06:27:50 2023
    none albert schrieb am Freitag, 7. Juli 2023 um 14:35:25 UTC+2:
    In article <ecafaa3e-d359-46b5...@googlegroups.com>,
    Heinrich Hohl <hheinri...@gmail.com> wrote:
    On Friday, July 7, 2023 at 4:56:00 AM UTC+2, dxforth wrote:
    I believe the correct stack comment is:

    UD* ( d|ud1 u -- d|ud2)

    No. Try a few examples, e.g.
    3. 5 UD* UD. ---> 15, correct result
    -3. 5 UD* D. ---> -15, correct result
    but
    3. -5 UD* gives wrong result, with UD. as well as with D.
    -3. -5 UD* gives wrong result, with UD. as well as with D.

    So the correct stack comments are
    UD* (ud1 u -- ud2) or (d1 n+ -- d2)

    Note that "n+" is not the same as "u"; n+ is the lower 50% of range
    of an unsigned number.

    I consider a routine 'signed' if any of the inputs in use are
    explicitly negative.
    By your definition M*/ would not be signed.

    This is in the eye of the beholder. In my eyes M*/ is not a fully signed >routine.

    In M*/ ( d1 n1 +n2 -- d2), the divisor n2 must not be negative.
    The Forth Standard allows this restriction because negative n2
    does not seem to be required in typical applications.

    I can live with that, although I would have preferred the requirement
    ( d1 n1 n2 -- d2) for reasons of consistency. Much easier to memorize.

    My own rule for math operators is:
    A leading "U" signals that all parameter (input and output) are unsigned >with full range of unsigned numbers.
    It there is no leading "U", this signals that all parameters (input and output)
    are signed with full range of signed numbers.

    This is how I defined my own library of math operators, and the related >stack effects are very easy to memorize. I generally start with the unsigned
    operator. Then I define the related signed operator by calculating the overall
    sign of the result first, then forming absolute values of the input parameters
    and forwarding these to the unsigned routine. In the last step the overall sign
    is applied to the unsigned result.

    Examples: UD* ( ud1 u -- ud2 ) and D* ( d1 n -- d2) as shown in
    my fist post
    of this thread.
    My take on this is as follows: defaults signed unless a prefix U.
    Prefix U then all operands all unsigned.
    A prefix M means mixed mode
    * single to double (what else)
    / double single (what else)
    result to single single.

    Inserting a D in front op de operator:
    For multiplication this make only sense for inputs, one operand goes
    to double.
    For division this make only sense for outputs,
    as this makes no sense for the remainder as the divisor is single.
    It applies to the quotient,

    So at last I can remember everything.
    The FIGForth M/MOD has to be renamed to UDM/MOD .

    I have no patience for mixing unsigned and signed.
    This is a recipe for disaster. If it were to be applicable,
    I'd define a separate word in the preamble of the program,
    with a ton of comment.

    +1

    For 64-bit Forths it becomes practically moot anyhow.
    For larger sizes, eg crypto math, bigints are the way to go.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From dxforth@21:1/5 to albert on Sat Jul 8 12:21:20 2023
    On 7/07/2023 10:35 pm, albert wrote:
    In article <ecafaa3e-d359-46b5-8dc0-e996e44cbd3dn@googlegroups.com>,
    Heinrich Hohl <hheinrich.hohl@gmail.com> wrote:
    On Friday, July 7, 2023 at 4:56:00 AM UTC+2, dxforth wrote:
    I believe the correct stack comment is:

    UD* ( d|ud1 u -- d|ud2)

    No. Try a few examples, e.g.
    3. 5 UD* UD. ---> 15, correct result
    -3. 5 UD* D. ---> -15, correct result
    but
    3. -5 UD* gives wrong result, with UD. as well as with D.
    -3. -5 UD* gives wrong result, with UD. as well as with D.

    So the correct stack comments are
    UD* (ud1 u -- ud2) or (d1 n+ -- d2)

    Note that "n+" is not the same as "u"; n+ is the lower 50% of range
    of an unsigned number.

    I consider a routine 'signed' if any of the inputs in use are
    explicitly negative.
    By your definition M*/ would not be signed.

    This is in the eye of the beholder. In my eyes M*/ is not a fully signed
    routine.

    In M*/ ( d1 n1 +n2 -- d2), the divisor n2 must not be negative.
    The Forth Standard allows this restriction because negative n2
    does not seem to be required in typical applications.

    I can live with that, although I would have preferred the requirement
    ( d1 n1 n2 -- d2) for reasons of consistency. Much easier to memorize.

    My own rule for math operators is:
    A leading "U" signals that all parameter (input and output) are unsigned
    with full range of unsigned numbers.
    It there is no leading "U", this signals that all parameters (input and output)
    are signed with full range of signed numbers.

    This is how I defined my own library of math operators, and the related
    stack effects are very easy to memorize. I generally start with the unsigned >> operator. Then I define the related signed operator by calculating the overall
    sign of the result first, then forming absolute values of the input parameters
    and forwarding these to the unsigned routine. In the last step the overall sign
    is applied to the unsigned result.

    Examples: UD* ( ud1 u -- ud2 ) and D* ( d1 n -- d2) as shown in
    my fist post
    of this thread.

    My take on this is as follows: defaults signed unless a prefix U.
    Prefix U then all operands all unsigned.
    A prefix M means mixed mode
    * single to double (what else)
    / double single (what else)
    result to single single.

    Inserting a D in front op de operator:
    For multiplication this make only sense for inputs, one operand goes
    to double.
    For division this make only sense for outputs,
    as this makes no sense for the remainder as the divisor is single.
    It applies to the quotient,

    So at last I can remember everything.
    The FIGForth M/MOD has to be renamed to UDM/MOD .

    I have no patience for mixing unsigned and signed.

    : * UM* DROP ;

    This is a recipe for disaster. If it were to be applicable,
    I'd define a separate word in the preamble of the program,
    with a ton of comment.

    6.1.0090 *
    star CORE

    ( n1|u1 n2|u2 -- n3|u3 )

    Multiply n1|u1 by n2|u2 giving the product n3|u3.

    What other comment would you add?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Heinrich Hohl@21:1/5 to dxforth on Sat Jul 8 00:47:26 2023
    On Saturday, July 8, 2023 at 4:21:23 AM UTC+2, dxforth wrote:
    : * UM* DROP ;
    ( n1|u1 n2|u2 -- n3|u3 )

    Multiply n1|u1 by n2|u2 giving the product n3|u3.

    What other comment would you add?

    This is an example where sign information gets lost completely due to truncation
    of the result.

    We could formally define two different words:
    : U* ( u1 u2 -- u3) UM* DROP ;
    : * ( n1 n2 -- n3) M* DROP ;

    but it turns out that U* handles not only the unsigned case correctly, but also the
    signed case. All sign information of the input ends up in the top cell of the intermediate
    double length result. And DROP discards this information.

    We can discard one of the two words shown above. U* is simpler by concept (unsigned
    operations do not contain internal branches for sign handling), so we keep the one
    that contains UM*. An because we prefer short names, we use the name "*" for both
    cases.

    A similar example for double length numbers would be:

    : DD* ( d1 d2 -- d3) \ lo1 hi1 lo2 hi2
    3 PICK * >R \ lo1 hi1 lo2 R: (lo1*hi2)
    TUCK * >R \ lo1 lo2 R: (lo1*hi2) (lo2*hi1)
    UM* \ u1 u2 R: (lo1*hi2) (lo2*hi1)
    2R> + + ; \ u1 u2
    \ double multiplication with double length result

    DD* does not need an UDD* counterpart because it handles unsigned double
    length input as well as signed double length input equally well. All sign information
    gets lost with the two upper cells of the quad result that we have discarded.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Heinrich Hohl@21:1/5 to dxforth on Sat Jul 8 00:32:40 2023
    On Friday, July 7, 2023 at 12:03:55 PM UTC+2, dxforth wrote:
    I believe the correct stack comment is:
    UD* ( d|ud1 u -- d|ud2)

    You are right. I checked again and it seems that the second operand "u"
    works properly for unsigned ud1 input as well as for signed d1 input.
    I thought that for signed input we are restricted to n+, but we can indeed
    use the full range of unsigned single length numbers.

    This means ( ud1 u -- ud2 ) or ( d1 u -- d2 ) which can be combined
    as shown in your stack comment.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From none) (albert@21:1/5 to dxforth@gmail.com on Sat Jul 8 10:27:25 2023
    In article <u8ah70$1kadq$1@dont-email.me>, dxforth <dxforth@gmail.com> wrote:

    : * UM* DROP ;

    This is a recipe for disaster. If it were to be applicable,
    I'd define a separate word in the preamble of the program,
    with a ton of comment.

    6.1.0090 *
    star CORE

    ( n1|u1 n2|u2 -- n3|u3 )

    Multiply n1|u1 by n2|u2 giving the product n3|u3.

    What other comment would you add?

    My concern is primarily that I remember correctly what names
    I have to use. This is got to be automatic, unless productivity
    suffers.
    As an implementer I have a different mindset, but I don't
    want thinking back to the time I have to implement these.

    * works for signed and unsigned, big deal. That is not hard
    to remember.

    Groetjes Albert
    --
    Don't praise the day before the evening. One swallow doesn't make spring.
    You must not say "hey" before you have crossed the bridge. Don't sell the
    hide of the bear until you shot it. Better one bird in the hand than ten in
    the air. First gain is a cat spinning. - the Wise from Antrim -

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From dxforth@21:1/5 to albert on Sat Jul 8 20:41:22 2023
    On 8/07/2023 6:27 pm, albert wrote:
    In article <u8ah70$1kadq$1@dont-email.me>, dxforth <dxforth@gmail.com> wrote:

    : * UM* DROP ;

    This is a recipe for disaster. If it were to be applicable,
    I'd define a separate word in the preamble of the program,
    with a ton of comment.

    6.1.0090 *
    star CORE

    ( n1|u1 n2|u2 -- n3|u3 )

    Multiply n1|u1 by n2|u2 giving the product n3|u3.

    What other comment would you add?

    My concern is primarily that I remember correctly what names
    I have to use. This is got to be automatic, unless productivity
    suffers.
    As an implementer I have a different mindset, but I don't
    want thinking back to the time I have to implement these.

    * works for signed and unsigned, big deal. That is not hard
    to remember.

    Yes but you only knew that from the stack comment. It's the stack
    comment your brain remembers. If I don't remember what a word does,
    I'd rather look it up and be certain; than try to decipher an encoded
    name details of which I've likely also forgotten.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Heinrich Hohl@21:1/5 to dxforth on Sat Jul 8 06:58:57 2023
    On Saturday, July 8, 2023 at 12:41:26 PM UTC+2, dxforth wrote:
    ... If I don't remember what a word does,
    I'd rather look it up and be certain; than try to decipher an encoded
    name details of which I've likely also forgotten.

    I agree with that. It makes no sense to encode the full stack effect into
    a word's name. This way we would end up with long and ugly word
    names that no one can remember.

    Word names should not only be unique and consistent.
    They should also be "nice" and easy to remember.
    UD* is much better than something like "UD1U--UD2*", although
    the latter name describes more clearly what the word actually does.

    I prefer simple names, for example:

    UD* ( ud1 u -- ud2) \ unsigned double length multiplication
    D* ( d1 n -- d2) \ signed double length multiplication

    UT* ( ud u -- ut) \ unsigned triple length multiplication
    T* ( d n -- t) \ signed triple length multiplication

    UDQ* ( ud1 ud2 -- uq) \ unsigned double length multiplication with quad result UDD* ( ud1 ud2 -- ud3) \ unsigned double length multiplication with double result
    DD* ( d1 d2 -- d3) \ signed double length multiplication with double result

    In case of multiplication with truncated result, the unsigned operators sometimes
    also work with signed input. In this case we drop the "U" prefix from the unsigned
    operator. UDD* for example handles signed numbers properly, so we can use
    the simpler name DD* for this routine.

    Similar naming rules can be applied to the related division operators.
    And when in doubt, I don't mind looking up stack effects.

    Typed languages like C don't have this kind of problem. There is only
    one multiplication operand named "*". But internally this routine consists
    of several subroutines for all possible cases. At compile time, the compiler decides which of these nameless subroutines should be used.

    I prefer the Forth approach where the programmer decides what to do.
    Even if we are sometimes forced to master the task of finding good names
    for operators.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From dxforth@21:1/5 to Heinrich Hohl on Sun Jul 9 13:51:49 2023
    On 8/07/2023 11:58 pm, Heinrich Hohl wrote:
    On Saturday, July 8, 2023 at 12:41:26 PM UTC+2, dxforth wrote:
    ... If I don't remember what a word does,
    I'd rather look it up and be certain; than try to decipher an encoded
    name details of which I've likely also forgotten.

    I agree with that. It makes no sense to encode the full stack effect into
    a word's name. This way we would end up with long and ugly word
    names that no one can remember.

    Word names should not only be unique and consistent.
    They should also be "nice" and easy to remember.
    UD* is much better than something like "UD1U--UD2*", although
    the latter name describes more clearly what the word actually does.

    I prefer simple names, for example:

    UD* ( ud1 u -- ud2) \ unsigned double length multiplication
    D* ( d1 n -- d2) \ signed double length multiplication

    UT* ( ud u -- ut) \ unsigned triple length multiplication
    T* ( d n -- t) \ signed triple length multiplication

    UDQ* ( ud1 ud2 -- uq) \ unsigned double length multiplication with quad result
    UDD* ( ud1 ud2 -- ud3) \ unsigned double length multiplication with double result
    DD* ( d1 d2 -- d3) \ signed double length multiplication with double result

    In case of multiplication with truncated result, the unsigned operators sometimes
    also work with signed input. In this case we drop the "U" prefix from the unsigned
    operator. UDD* for example handles signed numbers properly, so we can use
    the simpler name DD* for this routine.

    Similar naming rules can be applied to the related division operators.
    And when in doubt, I don't mind looking up stack effects.

    Typed languages like C don't have this kind of problem. There is only
    one multiplication operand named "*". But internally this routine consists
    of several subroutines for all possible cases. At compile time, the compiler decides which of these nameless subroutines should be used.

    I prefer the Forth approach where the programmer decides what to do.
    Even if we are sometimes forced to master the task of finding good names
    for operators.

    We're largely forced into it. OTOH we aren't compelled to name every conceivable multiply and division operator - only what we need. The
    latter makes the task of naming simpler.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Heinrich Hohl@21:1/5 to dxforth on Sun Jul 9 01:18:34 2023
    On Sunday, July 9, 2023 at 5:51:52 AM UTC+2, dxforth wrote:
    We're largely forced into it. OTOH we aren't compelled to name every conceivable multiply and division operator - only what we need. The
    latter makes the task of naming simpler.

    Yes, absolutely true. The collection of useful math operators is limited.
    This helps a lot.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)