[haiku-development] Re: DeskCalc Improvements (was need strtold() function)

  • From: John Scipione <jscipione@xxxxxxxxx>
  • To: haiku-development@xxxxxxxxxxxxx
  • Date: Thu, 31 Dec 2009 03:25:02 -0500

On Wed, Dec 30, 2009 at 3:23 PM, John Scipione <jscipione@xxxxxxxxx> wrote:
>> I think it would be better if
>> a) You used BString API instead of copying the string to a char[] buffer 
>> (for example you can use BString::CountChars to get the length accounting 
>> for multibyte characters),
>> b) You adapted the length of the displayed number to the window size.
>>
>> For number formatting, the Locale Kit should be taking care of it, have a 
>> look at headers/os/locale/FloatFormat.h . This is experimental API so :
>> - Some of the functions present in the header may be buggy or do nothing at 
>> all,
>> - You may need things that aren't available in this API.
>> In any case, please let me know and I will complete the locale kit as you 
>> need.
>>
>> --
>> Adrien Destugues / PulkoMandy
>> http://pulkomandy.ath.cx
>>
>
> On point a (should use BString API) I agree, I am sure there is a
> better way to do what I am trying to do using existing classes rather
> than resorting to sprintf and friends on a regular char array.
> However, me not being a BeOS/Haiku expert I went with what I know. If
> the FloatFormat class can do a better job than sprintf() and friends
> at rounding numbers and formatting them as exponentials, etc. than I
> am all for it. I'll work on it and post a revised patch.
>
> ---- Long rant follows, exec summary: resizing needs work ----
>
> On point b (adapt the length of the displayed number to the window
> size) I have to disagree. It is too hard a problem and not worth it to
> try and fit the result in whatever arbitrary window size is chosen. I
> would have to recalculate the result as you resize which is clunky and
> strange. A better method is to pick some precision and make sure that
> the result fits in the display window no matter how big or small you
> make the app. I don't like how when you resize the window less digits
> fit. Look at any other calculator apps out there and you'll see that
> they all use a fixed width and don't resize the input window.
>
> I like the fact that you can resize the window and it scales the font
> but I think it should scale the font in such a way that the result
> always fits in the display window. Perhaps a solution would be to
> disable resizing and have two "modes" -- small mode and big mode. You
> toggle between them by clicking the double block icon in the right
> hand side of the title bar. In either mode you get the same number of
> digits of result but in big mode the font is, well, bigger. Another
> more difficult but perhaps better solution would be to scale the font
> gradually as you resize.
>
> Either way the result has to be rounded at some point so that it fits
> in the window.
>
> John Scipione
>

I rewrote my DeskCalc changes so that I would not be doing any char*
manipulation. To achieve this had to extend the ExpressionParser Class
with two new methods: EvaluateToStandard() and EvaluateToScientific().
Each of these methods takes an int32 argument to specify the number of
digits to grab. Trailing zeros after the decimal point are removed.
Please review my patch below. Should patch from the haiku/ directory.

Index: headers/private/shared/ExpressionParser.h
===================================================================
--- headers/private/shared/ExpressionParser.h   (revision 34828)
+++ headers/private/shared/ExpressionParser.h   (working copy)
@@ -45,6 +45,11 @@
                        void                            SetSupportHexInput(bool 
enabled);

                        BString                         Evaluate(const char* 
expressionString);
+                       BString                         EvaluateToStandard(
+                                    const char* expressionString,
+                                    int32 decimalPlaces);
+                       BString                         
EvaluateToScientific(const char*
+                                    expressionString, int32 decimalPlaces);
                        int64                           EvaluateToInt64(const 
char* expressionString);
                        double                          EvaluateToDouble(const 
char* expressionString);

