## [REBOL] Decimal to Binary Script Re:

### From: joel:neely:fedex at: 1-Aug-2000 10:51

[ptretter--charter--net]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 -10>> dec-to-bin -100000>> 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 // 2With 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!