Question

I'm new to Delphi XE5 (part of Embarcadero's RAD Studio), and using the StrToFloat function in a VCL Forms project designed to build a simple calculator.

The code to display the text to the user works well, but I'm having trouble extracting it in a way that will allow the calculator to actually perform calculations. The means I've settled on is displaying the code to the user in a Tedit, and storing the code to be executed in a Tmemo. Pressing the buttons on the calculator stores the numbers to both, and pressing an operator (the symbols for add, subtract, exponent, multiply, and divide) starts a new line in the memo. This way, each number is on its own line, and each operator is on its own line.

I then want to extract the numbers with respect to the operators, by iterating through the lines of the memo. For 3 ^ 4, you would see...

3
^
4

...in the TMemo.

I want to find the ^ sign at line index 1 (lines start at index 0, right?), and store 3 in a base variable by the logic that, if ^ is lines[1], then 3 is lines[1-1], or lines[0], and store 4 in an exponent variable. Both variables would be of type extended. This is the code I've created to do this...

 procedure TForm1.btnEqualsClick(Sender: TObject);
var
  i: Integer;
  base: extended;
  exponent: extended;
  result: extended;
begin
  {This For loop looks for the exponentiation operator (^) in memCurrentEntry. If
   it's found, it then solves the equation.}
   for i := Low(memCurrentEntry.Lines.Text.Length) to High(memCurrentEntry.Lines.Text.Length) do
    if AnsiContainsStr(memCurrentEntry.Lines.Text, '^') then
    begin
        base := StrToFloat(memCurrentEntry.Lines[ansipos('^', memCurrentEntry.Lines.Text)-1]);
       exponent := StrToFloat(memCurrentEntry.Lines[ansiPos('^', memCurrentEntry.Lines.Text)+2]);
       result := power(base, exponent);
    end;

When I run the calculator, the numbers appear correctly, but when I hit the equals button to run the code above, I get the message Project Calculator_u.exe raised exception class EConvertError with Message ''' is not a valid floating point value'.

I know the StrToFloat function is trying to convert a null character or something (How do I identify what code is generating " '' is not a valid floating point value" error dialogue box), but what should I do to correct this?

Below is the code for each button -- the digit (0, 1, 2, 3, etc.) changes, but the rest is the same for each one...

procedure TForm1.btn0Click(Sender: TObject);
begin
  //Adds the digit  0 to the end of the current number in memCurrentEntry
  memCurrentEntry.SelStart := Length(memCurrentEntry.Text);
  memCurrentEntry.Lines.Text := Copy(memCurrentEntry.Lines.Text,1,
    memCurrentEntry.SelStart)+'0'+ Copy(memCurrentEntry.Lines.Text,
    memCurrentEntry.SelStart+1,Length(memCurrentEntry.Lines.Text)
    - memCurrentEntry.SelStart);

  //Adds the digit  0 to the end of the current number in edtCurrentEntry
  edtcurrententry.Text := edtcurrententry.Text + '0';
end;

I can post the full unit file upon request.

Was it helpful?

Solution

Use TryStrToFloat. This returns a boolean indicating whether or not the conversion succeeded.

var
  str: string;
  val: Double;
....
if TryStrToFloat(str, val) then
  // val contains the floating point value represented by str
else
  // str does not contain a floating point value, do something else with it

Of course, you could use StrToFloat, and catch the exception. But since you expect to encounter the exception, it will result in much cleaner code if you use TryStrToFloat.

OTHER TIPS

the usage of lines on the memo is wrong:

base := StrToFloat(memCurrentEntry.Lines[ansipos('^', memCurrentEntry.Lines.Text)-1]);

the lines.text returns the ENTIRE text, which is not what you want. Instead use some LOCAL variables to hold your lookup values...that way you can set a breakpoint and see whats wrong and use the following method to locate the "^"

result := 0;
carrotLine := memCurrentEntry.Lines.IndexOf('^');
if carrotline > 0 then // must be 1 or greater, so a number can be at the 0 position
  begin
    base := TryStrToFloat(MemCurrentEntry.Lines[carrotLine-1],0);
    exponent := 0;
    // only try to read the next value if there is one.  
    if memCurrentEntry.Lines.Count >= carrotLine then
      exponent := TryStrToFloat(MemCurrentEntry.Lines[carrotline+1],0);
    result := power(base,exponent);
  end;

Note that this code ONLY handles a single ^ in the memo (your code had the same issue), to go beyond this is to use a loop and possibly a state engine or some other parsing logic to handle computations.

Use StrToFloat is not wrong. If the conversion is failed, user should see an error message. If you want to suppress the error quietly, then you should add a try/except to protect your code.

try
  Value := StrToFloat(A_String);
except
  on EConvertError do
    Value := 0; // Fallback to a default value or execute other code logic
end;

According to your given code example, if the base and the exponent are entered in your given format (first line is the base, second line is a "^" and the third line is the exponent). I'd suggest you write your code as follows:

var
  Numbers: TArray<string>;
  // Other variables go here
begin
  // ...
  Numbers := memCurrentEntry.Lines.Text.Split('^'); 
  if Length(Numbers) = 2 then
  try
    Base := StrToFloat(Numbers[0]);
    Exponent := StrToFloat(Numbers[1]);
    Result := Power(Base, Exponent);
  except
    on EConvertError do
    begin
      ShowMessage(Format('Unable to calculate the formula %s', [memCurrentEntry.Lines.Text]));
      Result := 0; // Fallback to a default value, such as 0
    end;
  end;
  // ...
end;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top