HP Forums

Full Version: Base converter with fractions?
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Pages: 1 2
One of my favorite RPL programs is called BASECALC by Ian Matthew Smith. It not only converts numbers from any base to any other base (not just the usual 4 bases but ANY base 2 through 62), but it even supports fractions (not just whole numbers). So, for example, it converts pi to "3.3DC9FINE" in base 25. And it has a nice user interface.

Has anybody written a base converter for the HP Prime which handles fractions and non-standard bases like BASECALC does?
Hello Joe,

I have not written a program, but it is not so difficult to get pi in hexadecimal form, with digits after the point.

In Home settings choose 64 for number of bits.

Then multiply pi with 16^8, and subsequently divide it by 16^8.

Multiplying gives:13493037704.5
ROUND(13493037704.5,0)= 13493037705
R→B(13493037705)=#3243F6A89h

Dividing this by 16^8 is equivalent to moving the point by 8 positions, which gives:
#3.243F6A89h
Thanks, Jan. That's pretty much how my "hex" program works: http://www.hpmuseum.org/forum/thread-720.html
But my "hex" program only works in base 16 (obviously). So I guess what I need to do is generalize that program by changing the hard-coded "16" to be a variable. The fraction expansion for CAS values can be expressed exactly for any base that is an exact power of 2, but all other bases will need to stop at some arbitrary number of digits. Smith's BASECALC sets the number of fraction digits to IP(12/log10(base)). Prime's CAS might allow a higher value than 12 without losing accuracy. A perfect programming mini-challenge on this rainy day!
When I would like to express 3.142 in base 7, I would do the following.

Multiplication with 7^4 gives: 7543.942
Rounded this is: 7544

Now I am going to express this integer number in base 7:

Division of 7544 by 7^4 (=2401) gives 3+rest 341
Division of 341 by 7^3 (=343) gives 0+rest 341
Division of 341 by 7^2 (49) gives 6+rest 47
Division of 47 by 7 gives 6+ rest 5.

So 7544=3*7^4+0*7^3+6*7^2+6*7+5 and therefore 7544=30665_base7

In the beginning we multiplied with 7^4, so now we have to divide again by 7^4, which shifts the point by 4 positions, and thus gives: 3.142=3.0665_base7
(01-26-2017 12:38 PM)Jan_D Wrote: [ -> ]When I would like to express 3.142 in base 7, I would do the following.

Multiplication with 7^4 gives: 7543.942
Rounded this is: 7544
...
... and thus gives: 3.142=3.0665_base7

Unfortunately that amount of initial roundoff introduces a great amount of error. Smith's BASECALC converts 3.142 (decimal) to "3.06646410512335" (base 7), which it converts cleanly back to what we started with, namely 3.142 (decimal), whereas "3.0665" (base 7) converts to 3.1420241566 (decimal), which is not what we started with.

Multiplying by a greater power of 7 at the beginning helps achieve greater accuracy, but even using 7^13 (which turns 3.142 into an integer in a 12-digit machine) doesn't yield a result as accurate as BASECALC's. I guess it's time to explore Smith's method.

It might be objected that the input only had four significant digits, so the output should be rounded off accordingly. But that's not necessarily true; a user might intend that input to mean "exactly 3.142", as if they had input 3.14200000000 with the trailing zeros being significant digits. HP's philosophy has never been to round outputs to the number of significant digits of the input, because only the user knows the input's number of significant digits. HP's philosophy has always been, "Assume that the user's input is exactly what they want it to be, and let them adjust the accuracy of the result if they want to." IMHO all HP calculator software should follow that philosophy.
Here's the math for this specific example. A general program is left as an exercise for the diligent reader.

3.142 = 3 + 0.142

Convert the whole number part into base 7. Since it is only 3, then we are done. This leaves 0.142 for conversion to \( 0.d_1 d_2 d_3 \dotsm \) where the \( d_i \) are 0 through 6. Note that if we multiply the fractional part by 7 then we will get a value that is non-negative and strictly less than 7.
\[
\begin{align*}
0.142 & = 0.d_1 d_2 d_3 \dotsm \\
7\cdot 0.142 & = d_1 + 0.d_2 d_3 d_4 \dotsm\\
0.994 & = d_1 + 0.d_2 d_3 d_4 \dotsm \\
\end{align*}
\]
Therefore \( d_1 = 0 \). Now repeat...
\[
\begin{align*}
0.994 & = 0. d_2 d_3 d_4 \dotsm \\
7\cdot 0.994 & = d_2 + 0.d_3 d_4 d_5 \dotsm\\
6.958 & = d_2 + 0.d_3 d_4 d_5 \dotsm \\
\end{align*}
\]
Therefore \( d_2 = 6 \). Rinse and repeat (subtract the integer part and repeat for the fractional part) for as many digits as you need. There should be no round-off error when using BCD math (until you get to bases where the "digits" are represented by more than a single numeral in base 10 -- hence the \( 12/\log_{10}(b) \) limitation), but you might possibly be off using the HP Prime's base-2 representation of floating points.

EDIT: For those who just want the answer: The integer portion of a number can be converted to various bases using known methods. The "hard" part is the fractional portion. However, the fraction portion \( d \) is always such that \( 0 \le d < 1 \). So if \( b \) is the base, then \( 0 \le bd < b \). If \( 0.d_1 d_2 d_3 \dotsm \) is the base-\(b\) representation of \(d \), then \( bd\) is \( d_1 + 0.d_2 d_3 d_4 \dotsm \). Hence \( d_1 \) is the integer part of \( bd \).
Purposely avoided using the CAS directly so that I could use a "random" variable, resulting in code that does not read well.

EDIT: fixed some bugs

Code:
EXPORT basecalc()
BEGIN
  local maxdigits:=20; // maximum number of digits for frac part

  local j,k,t,run:=1;
  local b1:=10;
  local b2:=16;
  local d:=0;
  local n:="";
  local ds:="";
  local r:=string(rand,1,12); // create a random CAS variable
  local var:="";
  local ipn:=""; // integer part of n
  local fpn:=""; // frac part of n
  
  local digits:="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

  k:=size(r)-2;
  var:="tmp" + right(r,k);


  while run do
    t:=input(
      { 
        { n, [2], { 15, 80, 0 } },
        { b1, [0], { 25, 25, 1 } },
        { b2, [0], { 70, 25, 1 } },
        { maxdigits, [0], {25,25, 2 } }
      },
      "Base Converter",
      { "n=", "base1=", "base2=", "digits=" },
      {
        "Enter the integer",
        "Enter the base of the integer n",
        "Enter the base to convert to",
        "Max allowable digits"
      }
    );

    if t then
      r:=var + ":={0,0}";
      CAS(EVAL(r));
      k:=size(n);
      if (k AND (b1 >= 2) AND (b2 <= 62)) then

        j:=instring(n,".");

        if j then
          if (j-1>0) then ipn:=left(n,j-1); end;
          if (k-j>0) then fpn:=right(n,k-j); end;
          k:=j-2;
        else
          ipn:=n;
          k:=size(n)-1;
        end;

        // k = largest exponent
        t:=size(ipn);
        for j from 1 to t do
          ds:=mid(ipn,j,1);
          d:=instring(digits,ds)-1;
          if ((d >= b1) OR (d < 0)) then msgbox("Invalid digit: " + ds); j:=-1; break; end;
          r:=var + "[1]:=" + var + "[1]+" + d + "*" + b1 + "^" + k;
          k:=k-1;
          CAS(EVAL(r));
        end;
        if (j == -1) then continue; end;

        t:=size(fpn);
        for j from 1 to t do
          ds:=mid(fpn,j,1);
          d:=instring(digits,ds)-1;
          if ((d >= b1) OR (d < 0)) then msgbox("Invalid digit: " + ds); j:=-1; break; end;
          r:=var + "[2]:=" + var + "[2]+" + d + "*" + b1 + "^" + k;
          k:=k-1;
          CAS(EVAL(r));
        end;
        if (j == -1) then continue; end;

        n:="";

        t:=size(ipn);
        if t then
          r:="ip(ln(" + var + "[1])/ln(" + b2 + ")))";
          k:=CAS(EVAL(r));
          for j from k downto 0 do
            r:="ip(" + var + "[1]/(" + b2 + "^" + j + "))";
            d:=CAS(EVAL(r));
            ds:=mid(digits,d+1,1);
            n:=n+ds;
            r:=var + "[1]:=" + var + "[1] - " + d + "*" + b2 + "^" + j;
            CAS(EVAL(r));
          end;
        end;

        t:=size(fpn);
        if t then
          n:=n+".";
          r:="numer(" + var + "[2])"; t:=CAS(EVAL(r));
          r:="denom(" + var + "[2])"; k:=CAS(EVAL(r));
          for j from 1 to maxdigits do
            r:="ip(" + var + "[2]*" + b2 + ")";
            d:=CAS(EVAL(r));
            r:=var + "[2]:=" + var + "[2]*" + b2 + "-" + d;
            CAS(EVAL(r));

            // floating point issue
            r:=var + "[2] < 0";
            if CAS(EVAL(r)) then
              r:=var + "[2]:=" + var + "[2]+1";
              CAS(EVAL(r));
              d:=d-1;
            end;
            ds:=mid(digits,d+1,1);
            n:=n+ds;
            r:=var + "[2] == 0";
            if CAS(EVAL(r)) then break; end;
          end;
        end;

        t:=b1;
        b1:=b2;
        b2:=t;

      else
        msgbox("Invalid input");
      end;

    else
      run:=0;
    end;

  end;

  r:="purge(" + var + ")";
  CAS(EVAL(r));
  return(0);