Index: src/apps/deskcalc/CalcView.cpp
===================================================================
--- src/apps/deskcalc/CalcView.cpp      (revision 34828)
+++ src/apps/deskcalc/CalcView.cpp      (working copy)
@@ -894,6 +894,9 @@
 void
 CalcView::Evaluate()
 {
+    const double STD_NOTATION_UPPER = 1e16;
+    const double STD_NOTATION_LOWER = 1e-16;
+
        BString expression = fExpressionTextView->Text();

        if (expression.Length() == 0) {
@@ -903,21 +906,39 @@

        _AudioFeedback(false);

+    ExpressionParser parser;
+    double value = 0.00;
+    BString result;
+
        // evaluate expression
-       BString value;
-
        try {
-               ExpressionParser parser;
-               value = parser.Evaluate(expression.String());
+               value = parser.EvaluateToDouble(expression.String());
        } catch (ParseException e) {
                BString error(e.message.String());
                error << " at " << (e.position + 1);
-               fExpressionTextView->SetText(error.String());
-               return;
+        // just print error
+        result.SetTo("error");
+        fExpressionTextView->SetExpression(result.String());
+        return;
        }

+    if (value == 0) {
+        result.SetTo("0");
+        fExpressionTextView->SetExpression(result.String());
+        return;
+    } else if (((value < STD_NOTATION_UPPER)
+             && (value > STD_NOTATION_LOWER))
+             || ((value > -STD_NOTATION_UPPER)
+             && (value < -STD_NOTATION_LOWER))) {
+        // Standard Notation
+        result = parser.EvaluateToStandard(expression.String(), 16);
+    } else {
+        // Scientific Notation
+        result = parser.EvaluateToScientific(expression.String(), 12);
+    }
+
        // render new result to display
-       fExpressionTextView->SetExpression(value.String());
+       fExpressionTextView->SetExpression(result.String());
 }


Index: src/kits/shared/ExpressionParser.cpp
===================================================================
--- src/kits/shared/ExpressionParser.cpp        (revision 34828)
+++ src/kits/shared/ExpressionParser.cpp        (working copy)
@@ -317,6 +317,14 @@
 BString
 ExpressionParser::Evaluate(const char* expressionString)
 {
+    return EvaluateToStandard(expressionString, kMaxDecimalPlaces);
+}
+
+
+BString
+ExpressionParser::EvaluateToStandard(const char* expressionString,
+                                     int32 decimalPlaces)
+{
        fTokenizer->SetTo(expressionString);

        MAPM value = _ParseBinary();
@@ -327,11 +335,14 @@
        if (value == 0)
                return BString("0");

-       char* buffer = value.toFixPtStringExp(kMaxDecimalPlaces, '.', 0, 0);
+    if (decimalPlaces <= 0)
+        decimalPlaces = kMaxDecimalPlaces;
+
+       char* buffer = value.toFixPtStringExp(decimalPlaces, '.', 0, 0);
        if (buffer == NULL)
                throw ParseException("out of memory", 0);

-       // remove surplus zeros
+       // remove trailing decimal zeros
        int32 lastChar = strlen(buffer) - 1;
        if (strchr(buffer, '.')) {
                while (buffer[lastChar] == '0')
@@ -343,6 +354,50 @@
        BString result(buffer, lastChar + 1);
        free(buffer);
        return result;
+}
+
+
+BString
+ExpressionParser::EvaluateToScientific(const char* expressionString,
+                                       int32 decimalPlaces)
+{
+       fTokenizer->SetTo(expressionString);
+
+       MAPM value = _ParseBinary();
+       Token token = fTokenizer->NextToken();
+       if (token.type != TOKEN_END_OF_LINE)
+               throw ParseException("parse error", token.position);
+
+       if (value == 0)
+               return BString("0");
+
+    if (decimalPlaces <= 0)
+        decimalPlaces = kMaxDecimalPlaces;
+
+    // max number of characters possible for 32 bits of decimal places is
+    // decimal places + 15 for -0.9999999....E+2147483647
+    // 3 + decimal digits + 2 + 10 = decimalPlaces + 15
+       char buffer[decimalPlaces + 15];
+    value.toString(buffer, decimalPlaces);
+       if (buffer == NULL)
+               throw ParseException("out of memory", 0);
+
+       BString result(buffer, decimalPlaces + 15);
+
+       // remove trailing decimal zeros
+    if (result.FindFirst('.') != B_ERROR) {
+        int32 offset = result.FindLast('E');
+        if (offset != B_ERROR) {
+            offset--; // go to character before 'E'
+            while(result[offset] == '0') {
+                result = result.Remove(offset, sizeof(char));
+                offset--;
+            }
+            if(result[offset] == '.')
+                result = result.Remove(offset, sizeof(char));
+        }
+    }
+       return result;
 }

Other related posts: