Announcement

Collapse

Forum Guidelines

This forum is for finished source code that is working properly. If you have questions about this or any other source code, please post it in one of the Discussion Forums, not here.
See more
See less

Evaluate An Equation

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

  • PBWin Evaluate An Equation

    This is an example of how to use Jean-Pierre Leroy's expression expression evaluation code with expressions which contain variables (his examples were all numeric).

    Putting this code in a loop is a great way to generate data for plotting equations. This approach is what I used in the equation plotting source code that I posted earlier this week.

    With this code, you define a string that contains an equation such as this:

    Equation = "sin(sqr(x^2+x^2))"

    To find z, you substitute the variable values with their string equivalent and feed that to Jean-Pierre's code.

    Note: you must put parentheses () around the substitution.

    Without the parentheses, an expression of y^2, where y=-2 will become -2^2=-4. What you want is (-2)^2=4.

    Alternately, you could also include the extra () in the original equation expression.

    Compilable Example:
    Code:
    #Compile Exe
    #Dim All
    #Include "Win32API.inc"
    Global hDlg as DWord
    Function PBMain() As Long
       Dialog New Pixels, 0, "Test Code",300,300,200,200, %WS_OverlappedWindow To hDlg
       Control Add Button, hDlg, 100,"Push", 50,10,100,20
       Dialog Show Modal hDlg Call DlgProc
    End Function
    
    CallBack Function DlgProc() As Long
       If CB.Msg = %WM_Command AND CB.Ctl = 100 AND CB.Ctlmsg = %BN_Clicked Then
          Local x,y As Single, Expr As String
          x = 5  :  y = 5
          Equation = "x*x + y*y"
          Replace "x" With "(" + Str$(x) + ")" In Equation
          Replace "y" With "(" + Str$(y) + ")" In Equation
          MsgBox Str$(Evaluate(Equation))
       End If
    End Function
    
    Function Evaluate(ByVal pExpression As String) As Single
        Local lExpression As String
        lExpression = PrepExpNum(pExpression)      ' PREPare the NUMerical EXPexpression before the evaluation
        Function = EvalExpNum(lExpression)
    End Function
    
    Function PrepExpNum(ByRef pExpression As String) As String
        Local lNewExpression As String
        lNewExpression = UCase$(Trim$(pExpression))
        Replace "LOG10" With "LOGTEN"  In lNewExpression
        Replace "LOG2"  With "LOGTWO"  In lNewExpression
        Replace "EXP10" With "EXPTEN"  In lNewExpression
        Replace "EXP2"  With "EXPTWO"  In lNewExpression
        Replace "PI"    With "3.14159" In lNewExpression
        Replace "-"     With "_"       In lNewExpression  ' to avoid any confusion with a negative value
        Function = lNewExpression
    End Function
    
    Function EvalExpNum(ByVal pExpression As String) As Single      'Extended
        Local    lOpenParentheseStart, lOpenParentheseCounter, lCloseParentheseCounter As Long
        Local    lCurrentPos, lI, lPosOperator, lLeftOperandStart As Long
        Local    lleftOperandEnd, lRightOperandStart, lRightOperandEnd As Long
        Local    lEval, lLeftOperand, lRightOperand As Single         'Extended
    
        Do
            lOpenParentheseCounter  = 0 : lCloseParentheseCounter = 0   'reset the parentheses counters
            lOpenParentheseStart = Instr(pExpression, "(")              'search for a open parenthese "(" in the expression
            If lOpenParentheseStart <> 0 Then                           'only if we find a open parenthese "(" in the expression
                lOpenParentheseCounter = 1                              'initialize the open parenthese counter to 1
                lCurrentPos = lOpenParentheseStart                      'we start at the open parenthese
                Do
                    Incr lCurrentPos                                    'increment the current position
                    If Mid$(pExpression, lCurrentPos, 1) = ")" Then Incr lCloseParentheseCounter    ' to count the number of parentheses
                    If Mid$(pExpression, lCurrentPos, 1) = "(" Then Incr lOpenParentheseCounter
                Loop Until lOpenParentheseCounter = lCloseParentheseCounter
    
                pExpression = Left$(pExpression,lOpenParentheseStart-1)+ _    'recursive call to EvalExpNum
                              Format$(EvalExpNum(Mid$(pExpression,lOpenParentheseStart+1,lCurrentPos-lOpenParentheseStart-1)))+ _
                              Right$(pExpression,-lCurrentPos)
            Else
                Data "ATN", "COS", "SIN", "TAN", "LOGTEN", "LOGTWO", "LOG", "EXPTEN"
                Data "EXPTWO", "EXP", "SQR", "ABS", "^", "*", "/", "\", "MOD", "_", "+"
                For lI = 1 To Datacount                ' check all the Operators/Functions that could be used in the numerical expression
                     Do                                ' examines all the occurences of the operator/function
                         lPosOperator = Instr(pExpression,Read$(lI))
                         If lPosOperator <> 0 Then               ' only if we find the Operator/Function
                             lLeftOperandEnd = lPosOperator-1    ' search the start/end of the left operand
                             lCurrentPos     = lPosOperator
                             Do
                                Decr lCurrentPos                     ' decrement current pos
                                If   lCurrentPos < 1 Then Exit Loop  ' if we are out of the string
                            Loop Until Instr(" 0123456789.-",Mid$(pExpression, lCurrentPos, 1)) = 0
                            lLeftOperandStart = lCurrentPos+1
    
                            lRightOperandStart = lPosOperator+Len(Read$(lI))    ' search the start/end of the right operand
                            lCurrentPos          = lPosOperator+Len(Read$(lI))-1
                            Do
                                Incr lCurrentPos                                     ' decrement current pos
                                If   lCurrentPos > Len(pExpression) Then Exit Loop   ' if we are out of the string
                            Loop Until Instr(" 0123456789.-",Mid$(pExpression, lCurrentPos, 1)) = 0
                            lRightOperandEnd = lCurrentPos - 1
    
                            lLeftOperand  = Val(Trim$(Mid$(pExpression,lLeftOperandStart ,lLeftOperandEnd -lLeftOperandStart +1))) ' extract the operands removing spaces
                            lRightOperand = Val(Trim$(Mid$(pExpression,lRightOperandStart,lRightOperandEnd-lRightOperandStart+1))) ' extract the operands removing spaces
    
                            Select Case Read$(lI)                     ' depending of the Operator/Function
                                Case "ATN"     :  lEval = Atn(lRightOperand)
                                Case "COS"     :  lEval = Cos(lRightOperand)
                                Case "SIN"     :  lEval = Sin(lRightOperand)
                                Case "TAN"     :  lEval = Tan(lRightOperand)
                                Case "LOGTEN"  :  lEval = Log10(lRightOperand)
                                Case "LOGTWO"  :  lEval = Log2(lRightOperand)
                                Case "LOG"     :  lEval = Log(lRightOperand)
                                Case "EXPTEN"  :  lEval = Exp10(lRightOperand)
                                Case "EXPTWO"  :  lEval = Exp2(lRightOperand)
                                Case "EXP"     :  lEval = Exp(lRightOperand)
                                Case "SQR"     :  lEval = Sqr(lRightOperand)
                                Case "ABS"     :  lEval = ABS(lRightOperand)
                                Case "^"       :  lEval = lLeftOperand ^ lRightOperand
                                Case "*"       :  lEval = lLeftOperand * lRightOperand
                                Case "/"       :  lEval = lLeftOperand / lRightOperand
                                Case "\"       :  lEval = lLeftOperand \ lRightOperand
                                Case "MOD"     :  lEval = lLeftOperand Mod lRightOperand
                                Case "_"       :  lEval = lLeftOperand - lRightOperand
                                Case "+"       :  lEval = lLeftOperand + lRightOperand
                            End Select
                            lEval = Round(lEval, 2)  'round the evaluation to 6 decimal places to avoid scientific notation (E-)
                            pExpression = Left$(pExpression,lLeftOperandStart-1)+Format$(lEval)+Right$(pExpression,-lRightOperandEnd) 'place the result of the evaluation in the string
                        End If
                     Loop Until lPosOperator = 0
                 Next lI
                Function = Val(pExpression)  'to evaluate the final expression
            End If
        Loop Until lOpenParentheseStart = 0
    
    End Function
    
    'gbs_00570
    Last edited by Gary Beene; 31 Mar 2010, 09:43 AM.

  • #2
    Hey Gary,

    I used this code to create a generic math parser routine. I would like to add Boolean operators like this:

    CASE ">" : lEval = -(lLeftOperand > lRightOperand)
    CASE ">=" : lEval = -(lLeftOperand >= lRightOperand)

    CASE "<" : lEval = -(lLeftOperand < lRightOperand)
    CASE "<=" : lEval = -(lLeftOperand <= lRightOperand)

    CASE "==" : lEval = -(lLeftOperand = lRightOperand)

    Except I cannot get the >= and <= cases working. I don't follow the code well enough to debug this. Maybe you can offer a simple solution?

    Thanks. Dean

    Comment


    • #3
      Hi Dean!

      Did you make this change?

      Code:
                  Data "EXPTWO", "EXP", "SQR", "ABS", "^", "*", "/", "\", "MOD", "_", "+", "<=", ">="
      With that plus your lines of code in #2, it seems to work for me.

      Comment


      • #4
        This went empty with no edit button. Back to home page after next post and this was populated again with old version that needed fixing.
        Dale

        Comment


        • #5
          Get rid of minus sign to get -1 for TRUE.
          Code:
          #compile exe
          #dim all
          
          function pbmain () as long
            local OpStr as string
            local lLeftOperand, lRightOperand, lEval as long
            OpStr = "<="
          
            lLeftOperand = 5
            lRightOperand = 4
            gosub ParseOperator
            lLeftOperand = 4
            lRightOperand = 4
            gosub ParseOperator
            lLeftOperand = 4
            lRightOperand = 5
            gosub ParseOperator '
            waitkey$
            exit function
            ParseOperator:
              select case OpStr
                case ">" : lEval = -(lLeftOperand > lRightOperand)
                case ">=" : lEval = -(lLeftOperand >= lRightOperand)
                case "<" : lEval = -(lLeftOperand < lRightOperand)
          
                case "<=" : lEval = (lLeftOperand <= lRightOperand) '<<==fixed this one
                  ? str$(lLeftOperand) + " " + str$(lRightOperand) + " " + str$(lEval)
          
                case "==" : lEval = -(lLeftOperand = lRightOperand)
              end select
            return
          end function
          Cheers,
          Dale

          Comment


          • #6
            Dale

            I want the logical operators to evaluate 0 to 1. Hence the -1.

            Gary, here is the modified code. the following msgbox shows the fail case, i.e. x >=6, where x = 5, the result is +1

            Click image for larger version

Name:	Mathevaluator.png
Views:	90
Size:	6.7 KB
ID:	778170

            thanks

            Regards, Dean

            Code:
               '  Derives from http://www.powerbasic.com/support/pbforums/showthread.php?t=43222&highlight=evaluate+equation
            
               %L_Compile_Math_EVAL = -1
            
               #IF %L_Compile_Math_EVAL
            
                  $DIM ALL
                  $COMPILE EXE
            
                  GLOBAL Number_PI     AS DOUBLE
                  GLOBAL Number_INF_p  AS DOUBLE
                  GLOBAL Number_E      AS DOUBLE
                  $S_HeaderCode = "Gary Beene Math Evaluator"
            
               #ENDIF
            
               GLOBAL N_Math_FCN    AS LONG
               GLOBAL S_Math_FCN()  AS STRING
            
               GLOBAL N_Math_VAR    AS LONG
               GLOBAL S_Math_VAR()  AS STRING
               GLOBAL Q_Math_VAR()  AS DOUBLE
            
            '   #INCLUDE ONCE "FCN ATAN.bas"
            '   #INCLUDE ONCE "FCN ASIN.bas"
            '   #INCLUDE ONCE "FCN ACOS.bas"
            '   #INCLUDE ONCE "FCN ATANH.bas"
            '   #INCLUDE ONCE "FCN ASINH.bas"
            '   #INCLUDE ONCE "FCN ACOSH.bas"
            '   #INCLUDE ONCE "FCN TANH.bas"
            '   #INCLUDE ONCE "FCN SINH.bas"
            '   #INCLUDE ONCE "FCN COSH.bas"
            
               #IF %L_Compile_Math_EVAL
            
                  FUNCTION PBMAIN()
            
                     DIM S_Command     AS STRING
                     DIM S_Command_SUB AS STRING
                     DIM Q_Math_EVAL   AS DOUBLE
            
                     DIM x             AS DOUBLE : x = 5.0#
                     DIM y             AS DOUBLE : y = 0
                     DIM z             AS DOUBLE : z = 0
            
                     DIM S_x           AS STRING : S_x = STR$(x, 16)
                     DIM S_y           AS STRING : S_y = STR$(y, 16)
                     DIM S_z           AS STRING : S_z = STR$(z, 16)
            
                     Number_PI = 4.0 * ATN(1.0)
                     Number_E  = EXP(1.0)
            
                    'S_Command = "sin(x) * cos(2*x)"
                     S_Command = "x >= 6"
            
                     DO
            
                        S_Command = INPUTBOX$("Input expression in xyz", "Math EVAL Parser", S_Command)
            
                        S_Command_SUB = S_Command
            
                        REPLACE "x" WITH "(" + STR$(VAL(S_x)) + ")" IN S_Command_SUB
                        REPLACE "y" WITH "(" + STR$(VAL(S_y)) + ")" IN S_Command_SUB
                        REPLACE "z" WITH "(" + STR$(VAL(S_z)) + ")" IN S_Command_SUB
            
                       'REPLACE "x" WITH "(" + STR$(x) + ")" IN S_Command_SUB
                       'REPLACE "y" WITH "(" + STR$(y) + ")" IN S_Command_SUB
                       'REPLACE "z" WITH "(" + STR$(z) + ")" IN S_Command_SUB
            
                        MATH_Evaluate(S_Command_SUB) TO Q_Math_EVAL
            
                        MSGBOX S_Command_SUB + " = " + STR$(Q_Math_EVAL) , %MB_ICONINFORMATION OR %MB_TASKMODAL, $S_HeaderCode
            
                     LOOP UNTIL S_Command = ""
            
                  END FUNCTION
               #ENDIF
            
            
            FUNCTION MATH_Evaluate(BYVAL pExpression AS STRING) AS DOUBLE
            
               DIM lExpression                                       AS STRING
            
               ' Prepare the numerical expression before the evaluation
               lExpression = PrepExpNum(pExpression)
            
               FUNCTION = EvalExpNum(lExpression)
            
            END FUNCTION
            
            
            FUNCTION PrepExpNum(BYREF pExpression AS STRING) AS STRING
            
               DIM lNewExpression                                    AS STRING
            
            
               lNewExpression = UCASE$(TRIM$(pExpression))
            
               REPLACE "LOG10"   WITH "LOGTEN"           IN lNewExpression
               REPLACE "LOG2"    WITH "LOGTWO"           IN lNewExpression
               REPLACE "EXP10"   WITH "EXPTEN"           IN lNewExpression
               REPLACE "EXP2"    WITH "EXPTWO"           IN lNewExpression
            
               REPLACE "PI"      WITH STR$(Number_PI)    IN lNewExpression
               REPLACE "INF"     WITH STR$(Number_INF_P) IN lNewExpression
            
               REPLACE "-"       WITH "_"                IN lNewExpression    '  to avoid any confusion with a negative value
            
               REPLACE "ACOS"    WITH "ACN"              IN lNewExpression
               REPLACE "ARCCOS"  WITH "ACN"              IN lNewExpression
               REPLACE "ASIN"    WITH "ASN"              IN lNewExpression
               REPLACE "ARCSIN"  WITH "ASN"              IN lNewExpression
            
               FUNCTION = lNewExpression
            
            END FUNCTION
            
            
            FUNCTION EvalExpNum(BYVAL pExpression AS STRING) AS DOUBLE
            
               DIM lOpenParentheseStart                              AS LONG
               DIM lOpenParentheseCounter                            AS LONG
               DIM lCloseParentheseCounter                           AS LONG
            
               DIM lCurrentPos                                       AS LONG
               DIM lI                                                AS LONG
               DIM lPosOperator                                      AS LONG
               DIM lLeftOperandStart                                 AS LONG
               DIM lleftOperandEnd                                   AS LONG
               DIM lRightOperandStart                                AS LONG
               DIM lRightOperandEnd                                  AS LONG
            
               DIM lEval                                             AS DOUBLE
               DIM lLeftOperand                                      AS DOUBLE
               DIM lRightOperand                                     AS DOUBLE
            
               DO
            
                  lOpenParentheseCounter  = 0 : lCloseParentheseCounter = 0      '  Reset the parentheses counters
                  lOpenParentheseStart = INSTR(pExpression, "(")                 '  Search for a open parenthese "(" in the expression
            
                  IF lOpenParentheseStart <> 0                       THEN        '  only if we find a open parenthese "(" in the expression
            
                     lOpenParentheseCounter = 1                                  '  Initialize the open parenthese counter to 1
                     lCurrentPos = lOpenParentheseStart                          '  we start at the open parenthese
            
                     DO
            
                        INCR lCurrentPos                                         '  Increment the current position
            
                        IF MID$(pExpression, lCurrentPos, 1) = ")"   THEN
                           INCR lCloseParentheseCounter                          '  to count the number of parentheses
                        END IF
            
                        IF MID$(pExpression, lCurrentPos, 1) = "("   THEN
                           INCR lOpenParentheseCounter
                        END IF
            
                     LOOP UNTIL lOpenParentheseCounter = lCloseParentheseCounter
            
                     pExpression = LEFT$(pExpression,lOpenParentheseStart-1)                                                           + _   '  Recursive call to EvalExpNum
                                   FORMAT$(EvalExpNum(MID$(pExpression,lOpenParentheseStart+1,lCurrentPos-lOpenParentheseStart-1)))    + _
                                   RIGHT$(pExpression,-lCurrentPos)
                  ELSE
            
                     DATA "COS"     , "ACN"
                     DATA "SIN"     , "ASN"
                     DATA "TAN"     , "ATN"
                     DATA "LOGTEN"  , "LOGTWO"  , "LOG"     , "EXPTEN"
                     DATA "EXPTWO"  , "EXP"     , "SQR"     , "ABS"     , "MOD"
                     DATA "^"       , "*"       , "/"       , "\"       , "_"       , "+"
                     DATA "<"       , "<="
                     DATA ">"       , ">="
                     DATA "=="
            
                     FOR lI = 1 TO DATACOUNT                                     '  Check all the Operators/Functions that could be used in the numerical expression
            
                        DO                                                       '  Examines all the occurences of the operator/function
            
                           lPosOperator = INSTR(pExpression, READ$(lI))
            
                           IF lPosOperator <> 0                      THEN        '  Only if we find the Operator/Function
            
                              lLeftOperandEnd = lPosOperator - 1                 '  Search the start/end of the left operand
                              lCurrentPos     = lPosOperator
            
                              DO
                                 DECR lCurrentPos                                '  Decrement current pos
            
                                 IF   lCurrentPos < 1                THEN
                                    EXIT LOOP                                    '  If we are out of the string
                                 END IF
            
                              LOOP UNTIL INSTR(" 0123456789.-", MID$(pExpression, lCurrentPos, 1)) = 0
                              lLeftOperandStart = lCurrentPos + 1
            
                              lRightOperandStart = lPosOperator + LEN(READ$(lI)) ' search the start/end of the right operand
                              lCurrentPos        = lPosOperator + LEN(READ$(lI)) - 1
            
                              DO
            
                                 INCR lCurrentPos                                ' decrement current pos
            
                                 IF   lCurrentPos > LEN(pExpression) THEN
                                    EXIT LOOP                                    '  If we are out of the string
                                 END IF
            
                              LOOP UNTIL INSTR(" 0123456789.-", MID$(pExpression, lCurrentPos, 1)) = 0
            
                              lRightOperandEnd = lCurrentPos - 1
            
                              '  Extract the operands removing spaces
                              lLeftOperand  = VAL(TRIM$(MID$(pExpression, lLeftOperandStart , lLeftOperandEnd - lLeftOperandStart  + 1)))
                              lRightOperand = VAL(TRIM$(MID$(pExpression, lRightOperandStart, lRightOperandEnd- lRightOperandStart + 1)))
            
                              '  Depending of the Operator/Function
                              SELECT CASE READ$(lI)
            
                                 CASE "COS"        :  lEval = COS(lRightOperand)
            '                     CASE "ACN"        :  lEval = ACOS_DP(lRightOperand)
            
                                 CASE "SIN"        :  lEval = SIN(lRightOperand)
            '                     CASE "ASN"        :  lEval = ASIN_DP(lRightOperand)
            '
                                 CASE "TAN"        :  lEval = TAN(lRightOperand)
                                 CASE "ATN"        :  lEval = ATN(lRightOperand)
            
                                 CASE "LOGTEN"     :  lEval = LOG10(lRightOperand)
                                 CASE "LOGTWO"     :  lEval = LOG2(lRightOperand)
                                 CASE "LOG"        :  lEval = LOG(lRightOperand)
                                 CASE "EXPTEN"     :  lEval = EXP10(lRightOperand)
                                 CASE "EXPTWO"     :  lEval = EXP2(lRightOperand)
                                 CASE "EXP"        :  lEval = EXP(lRightOperand)
                                 CASE "SQR"        :  lEval = SQR(lRightOperand)
                                 CASE "ABS"        :  lEval = ABS(lRightOperand)
                                 CASE "MOD"        :  lEval = lLeftOperand MOD lRightOperand
            
                                 CASE "^"          :  lEval = lLeftOperand ^ lRightOperand
                                 CASE "*"          :  lEval = lLeftOperand * lRightOperand
                                 CASE "/"          :  lEval = lLeftOperand / lRightOperand
                                 CASE "\"          :  lEval = lLeftOperand \ lRightOperand
                                 CASE "_"          :  lEval = lLeftOperand - lRightOperand
                                 CASE "+"          :  lEval = lLeftOperand + lRightOperand
            
                                 CASE ">"          :  lEval = -(lLeftOperand >  lRightOperand)
                                 CASE ">="         :  lEval = -(lLeftOperand >= lRightOperand)
            
                                 CASE "<"          :  lEval = -(lLeftOperand <  lRightOperand)
                                 CASE "<="         :  lEval = -(lLeftOperand <= lRightOperand)
            
                                 CASE "=="         :  lEval = -(lLeftOperand =  lRightOperand)
            
                                 CASE ELSE         :  lEval = 0.0#
            
                              END SELECT
            
                              '  Round the evaluation to n decimal places to avoid scientific notation (E-)
                              '  Note this was orginally 6 places, but testing shows that it cannot be too large, value of 8 currently used
                              lEval = ROUND(lEval, 06)
            
                              '  Place the result of the evaluation in the string
                              pExpression =  LEFT$(pExpression, lLeftOperandStart - 1)    + _
                                             FORMAT$(lEval)                               + _
                                             RIGHT$(pExpression,-lRightOperandEnd)
            
                           END IF
            
                        LOOP UNTIL lPosOperator = 0
            
                     NEXT lI
            
                     '  Evaluate the final expression
                     FUNCTION = VAL(pExpression)
            
                  END IF
            
               LOOP UNTIL lOpenParentheseStart = 0
            
            END FUNCTION

            Comment


            • #7
              Here is the end use of the math evaluator. I started a few simple commands but am wondering how far I may take this. The math evaluator is really nice. Click image for larger version

Name:	PV Math Evaluator.png
Views:	69
Size:	36.4 KB
ID:	778173

              Comment


              • #8
                from post 2
                Except I cannot get the >= and <= cases working. I don't follow the code well enough to debug this. Maybe you can offer a simple solution?
                and they were working already and you didn't say what was wrong.

                A little late now that I've got myself roped in too, but from top of page -
                Forum Guidelines

                This forum is for finished source code that is working properly. If you have questions about this or any other source code, please post it in one of the Discussion Forums, not here.
                for future reference.
                Dale

                Comment

                Working...
                X