END;

I'm sure there is another method using numer() and denom() rather than ip().

Basically, enter your integer as n, along with the presumed base1. The program converts n written in base1 into base2 representation, and sets up the program to re-run it in the opposite direction. Hit CANCEL or press ON to stop.
(01-26-2017 05:18 PM)Joe Horn Wrote: [ -> ]Unfortunately that amount of initial roundoff introduces a great amount of error. Smith's BASECALC converts 3.142 (decimal) to "3.06646410512335" (base 7), which it converts cleanly back to what we started with, namely 3.142 (decimal), whereas "3.0665" (base 7) converts to 3.1420241566 (decimal), which is not what we started with.

It appears that the back-conversion to 3.142 is only "clean" due to rounding (limited number of significant digits).
Thanks, Han! Great program!

(01-27-2017 05:00 AM)Han Wrote: [ -> ]
(01-26-2017 05:18 PM)Joe Horn Wrote: [ -> ]Unfortunately that amount of initial roundoff introduces a great amount of error. Smith's BASECALC converts 3.142 (decimal) to "3.06646410512335" (base 7), which it converts cleanly back to what we started with, namely 3.142 (decimal), whereas "3.0665" (base 7) converts to 3.1420241566 (decimal), which is not what we started with.

It appears that the back-conversion to 3.142 is only "clean" due to rounding (limited number of significant digits).

That's correct. BASECALC uses ordinary floating-point reals for base 10 numbers, which in RPL means that they are rounded to 12 significant digits.
(01-26-2017 06:57 PM)Han Wrote: [ -> ]Here's the math for this specific example. A general program is left as an exercise for the diligent reader.

3.142 = 3 + 0.142

Convert the whole number part into base 7. Since it is only 3, then we are done. This leaves 0.142 for conversion to \( 0.d_1 d_2 d_3 \dotsm \) where the \( d_i \) are 0 through 6. Note that if we multiply the fractional part by 7 then we will get a value that is non-negative and strictly less than 7.
\[
\begin{align*}
0.142 & = 0.d_1 d_2 d_3 \dotsm \\
7\cdot 0.142 & = d_1 + 0.d_2 d_3 d_4 \dotsm\\
0.994 & = d_1 + 0.d_2 d_3 d_4 \dotsm \\
\end{align*}
\]
Therefore \( d_1 = 0 \). Now repeat...
\[
\begin{align*}
0.994 & = 0. d_2 d_3 d_4 \dotsm \\
7\cdot 0.994 & = d_2 + 0.d_3 d_4 d_5 \dotsm\\
6.958 & = d_2 + 0.d_3 d_4 d_5 \dotsm \\
\end{align*}
\]
Therefore \( d_2 = 6 \). Rinse and repeat (subtract the integer part and repeat for the fractional part) for as many digits as you need.

Nice and efficient math, Han!
I think your desired programme does the same (?) as this 50g programme

http://www.hpmuseum.org/forum/thread-7675.html

if you have the divisor set to 1.
(01-26-2017 05:18 PM)Joe Horn Wrote: [ -> ]Unfortunately that amount of initial roundoff introduces a great amount of error.

Sure. The example rounds to four decimal places, so the result of course is not correct in all 12 digits, let alone the exact answer.

(01-26-2017 05:18 PM)Joe Horn Wrote: [ -> ]Smith's BASECALC converts 3.142 (decimal) to "3.06646410512335" (base 7), which it converts cleanly back to what we started with, namely 3.142 (decimal),

FTR: this still is not the exact result, it's just the first 15 digits. The exact base-7 representation of 3,142 is periodic, it's \(3,\overline{06646410512335362252}\).

Since we're in a museum here: one of my first books on RPN and calculators included an HP67/97 program for base conversions, both for integer and fractional numbers. The method has been posted here: simply multiply the fractional part by the base, then the integer part of this is the next digit. I just noticed that this book is available on Google books (Helmut Alt, Anwendung programmierbarer Taschenrechner Band 1). If you read German: it's page 140ff.

Dieter
(01-27-2017 04:37 AM)Han Wrote: [ -> ]I'm sure there is another method using numer() and denom() rather than ip().

Yes, that is true.
Instead of IP(x) we can use iquo(numer(x),denom(x))
(easy to find in Toolbox menu: CAS -Integer - Division - Quotient)

When x=999999999999999/1000000000000000
the first method returns 1 and the second method gives the correct result 0.

You found a smart way to correct it though, in a smart program!
(01-27-2017 04:37 AM)Han Wrote: [ -> ]Purposely avoided using the CAS directly so that I could use a "random" variable, resulting in code that does not read well.

I slightly modified your program, not because it is not good enough but to make it better readable.

Instead of a CAS variable like tmp586605323013 it uses the CAS variable tmp.

Code:

EXPORT basecalc()
BEGIN
  local maxdigits:=20; // maximum number of digits for frac part

  local j,k,t,run:=1;
  local b1:=10;
  local b2:=16;
  local d:=0;
  local n:="";
  local ds:="";
  local r;
  local var:="";
  local ipn:=""; // integer part of n
  local fpn:=""; // frac part of n
  
  local digits:="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

  k:=size(r)-2;
  
  while run do
    t:=input(
      { 
        { n, [2], { 15, 80, 0 } },
        { b1, [0], { 25, 25, 1 } },
        { b2, [0], { 70, 25, 1 } },
        { maxdigits, [0], {25,25, 2 } }
      },
      "Base Converter",
      { "n=", "base1=", "base2=", "digits=" },
      {
        "Enter the integer",
        "Enter the base of the integer n",
        "Enter the base to convert to",
        "Max allowable digits"
      }
    );

    if t then                 //when pressed the OK menu key.
      CAS("tmp:={0,0}");      //creation of global CAS variable with name tmp
      k:=size(n);
      if (k AND (b1 >= 2) AND (b2 <= 62)) then

        j:=instring(n,".");

        if j then
          if (j-1>0) then ipn:=left(n,j-1); end;
          if (k-j>0) then fpn:=right(n,k-j); end;
          k:=j-2;
        else
          ipn:=n;
          k:=size(n)-1;
        end;

                     // k = largest exponent
        
                     //conversion of ipn to decimal form and storing it into tmp[1]:
        t:=size(ipn);
        for j from 1 to t do
          ds:=mid(ipn,j,1);
          d:=instring(digits,ds)-1;
          if ((d >= b1) OR (d < 0)) then msgbox("Invalid digit: " + ds); j:=-1; break; end;
          CAS("tmp[1]:=tmp[1]+d*b1^k");
          k:=k-1;
        end;
        if (j == -1) then continue; end;
             
                      //k=-1 now!

                     //conversion of fpn to decimal form and storing it into tmp[2]:
        t:=size(fpn);
        for j from 1 to t do
          ds:=mid(fpn,j,1);
          d:=instring(digits,ds)-1;
          if ((d >= b1) OR (d < 0)) then msgbox("Invalid digit: " + ds); j:=-1; break; end;
          CAS("tmp[2]:=tmp[2]+d*b1^k");
          k:=k-1;
        end;
        if (j == -1) then continue; end;

                 //tmp[2] contains a decimal number in the form of a fraction now.

        n:="";
        
                 //conversion of tmp[1] to base b2 and storing it into n
        t:=size(ipn);
        if t then
          k:=CAS("ip(ln(tmp[1])/ln(b2))");    //calculation of maximal power of b2
                                              //which is less or equal to tmp[1]
          for j from k downto 0 do
            d:=CAS("ip(tmp[1]/(b2^j))");
            ds:=mid(digits,d+1,1);
            n:=n+ds;
            CAS("tmp[1]:=tmp[1]-d*b2^j");
          end;
        end;

                    //conversion of tmp[2] to base b2 and storing it into n
        t:=size(fpn);
        if t then
          n:=n+".";
          for j from 1 to maxdigits do
            d:=CAS("ip(tmp[2]*b2)");
            CAS("tmp[2]:=tmp[2]*b2-d");

            // floating point issue
            if CAS("tmp[2]<0") then
              CAS("tmp[2]:=tmp[2]+1");
              d:=d-1;
            end;

            ds:=mid(digits,d+1,1);
            n:=n+ds;
            if CAS("tmp[2]==0") then break; end;
          end;
        end;

        t:=b1;
        b1:=b2;
        b2:=t;

      else
        msgbox("Invalid input");
      end;

    else
      run:=0;              //when pressed Esc leave the loop
    end;

  end;
