Announcement

Collapse
No announcement yet.

[CODE] aMath.lib

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • [CODE] aMath.lib

    I thought about sharing some of my work in 4DGL done so far even though don't know how helpful is for others. All code uploading related topics I will open will have a [ CODE ] in the title for easy spotting. I encourage others to use this convention as well.

    i32xxxx functions are work in progress, sadly I don't have the time to get into them.

    Source code:

    HTML Code:
    //===========================================================================
    // Math - functions for various math stuff
    //===========================================================================
    
    //===========================================================================
    // Functions
    //===========================================================================
    
    //divides value by amount and rounds the result
    #IF USING round
    func round(var value, var amount)
    return(value/amount + ((value % amount) < (amount / 2)));
    endfunc;
    #ENDIF
    
    //returns number^power
    #IF USING pow
    func pow(var number, var power)
    var result;
    var i;
    
    if (power == 0)
    result := 1;
    else
    result := number;
    endif
    
    for (i:=1; i ((result+1)^2 - number) we must round up
    if ((number - result*result) > (result*(result+2)+1 - number)) result++;
    return(result);
    endfunc;
    #ENDIF
    
    //gets percentage (from 0 to 100%) of a number (ranging from min to max)
    #IF USING percentage
    func percentage(var value, var min, var max)
    var result;
    result := 100*(value-min)/(max-min);
    if (result <   0) result :=   0;
    if (result > 100) result := 100;
    
    return(result);
    endfunc;
    #ENDIF
    
    
    
    
    var Term1[2];
    var Term2[2];
    //32bit sum function. Result in Term1
    #IF USING i32Sum
    func i32Sum()
    Term1[0] += Term2[0];
    
    Term1[1] += (Term2[1] + OVF());
    endfunc;
    #ENDIF
    
    //32bit substraction function. Result in Term1
    #IF USING i32Sub
    func i32Sub()
    Term1[0] -= Term2[0];
    
    Term1[1] -= (Term2[1] + OVF());
    endfunc;
    #ENDIF
    
    //32bit print function of Term1
    #IF USING i32Print
    func i32Print()
    
    endfunc;
    #ENDIF
    
    
    //===========================================================================
    // EOF
    //===========================================================================

  • #2

    I am also in a desperate need for the 32 bit arithmetic functions, so I have started to develop them.

    I have develop the 32 bit subtraction in the same way you did, but using the OVF is actually wrong and I was consistently getting garbage data.

    Overflow bit is in subtraction used to indicate that the sign of the result is not the sign that is presented by the MSB (in other words, the signed range was exceeded by the result). This is not useful for 32bit subtraction. What you need is the carry bit, which indicates that the unsigned range was exceeded. Carry bit is then subtracted from the HI word. Unfortunately, there seems to be no direct way of getting the value of carry bit, so it has to be determined manually in a rather time consuming manner.

    Since carry is normally calculated by the low-level processor commands, it would be very convenient to make it available in a similar way as the overflow?

    #platform "uLCD-32PT_GFX2"
    //Result := A - B
    func main()
    var A[2],B[2],Result[2],overflow,carry,pointer;
    A[0]:=-27680; //initialize A to 300000
    A[1]:=4;
    B[0]:=3392; //initialize B to 200000
    B[1]:=3;
    Result[0]:=A[0]-B[0]; //subtraction of lower words works correctly: -31072

    overflow:=OVF(); //overflow is set to -1

    carry:=(A[0]>0)&amp(B[0]<0) | (Result[0]<0)) | ((B[0]<0) &amp; (Result[0]<0)); //carry is set to 0

    Result[1]:=A[1]-B[1] + overflow;
    pointer:=str_Ptr(Result);
    print("OVF: 300000-200000 = " ) ;
    str_Printf( &amp;pointer,"%lu" ) ; //result is incorrect: 34464

    Result[1]:=A[1]-B[1] - carry;
    pointer:=str_Ptr(Result);
    print("\ncarry: 300000-200000 = " ) ;
    str_Printf( &amp;pointer,"%lu" ) ; //result is correct: 100000

    repeat
    forever
    endfunc



    Comment


    • #3
      [HTML] aMath.lib

      thanks for the feedback bateng, i will update my lib also till we have a fast carry bit access

      in the mean time I notice that code posted gets transformed as such:

      < becomes <

      > becomes >

      & becomes &

      Comment


      • #4
        [HTML] aMath.lib

        I posted this in another thread... this is what works for me:var Sub32OutHi:=0x0000;var Sub32OutLo:=0x0000;func Sub32(var AHi, var ALo, var BHi, var BLo) Sub32OutLo := ALo-BLo; Sub32OutHi := AHi-(BHi+OVF()+1);endfunc
        I don't know why I need the +1 in there, but it seems to workBasically, the OVF returns a -1 if there is no carry, or 0 if there is a carry... not sure why.

        Comment


        • #5
          Hi android78,

          your observation is absolutely correct, the code you are suggesting SEEMS to work for you. Unfortunately, it does work for some numbers, but not for all...

          It is Sunday afternoon and the weather is lousy, so I have decided to use some of my Boolean algebra to make my day

          If you examine the problem of 32 bit subtraction, there are 65536*65536 combinations of low word operands A[0] and B[0], and for each of them it can be determined if a carry needs to be subtracted from the high word result. So:
          Result[0] := A[0] - B[0];
          carry := something...
          Result[1]:= A[1] - B[1] - carry;

          Fortunately, there are only 8 distinct cases for carry calculation, which depend on the sign of operands A[0] and B[0], and the sign of Result[0]. It is therefore relatively easy to create a truth table and to generate the Boolean expression.

          The results are summarized in the screenshot bellow. The first ten lines are presenting the results for lower word and carry calculation, while the other ten lines present an example and results using different carry calculations.

          The truth table is defined as:
          a := A[0] >= 0;
          b := B[0] >= 0;
          r := Result[0] >= 0;

          Carry can be calculated in several ways:
          o := -OVF();
          O := 1+OVF();
          c :=:=(A[0]>0)&amp(B[0]<0) | (Result[0]<0)) | ((B[0]<0) &amp; (Result[0]<0));


          This example clearly shows that using either -OVF() or 1+OVF() will give correct results for some numbers, but you can easily find cases when the result is wrong. On the other hand, carry calculation always produces the correct result.

          The truth table above looks not only looks nice, but carefully observing the truth table gave me some interesting new ideas. If you try to calculate carry directly from a, b and r, the formula above is probably the fastest approach. However, there is also the OVF(), which can be efficiently retrieved and combined with a,b and r. After some recombinations I have found this surprisingly efficient formula:

          C := (A[0]<0)^(B[0]>=0) ^ (!OVF());

          This formula gives the same carry as the normal carry calculation.

          Regards,
          Valentin


          Code:
           #platform "uLCD-32PT_GFX2"
          //Result := A - B
          func main()
          var A[2],B[2],Result[2],overflow,carry,FastCarry,pointer,n;
          //predefined values for A[0] and B[0]
          var A0[8]:=[-20000,-10000,-10000,-10000, 10000, 10000,10000,20000];
          var B0[8]:=[-10000,-30000, 20000, 30000,-30000,-10000,30000,10000];
          gfx_ScreenMode(4);
          print(" A[0] - B[0] = R[0] | a b r|o O c C");
          //a = A[0]>=0
          //b = B[0]>=0
          //r = Result[0]>=0
          //o = -OVF()
          //O = 1-OVF()
          //c = carry
          //C = fast carry
          for(n:=0;n<8;n++)
          A[0]:=A0[n];
          B[0]:=B0[n];
          Result[0]:=A[0]-B[0];
          overflow:=OVF();
          carry:=(A[0]>=0)&amp;((B[0]<0) | (Result[0]<0)) | ((B[0]<0) &amp; (Result[0]<0));
          FastCarry:=(A[0]<0)^(B[0]>=0) ^ (!overflow);
          if(A[0]>=0) putch('+');
          print([DEC5Z]A[0], " - ");
          if(B[0]>=0) putch('+');
          print([DEC5Z](B[0])," = ");
          if(Result[0]>=0) putch('+');
          print([DEC5Z]Result[0]," | ");
          print((A[0]>=0), " ",(B[0]>=0)," ", Result[0]>=0 ,"|" ,0-overflow," ",overflow+1," ",carry," ",FastCarry);
          next
          A[1]:=5;
          B[1]:=3;
          print("\n A - B = -OVF 1+OVF carry\n");
          for(n:=0;n<8;n++)
          A[0]:=A0[n];
          B[0]:=B0[n];
          //display A and B
          pointer:=str_Ptr(A);
          str_Printf( &amp;pointer,"%lu" );
          putstr(" - ");
          pointer:=str_Ptr(B);
          str_Printf( &amp;pointer,"%lu" );
          putstr(" = ");
          //Calculate lower result
          Result[0]:=A[0]-B[0];
          overflow:=OVF();
          //result with -OVF()
          Result[1]:= A[1]-(B[1]-overflow);
          pointer:=str_Ptr(Result);
          str_Printf( &amp;pointer,"%06lu" );
          putstr(" ");
          //result with 1+OVF()
          Result[1]:= A[1]-(B[1]+1+overflow);
          pointer:=str_Ptr(Result);
          str_Printf( &amp;pointer,"%06lu" );
          putstr(" ");
          //result with carry
          carry:=(A[0]>=0)&amp;((B[0]<0) | (Result[0]<0)) | ((B[0]<0) &amp; (Result[0]<0)); //carry is set to 0
          Result[1]:= A[1]-(B[1]+carry);
          pointer:=str_Ptr(Result);
          str_Printf( &amp;pointer,"%06lu" );
          putstr("\n");
          next
          repeat
          forever
          endfunc

          Comment


          • #6
            [HTML] aMath.lib

            Here are the "optimized" functions for 32bit subtraction and addition. I used them inline, but they can be easily converted to library functions. I am not quite sure when the OVF() gets updated, so I will do some more tests, but so far it looks it works correctly. Note that A=A-B is slightly less efficient than the Result=A-B, but so far I have not found any better solution.


            //Result=A-B inline, duration 29,3 us 37 bytes
            Result[0]:= A[0]-B[0];
            Result[1]:= A[1]-(B[1]+(((A[0]=0)) ^ (!OVF())));

            //A=A-B inline, duration 34,0 us 43 bytes
            tmp1:=A[0];
            A[0]:=A[0]-B[0];
            A[1]:= A[1] - (B[1]+(((tmp1=0)) ^ (!OVF())));

            //Result=A+B inline, duration 29,3 us 37 bytes
            Result[0]:= A[0]+B[0];
            Result[1]:= A[1]+(B[1]+(((A[0]=0)) ^ (!OVF())));

            //A=A-B inline, duration 34,0 us 43 bytes
            tmp1:=A[0];
            A[0]:=A[0]+B[0];
            A[1]:= A[1] + (B[1]+(((tmp1=0)) ^ (!OVF())));


            Just for comparison, I did the benchmark for the direct OVF():
            Result[0]:= A[0]+B[0];
            Result[1]:= A[1]+(B[1]+OVF();
            this takes 24 bytes and 19,3 us.


            Regards,
            Valentin

            Comment


            • #7
              [HTML] aMath.lib

              Thanks Valentin. I'm still not sure the times my function doesn't work.I've tested all the scenarios that I can think of where a carry should/shouldn't happen and it calculates correctly.Any chance you could just give me the two numbers that won't work?
              Thanks again,Andrew

              Comment


              • #8
                [HTML] aMath.lib

                you can find some examples in the screenshot. If you are using the 1+OVF() method, the last line should give the wrong result. So:

                1+OVF(): 347680 - 206608 = 075536 (wrong)
                carry: 347680 - 206608 = 141072 (correct)


                Regards,
                Valentin

                Comment


                • #9
                  [HTML] aMath.lib

                  I just did a test with that and it seemed to work correctly still:
                  (I converted the numbers to hex first as I prefer to work that way)I'm kind of happy that mine seems to work, but don't want to use it if there are situations where it doesn't work. Am I missing something here?
                  My code for subtraction is:var Sub32OutHi:=0x0000;var Sub32OutLo:=0x0000;func Sub32(var AHi, var ALo, var BHi, var BLo) Sub32OutLo := ALo-BLo; Sub32OutHi := AHi-(BHi+OVF()+1);endfunc[/code]
                  Thanks again for your help,Andrew.

                  Comment

                  Working...
                  X