Friday, January 27, 2012

Calculator Examples

I am posting two examples of Calculator code that work. They both have different but effective approaches. The first is From Victoria Bailey. I have only included the code from the form.

Victoria's only works with binary operations, that is [number] [operator] [number], for example 4 + 2, 3 * 5, etc. It doesn't work with examples like 4 + 3 * 2 / 2. Christian's does

Default.aspx
   <form id="form1" runat="server">
    <div>
    
        <asp:Label ID="lblScreen" runat="server" Text=""></asp:Label><br />
        <asp:Button ID="Button1" runat="server" Text="1"  
onclick="btnNumber_Click" /><asp:Button ID="Button2"
runat="server" Text="2" onclick="btnNumber_Click" /> 
<asp:Button ID="Button3" runat="server" Text="3" onclick="btnNumber_Click" /><br />
        <asp:Button ID="Button4" runat="server" 
Text="4" onclick="btnNumber_Click" /><asp:Button ID="Button5"
            runat="server" Text="5" onclick="btnNumber_Click" />
<asp:Button ID="Button6" runat="server" Text="6" onclick="btnNumber_Click" /><br />
        <asp:Button ID="Button7" runat="server" 
Text="7" onclick="btnNumber_Click" /><asp:Button ID="Button8"
            runat="server" Text="8" 
onclick="btnNumber_Click" />
<asp:Button ID="Button9" runat="server" Text="9" 
onclick="btnNumber_Click" /><br />
        <asp:Button ID="Button10" runat="server" Text="0" 
onclick="btnNumber_Click" /><br />
        <asp:Button ID="Button11" runat="server" Text="+" 
onclick="btnOperation_Click" />
        <asp:Button ID="Button12" runat="server" Text="-" 
onclick="btnOperation_Click" />
        <asp:Button ID="Button13" runat="server" Text="*" 
onclick="btnOperation_Click" />
        <asp:Button ID="Button14" runat="server" Text="/" 
onclick="btnOperation_Click" />
        <br /><br />

        <asp:Button ID="btnCalculate"
            runat="server" Text="Calculate" onclick="btnCalculate_Click" /><br /><br />
        <asp:Label ID="lblAnswer" runat="server" Text="Answer: "></asp:Label>
        <asp:Label ID="lblCalculation" runat="server" Text=""></asp:Label><br /><br />

    </div>
    </form>



Code Behind:
    protected void Page_Load(object sender, EventArgs e)
    {
    }


    protected void btnCalculate_Click(object sender, EventArgs e)
    {
        string op = Session["operation"].ToString();
        double number = 0;
        double secondNumber = 0;
        double total = 0;
        if (lblScreen.Text != String.Empty)
        {
            secondNumber = double.Parse(lblScreen.Text);
        }
        if ((Session["firstNumber"] != null) && (Session["firstNumber"].ToString() != ""))
        {
            number = double.Parse(Session["firstNumber"].ToString());
        }
        switch (op)
        {
            case "+":
                total = number + secondNumber;
                break;
            case "-":
                total = number - secondNumber;
                break;
            case "*":
                total = number * secondNumber;
                break;
            case "/":
                total = number / secondNumber;
                break;
            default:
                break;
        }
        lblCalculation.Text = total.ToString();
    }

    protected void btnNumber_Click(object sender, EventArgs e)
    {
        Button button = (Button)sender;
        lblScreen.Text += button.Text;
    }

    protected void btnOperation_Click(object sender, EventArgs e)
    {
        Session["firstNumber"] = lblScreen.Text;
        Button button = (Button)sender;
        Session["operation"] = button.Text;
        lblScreen.Text = String.Empty;
    }


The second example is from Christian Roehr. It uses a very different approach. It is also well commented. Again I have only included the source from the form

>
 <form id="form1" runat="server">
 <div>
 <table>
 <tbody>

 <tr>
 <td colspan="4"></td>
     <asp:TextBox ID="txtDisplay" runat="server"></asp:TextBox>
 </tr>

 <tr>
 <td>
     <asp:Button ID="Button1" runat="server" Text="7" 
CssClass="buttonstyle" OnClick="InputNumber_Click"/></td>
 <td>
     <asp:Button ID="Button2" runat="server" Text="8" 
CssClass="buttonstyle" OnClick="InputNumber_Click"/></td>
 <td>
     <asp:Button ID="Button3" runat="server" Text="9" 
CssClass="buttonstyle" OnClick="InputNumber_Click"/></td>
 <td>
     <asp:Button ID="btnAdd" runat="server" Text="+" 
CssClass="buttonstyle" OnClick="Operator_Click"/></td>   
 </tr>

 <tr>
 <td>
     <asp:Button ID="Button5" runat="server" Text="4" 
CssClass="buttonstyle" OnClick="InputNumber_Click"/></td>
 <td>
     <asp:Button ID="Button6" runat="server" Text="5" 
CssClass="buttonstyle" OnClick="InputNumber_Click"/></td>
 <td>
     <asp:Button ID="Button7" runat="server" Text="6"
 CssClass="buttonstyle" OnClick="InputNumber_Click"/></td>
 <td>
     <asp:Button ID="btnSubtract" runat="server" Text="-" 
CssClass="buttonstyle" OnClick="Operator_Click"/></td>   
 </tr>

 <tr>
 <td>
     <asp:Button ID="Button9" runat="server" Text="1" 
CssClass="buttonstyle" OnClick="InputNumber_Click"/></td>
 <td>
     <asp:Button ID="Button10" runat="server" Text="2" 
CssClass="buttonstyle" OnClick="InputNumber_Click"/></td>
 <td>
     <asp:Button ID="Button11" runat="server" Text="3" 
CssClass="buttonstyle" OnClick="InputNumber_Click"/></td>
 <td>
     <asp:Button ID="btnMult" runat="server" Text="*" 
CssClass="buttonstyle" OnClick="Operator_Click"/></td>  
 </tr>

 <tr>
 <td>
     <asp:Button ID="btnDel" runat="server" Text="Del" ToolTip="Use the <Del> button to clear the textbox in order to correct a value before clicking one of the operator buttons. The intermediate result and last operator are still kept in memory." CssClass="buttonstyle"
         onclick="btnDel_Click"/></td>
 <td>
     <asp:Button ID="Button14" runat="server" Text="0" 
CssClass="buttonstyle" OnClick="InputNumber_Click"/></td>
 <td>
     <asp:Button ID="Button15" runat="server" Text="." 
CssClass="buttonstyle" OnClick="InputNumber_Click"/></td>
 <td>
     <asp:Button ID="btnDiv" runat="server" Text="/" 
CssClass="buttonstyle" OnClick="Operator_Click"/></td>    
 </tr>

 <tr>
 <td>
     <asp:Button ID="btnClear" runat="server" Text="CL" ToolTip="Use the <CL> button to reset the calculator to its original state. Any prior values or operators are lost." CssClass="buttonstyle" OnClick="btnCl_Click"/></td>  
 <td colspan="3">
     <asp:Button ID="btnEqual" runat="server" Text="="  CssClass="buttonstyle" OnClick="Operator_Click"/></td>    
 </tr>  


 </tbody>
    
 </table>
 </div>
 </form>
</body>
</html>

======================== default.aspx.cs
using System;
using System.Diagnostics; // needed for "Debug" class
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

public partial class _Default : System.Web.UI.Page
{
 // define unique integers to identify one of
 // {+,-,*,/,=}
 protected enum OperatorTypes
 {
     UNKNOWN=0,
     ADD = 1,
     SUBTRACT = 2,
     MULT = 3,
     DIVIDE = 4,
     EQUALS = 5
 };
 protected const double m_initialValue = 0.0;
 protected void Page_Load(object sender, EventArgs e)
 {
     if (!IsPostBack) // wonna do it when page loads but not every time when we push a button
     {
         InitializeCalculator();
     }
 }
 protected void InitializeCalculator()
 {
     // START: define the session variables
     //
     // Here is why the operator is initially "+".
     // When the user clicks any operator
     // op_new = one of {+,-,*,/,=}
     // then the evaluation of the
     // _previous_ operator (op_prev ) happens and _not_ the one that was just clicked (op_new).
     // Thus, if we want everything to work the same and if we want to save ourselves
     // too much extra specialized initialization-specific code, we can just assume that
     // the very first op_prev was "+" and that the previous result (result_prev) was 0.0.
     // Then , when op_new is clicked, we evaluate
     // result_new = execute(result_prev, op_prev, number);
     // Where "number" is the number the user typed in prior to pressing op_new. "number"
     // got parsed right when we clicked op_new.
     // We could just as well have selected Session["result"] = 1.0 and Session["operator"] = "*"
     // but "+" is simpler and somehow cleaner, avoiding possible rounding errors.
     //
     // In any future discussion, i will call the session variables
     // the "stack", since that is what we kind of do when we
     // store the value on the server side and update it or retrieve it.
     Session["result"] = m_initialValue; // this writes it to the server and gives it a value of 0.0
     Session["operator"] = OperatorTypes.ADD;

     // END: define the session variables

     // in order to facilitate the very first parsing of a number
     // down the line, let's put a "0" into the text field.
     // That is how most calculators work and it also
     // puts it in sync with what is in our initial session "result" value.
     InitializeTextDisplay();
 }
 protected void InitializeTextDisplay()
 {
     txtDisplay.Text = m_initialValue.ToString();
 }
 protected void EmptyTextDisplayIfContainsInitialValue()
 {
     if (txtDisplay.Text == m_initialValue.ToString())
     {
         txtDisplay.Text = "";
     }
 }
 protected void InputNumber_Click(object sender, EventArgs e)
 {
     // avoid this scenario from happening:
     // after entering a number, get something like 06, 0555, 000, 0024
     // We dont want such numbers since they may not be parsable, or even if
     // they are, they dont look pretty.
     EmptyTextDisplayIfContainsInitialValue();

     // the button that is sending the message
     Button b = (Button)sender;
     txtDisplay.Text += b.Text;

 }
 protected void Operator_Click(object sender, EventArgs e)
 {
     // the button that is sending the message
     Button b = (Button)sender;
     // we could use the ID or we could use the text

     // START: determine which operator was clicked
     //
     // FYI: Equals() operator more optimized to work with strings, but could have done '==' too
     //
     OperatorTypes op_new = OperatorTypes.UNKNOWN;
     if (b.ID.Equals("btnAdd"))
     {
         op_new = OperatorTypes.ADD;
     }
     else if (b.ID.Equals("btnSubtract"))
     {
         op_new = OperatorTypes.SUBTRACT;
     }
     else if (b.ID.Equals("btnMult"))
     {
         op_new = OperatorTypes.MULT;
     }
     else if (b.ID.Equals("btnDiv"))
     {
         op_new = OperatorTypes.DIVIDE;
     }
     else if (b.ID.Equals("btnEqual"))
     {
         op_new = OperatorTypes.EQUALS;
     }
     else
     {
         // this should not happen anyways if we wrote
         // the code correctly.
         op_new = OperatorTypes.UNKNOWN;
     }

     // for debugging only
     Debug.Write(String.Format("op_new={0}\n", (int)op_new));

     if (op_new == OperatorTypes.UNKNOWN)
     {
         Exception ex = new Exception("Invalid operator");
         throw ex;
     }

     // END: determine which operator was clicked

     // 1. Parse the number which user had entered up to
     // the point of clicking one of the operator buttons.
     double operandNumber = GetNumber();

     // 2. Get the last result from the stack.
     double result_prev = (double)Session["result"];

     // 3. Get the operator from the stack.
     // This operator is _not_ the one we just got, but the one
     // entered before user entered <operandNumber>
     OperatorTypes op_prev = (OperatorTypes)Session["operator"];
     Debug.WriteLine("op_prev={0}\n", (int)op_prev);

     // 4. evaluate the new result based
     // on <result_prev>, <op_prev>, <operandNumber>
     // For instance, if op_prev is "*" (multiplication), then
     //
     // result_new = result_prev * operandNumber
     double result_new = ExecuteOperator(result_prev, op_prev, operandNumber);
     Debug.WriteLine("result_new={0}\n", result_new);

     // test if the number is wellformed. That can be for one of the following reasons
     // a) divide-by-zero
     // b) overflow (two very large numbers multiplied or a very small one in the denominator)
     // c) perhaps some other reason
     if (double.IsInfinity(result_new) || double.IsNaN(result_new))
     {
         // first reset the calculator since you cannot utilize the result for any future operation
         InitializeCalculator();

         // throw an exception
         string numberString = string.Format("resulting number = {0} .", result_new);
         Exception ex = new Exception(numberString+ "You either divided by zero or the operands resulted in an out-of-range number");
         throw ex;
     }




     // 5. put new result on the stack
     Session["result"] = result_new;

     // 6. put new operator on the stack
     Session["operator"] = op_new;

     // 7. display the new result
     if (op_new == OperatorTypes.EQUALS)
     {
         // return result to text box
         txtDisplay.Text = result_new.ToString();
     }
     else
     {
         // as shown in the programming assignment specification,
         // ("and clear it to make it ready for the next entry")
         // clear the text (even though that is not the typical
         // calculator behavior i think. the typical calculator
         // shows the intermittent result and only clears after
         // you press the first digit of
         // the new number you are about to enter)
         InitializeTextDisplay();
     }
 }
 protected double GetNumber()
 {
     // double number = double.Parse(txtDisplay.Text);
     double number = 0;
     bool isDouble = double.TryParse(txtDisplay.Text, out number);
     if (isDouble == false)
     {
         txtDisplay.Text = "";
         Exception ex = new Exception("Only enter valid numbers");
         throw ex;
     }
     return number;
 }
 // Perform the operation
 // returnvalue = result_old op num
 // e.g. if op=OperatorTypes.ADD
 // you will get
 // returnvalue = result_old + num
 protected double ExecuteOperator(
     double result_old,
     OperatorTypes op,
     double num
     )
 {
     double result = result_old; // default in case of an invalid operator
     switch (op)
     {
         case OperatorTypes.ADD:
             result = result_old + num;
             break;
         case OperatorTypes.SUBTRACT:
             result = result_old - num;
             break;
         case OperatorTypes.MULT:
             result = result_old * num;
             break;
         case OperatorTypes.DIVIDE:
             result = result_old / num;
             break;
         case OperatorTypes.EQUALS:
             result = result_old;
             break;
         default:
             result = result_old;
             break;
     }
     return result;
 }
 protected void btnDel_Click(object sender, EventArgs e)
 {
     // as specified in the assignment
     // have a "clear" button to clear the textbox.
     InitializeTextDisplay();
 }
 protected void btnCl_Click(object sender, EventArgs e)
 {
     // if we dont have a CLEAR button to reset everything,
     // I found that funky things happen where the operators
     // stop doing the right things after clicking a certain
     // combination of equal sign and operators etc.
     // Thus we must have a way to clear all memory and reset
     // the stack to the beginning of what it was when the calculator
     // first started.
     InitializeCalculator();
 }
}

No comments:

Post a Comment