CAS("purge(tmp)");
END;
Just a bit of info on why I used a "random" CAS variable. I personally believe that any program should do whatever it can to preserve the state of the machine it runs on. This means leaving variables in tact where possible, restoring user settings to their original state prior to running the program, etc. Because 'tmp' could very well be a user variable (in fact, I use it a lot while doing CAS-related things), this program would over-write the user's content. On the other hand, it would be very unlikely that there is a user variable named something like tmp164503545498 (certainly too long to be user friendly, and the random digits makes a name collision highly unlikely). That was the driving factor behind using the random variable -- I can still use a CAS variable (which is necessary) without worrying too much about destroying the user's CAS variables.
When we consider the program as a black box your code is certainly more professional than my simplified one.
But when we are interested in understanding the program, and possibly modify it, I prefer the other.
It should not be a problem that we reserve one CAS variable, say tmp or ex1, ex2, for use of programs, there are enough variables left.

Built - in apps like the Function app or Advanced Graphing also change the content of X and Y, which could be considered more serious.
The advantage of this is however that by doing this they can very effectively collaborate with e.g. the Solve app.
Like Han pointed out in one of his good and important articles:

HP Prime CAS programming

it is also possible to write his program with the help of a CAS – helpprogram.

This could be the code:

Code:


basecalc3();
cashelpprogram();


