This is excellent stuff and I will be absorbing everything you have done.  I
knew it didnt correctly handle negative integers and I also knew that it
would clobber "holder".  Your suggestions are excellent and I appreciate
your comments.  I originally made this to supplement a Subnet Calculator
that I was working on which my function was sufficient.  I like your
modifications and explaination.  Thanks for all the feedback.

Paul Tretter


-----Original Message-----
From: [EMAIL PROTECTED] [mailto:[EMAIL PROTECTED]]
Sent: Tuesday, August 01, 2000 10:52 AM
To: [EMAIL PROTECTED]
Subject: [REBOL] Decimal to Binary Script Re:


[EMAIL PROTECTED] wrote:
>
>         Ok, I created my first useful function.
>

Congratulations!  Good feeling, isn't it?  I always get psyched when I
get my head around a tool well enough to actually do something with it.

>
>  I created the following function
> because enbase/base value 2 gave me a limitation.  So add this script to
> your %user.r file. Wish this could be added to /Core as it really needs a
> simple function for this. I hope to post a view based Subnet Calculator
> soon. Let me know if it helps.
>
> Paul Tretter
>
> dec-to-bin: func[
>     "Converts Based 10 Integers to Binary"
>     dn [integer!] "Base 10 Integer"][
>     holder: copy ""
>     while [dn / 2 <> 0][
>         either dn // 2 > 0 [insert holder 1][insert holder 0]
>         dn: to-integer dn / 2
>     ]
>     print holder
>
> ]

Can I make a couple of suggestions?

1)  As written, the function doesn't convert "integers", but only
    positive integers.  For example:

        >> dec-to-bin -1
        0
        >> dec-to-bin -10
        0000
        >> dec-to-bin 0

        >>

    Handling these cases will make the function more general.

2)  Since "holder" is not declared local, it shows up in the global
    context.  This is A Bad Idea.  If there was already a variable
    or function named "holder", invoking dec-to-bin would clobber it!

3)  Instead of printing the result, why not simply return it?  If
    the result is returned to an interactive session, it will be
    printed anyway.  However, making the result available for further
    processing allows for more general use.  For example, how many
    bits does it take to hold 4508321?  (see below)

4)  Avoid unnecessary tests whenever possible.  In the line of code

>         either dn // 2 > 0 [insert holder 1][insert holder 0]

    notice that the expression  dn // 2  returns 0 or 1 (for natural
    dn, but see below), therefore this line can be shortened to

>         insert holder dn // 2

With all of these suggestions implemented, the function looks like
this (the name change was simply so that I could have both defined
at once...)

    dec2bin: func[
        "Converts Based 10 Integers to Binary"
        dn [integer!] "Base 10 Integer"
        /local holder "accumulate non-zero results"
    ][
        either dn = 0 [
            "0"
        ][
            either dn < 0 [
                holder: next copy "-"
                dn: - dn
            ][
                holder: copy ""
            ]
            while [dn > 0][
                insert holder dn // 2
                dn: to-integer dn / 2
            ]
            head holder
        ]
    ]

And, to check the suggestions...

    >> dec2bin 0
    == "0"
    >> dec2bin -1
    == "-1"
    >> dec2bin -10
    == "-1010"
    >> dec2bin 4508321
    == "10001001100101010100001"

This last case (from point 3) reminds me to do

    >> length? dec2bin 4508321
    == 23

About the open issue from point 4, don't bother to read the rest of this if
you don't care about the mathematics -- the suggested implementation in
dec2bin avoids signed integer arithmetic and is therefore immune from the
problem discussed below.

-jn-

REBOL (unfortunately, in my opinion) interprets "to-integer" as

    "the integer part as written out in text",

rather than the more mathematically correct

    "largest integer not exceeding"

(and REBOL is not alone in this).  What this means is that we get

    >> -5 // 2
    == -2.5
    >> to-integer -5 / 2
    == -2

Now, based on the definition of remainder, / and // MUST satisfy (for
integral a and b)

    a = b * (to-integer a / b) + (a // b)

which backs us into the weird case that

    >> -5 // 2
    == -1

Why is this weird?  There's lots of useful mathematics (and several
useful programming techniques, as well) based on the idea that the
modulus (remainder after division) is ALWAYS bounded between zero
and (divisor - 1).  For example, think about "clock arithmetic"
using a 24-hour clock (in which the hours run from 0 to 23).  To
get the hour that is thirteen hours after four in the afternoon,
just evaluate

    >> (4 + 12 + 13) // 24
    == 5

that is, 4 (o'clock) + 12 (i.e. PM) + 13 (elapsed time).

BUT...  If I want to find out what hour is seventeen hours before
three in the morning, I get a problem...

    >> (3 - 17) // 24
    == -14

which ISN'T on the clock.  We can sidestep this ugliness by either

1)  making sure that we never try to take a remainder with negative
    arguments (which is the approach taken in dec2bin above), or

2)  write a helper function that cleans up the positive/negative
    mess, as follows

        mod: func [
            "computes true (positive) modulus"
            a [integer!] "dividend"
            b [integer!] "divisor"
        ][
            a // b + b // b
        ]

which allows us to see that

    >> mod -5 2
    == 1
    >> mod -5 3
    == 1

and solve the time problems above

    >> mod (4 + 12 + 13) 24
    == 5
    >> mod (3 - 17) 24
    == 10

Have fun!

Reply via email to