EXPORT basecalc3()
BEGIN
  local n:="45.76";  
  local b1:=8;
  local b2:=4;
  local maxdigits:=20; // maximum number of digits for frac part
  local run:=1;
  local t;
  local s;
    
  
  while run do
    t:=input(
      { 
        { n, [2], { 15, 80, 0 } },
        { b1, [0], { 25, 25, 1 } },
        { b2, [0], { 70, 25, 1 } },
        { maxdigits, [0], {25,25, 2 } }
      },
      "Base Converter",
      { "n=", "base1=", "base2=", "digits=" },
      {
        "Enter the integer",
        "Enter the base of the integer n",
        "Enter the base to convert to",
        "Max allowable digits"
      }
      );


    if t then            //when pressed the OK menu key.
       if SIZE(n) AND 2<=b1<=62 AND 2<=b2<=62 then
          
         s:=cashelpprogram(n,b1,b2,maxdigits);
          
         if s(1)=1 then msgbox("Invalid digid "+s(2)); continue; end;
        
         n:=s(2);
         t:=b1;
         b1:=b2;
         b2:=t;

       else
         msgbox("Invalid input";   
       end;
        
    else
      run:=0;            //when pressed Esc leave the loop
    end;

  end;                   //end loop
END;






#cas
cashelpprogram(n,b1,b2,maxdigits)
BEGIN
  
  local j,k,t;  
  local d;  
  local ds;   
  local ipn;
  local fpn;  
  local digits;
  local tmp;       
  
  ipn:=""; // integer part of n
  fpn:=""; // frac part of n
  digits:="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
  tmp:={0,0};
      
        k:=size(n);
        j:=instring(n,".");

        if j then
          if (j-1>0) then ipn:=left(n,j-1); end;
          if (k-j>0) then fpn:=right(n,k-j); end;
          k:=j-2;
        else
          ipn:=n;
          k:=size(n)-1;
        end;

                     // k = largest exponent
        
                     //conversion of ipn to decimal form and storing it into tmp[1]:
        t:=size(ipn);
        for j from 1 to t do
          ds:=mid(ipn,j,1);
          d:=instring(digits,ds)-1;
          if ((d >= b1) OR (d < 0)) then RETURN {1,ds}; end; 
          tmp[1]:=tmp[1]+d*b1^k;
          k:=k-1;
        end;
        
             
                      //k=-1 now!

                     //conversion of fpn to decimal form and storing it into tmp[2]:
        t:=size(fpn);
        for j from 1 to t do
          ds:=mid(fpn,j,1);
          d:=instring(digits,ds)-1;
          if ((d >= b1) OR (d < 0)) then RETURN {1,ds}; end; 
          tmp[2]:=tmp[2]+d*b1^k;
          k:=k-1;
        end;
        

                 //tmp[2] contains a decimal number in the form of a fraction now.

        n:="";
        
                 //conversion of tmp[1] to base b2 and storing it into n
        t:=size(ipn);
        if t then
          k:=ip(ln(tmp[1])/ln(b2));    //calculation of maximal power of b2
                                              //which is less or equal to tmp[1]
          for j from k downto 0 do
            d:=ip(tmp[1]/(b2^j));
            ds:=mid(digits,d+1,1);
            n:=n+ds;
            tmp[1]:=tmp[1]-d*b2^j;
          end;
        end;

                    //conversion of tmp[2] to base b2 and storing it into n
        t:=size(fpn);
        if t then
          n:=n+".";
          for j from 1 to maxdigits do
            d:=ip(tmp[2]*b2);
            tmp[2]:=tmp[2]*b2-d;

            // floating point issue
            if tmp[2]<0 then
              tmp[2]:=tmp[2]+1;
              d:=d-1;
            end;

            ds:=mid(digits,d+1,1);
            n:=n+ds;
            if tmp[2]==0 then break; end;
          end;
        end;
       
        RETURN {0,n};

END;
#end

Disadvantage of this method, though the code is easier, is that cashelpprogram() is not a local program.

Suppose that we had already written another program which also uses a Cas program with the name cashelpprogram(), then this would not affect the working of our program, but when we would execute the earlier program again it would not work properly anymore because it would use the most recent version of cashelpprogram(), and in order to let it work properly again we would have to recompile it.

This can be done easily though, by opening it in the Program Catalog and leaving it again.

So when we forget to do this it can cause unexpected problems.
In such a case I recommend using a unique identifier such as casbasecalc() to avoid reusing the same name.
(02-03-2017 02:31 PM)Han Wrote: [ -> ]Just a bit of info on why I used a "random" CAS variable. I personally believe that any program should do whatever it can to preserve the state of the machine it runs on. This means leaving variables in tact where possible, restoring user settings to their original state prior to running the program, etc. Because 'tmp' could very well be a user variable (in fact, I use it a lot while doing CAS-related things), this program would over-write the user's content. On the other hand, it would be very unlikely that there is a user variable named something like tmp164503545498 (certainly too long to be user friendly, and the random digits makes a name collision highly unlikely). That was the driving factor behind using the random variable -- I can still use a CAS variable (which is necessary) without worrying too much about destroying the user's CAS variables.

To me, tmp or temp mean a temporary variable. I never care about the value of a temporary variable after the program using it ends. If I did want to preserve the value, I'd put it in a less temporary-sounding variable! (Don't ask about temperature! Big Grin ) I guess that's why I make almost all the variables in a program LOCAL so they disappear after the program ends. I do save the angle mode and other state-related variables if my program alters any of them.

Tom L
(02-03-2017 01:51 PM)Jan_D Wrote: [ -> ]I slightly modified your program, not because it is not good enough but to make it better readable.

Instead of a CAS variable like tmp586605323013 it uses the CAS variable tmp.

It is also possible to make the program completely local, without using a global CAS variable tmp, and still do the calculations in CAS.

We can declare a local variable tmp, and the program will work with this local variable and use it for CAS calculations.

The only thing we have to pay attention to is that it is not allowed to use square brackets, but we have to use parentheses.

So instead of CAS("tmp[1]:=tmp[1]+d*b1^k") we have to write CAS("tmp(1):=tmp(1)+d*b1^k").

Instead of CAS("tmp(1):=tmp(1)+d*b1^k") we could also write: tmp(1):=CAS(“tmp(1)+d*b1^k").


Code:



EXPORT basecalc4()
BEGIN
  local maxdigits:=20; // maximum number of digits for frac part

  local j,k,t,run:=1;
  local b1:=10;
  local b2:=16;
  local d:=0;
  local n:="";
  local ds:="";
  
  local ipn:=""; // integer part of n
  local fpn:=""; // frac part of n
  
  local digits:="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

  local tmp;
  
  while run do
    t:=input(
      { 
        { n, [2], { 15, 80, 0 } },
        { b1, [0], { 25, 25, 1 } },
        { b2, [0], { 70, 25, 1 } },
        { maxdigits, [0], {25,25, 2 } }
      },
      "Base Converter",
      { "n=", "base1=", "base2=", "digits=" },
      {
        "Enter the integer",
        "Enter the base of the integer n",
        "Enter the base to convert to",
        "Max allowable digits"
      }
    );

    if t then                 //when pressed the OK menu key.
      CAS("tmp:={0,0}");      //creation of global CAS variable with name tmp
      k:=size(n);
      if (k AND (b1 >= 2) AND (b2 <= 62)) then

        j:=instring(n,".");

        if j then
          if (j-1>0) then ipn:=left(n,j-1); end;
          if (k-j>0) then fpn:=right(n,k-j); end;
          k:=j-2;
        else
          ipn:=n;
          k:=size(n)-1;
        end;

                     // k = largest exponent
        
                     //conversion of ipn to decimal form and storing it into tmp[1]:
        t:=size(ipn);
        for j from 1 to t do
          ds:=mid(ipn,j,1);
          d:=instring(digits,ds)-1;
          if ((d >= b1) OR (d < 0)) then msgbox("Invalid digit: " + ds); j:=-1; break; end;
          CAS("tmp(1):=tmp(1)+d*b1^k");
          k:=k-1;
        end;
        if (j == -1) then continue; end;
             
                      //k=-1 now!

                     //conversion of fpn to decimal form and storing it into tmp[2]:
        t:=size(fpn);
        for j from 1 to t do
          ds:=mid(fpn,j,1);
          d:=instring(digits,ds)-1;
          if ((d >= b1) OR (d < 0)) then msgbox("Invalid digit: " + ds); j:=-1; break; end;
          CAS("tmp(2):=tmp(2)+d*b1^k");
          k:=k-1;
        end;
        if (j == -1) then continue; end;

                 //tmp[2] contains a decimal number in the form of a fraction now.


        n:="";
        
                 //conversion of tmp[1] to base b2 and storing it into n
        t:=size(ipn);
        if t then
          k:=CAS("ip(ln(tmp(1))/ln(b2))");    //calculation of maximal power of b2
                                              //which is less or equal to tmp[1]
          for j from k downto 0 do
            d:=CAS("ip(tmp(1)/(b2^j))");
            ds:=mid(digits,d+1,1);
            n:=n+ds;
            CAS("tmp(1):=tmp(1)-d*b2^j");
          end;
        end;

                    //conversion of tmp[2] to base b2 and storing it into n
        t:=size(fpn);
        if t then
          n:=n+".";
          for j from 1 to maxdigits do
            d:=CAS("ip(tmp(2)*b2)");
            CAS("tmp(2):=tmp(2)*b2-d");

            // floating point issue
            if CAS("tmp(2)<0") then
              CAS("tmp(2):=tmp(2)+1");
              d:=d-1;
            end;

            ds:=mid(digits,d+1,1);
            n:=n+ds;
            if CAS("tmp(2)==0") then break; end;
          end;
        end;

        t:=b1;
        b1:=b2;
        b2:=t;

      else
        msgbox("Invalid input");
      end;

    else
      run:=0;              //when pressed Esc leave the loop
    end;

  end;
END;
Pages: 1 2
Reference URL's