summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorHans Boehm <hboehm@google.com>2015-07-30 17:00:33 -0700
committerHans Boehm <hboehm@google.com>2015-10-11 05:49:43 +0000
commit6ab94a3f166c3bb1bbca83f60a2cd435285fb786 (patch)
tree3640dd46f7f66fc2a1f0179913f27b9a8fc8ad75 /src
parentb7bd34886fb39401b6e38ad97634655a0d025902 (diff)
downloadandroid_packages_apps_ExactCalculator-6ab94a3f166c3bb1bbca83f60a2cd435285fb786.tar.gz
android_packages_apps_ExactCalculator-6ab94a3f166c3bb1bbca83f60a2cd435285fb786.tar.bz2
android_packages_apps_ExactCalculator-6ab94a3f166c3bb1bbca83f60a2cd435285fb786.zip
Generalize % operator: Handle 100+10% as expected
Bug: 22836179 Handle + or - operators specially if they are followed by a constant or pre-evaluated expression and the % operator. Since this is basically an idiomatic use of the operators, we apply it very conservatively. When in doubt we use the old interpretation. Also fixes one unrelated anachronism in a comment. Change-Id: I2f9fd26dd6c0456f0210815ef2bc1afca3a2b4d7 (cherry picked from commit 8afd0f85ed0b9fa1c96297c540cb74e6d8b9a64d)
Diffstat (limited to 'src')
-rw-r--r--src/com/android/calculator2/CalculatorExpr.java80
1 files changed, 71 insertions, 9 deletions
diff --git a/src/com/android/calculator2/CalculatorExpr.java b/src/com/android/calculator2/CalculatorExpr.java
index b387e8b..0850fe5 100644
--- a/src/com/android/calculator2/CalculatorExpr.java
+++ b/src/com/android/calculator2/CalculatorExpr.java
@@ -310,7 +310,7 @@ class CalculatorExpr {
}
// In writing out PreEvals, we are careful to avoid writing
// out duplicates. We assume that two expressions are
- // duplicates if they have the same mVal. This avoids a
+ // duplicates if they have the same CR value. This avoids a
// potential exponential blow up in certain off cases and
// redundant evaluation after reading them back in.
// The parameter hash map maps expressions we've seen
@@ -1022,6 +1022,58 @@ class CalculatorExpr {
return new EvalRet(cpos, crVal, ratVal);
}
+ /**
+ * Is the subexpression starting at pos a simple percent constant?
+ * This is used to recognize exppressions like 200+10%, which we handle specially.
+ * This is defined as a Constant or PreEval token, followed by a percent sign, and followed
+ * by either nothing or an additive operator.
+ * Note that we are intentionally far more restrictive in recognizing such expressions than
+ * e.g. http://blogs.msdn.com/b/oldnewthing/archive/2008/01/10/7047497.aspx .
+ * When in doubt, we fall back to the the naive interpretation of % as 1/100.
+ * Note that 100+(10)% yields 100.1 while 100+10% yields 110. This may be controversial,
+ * but is consistent with Google web search.
+ */
+ private boolean isPercent(int pos) {
+ if (mExpr.size() < pos + 2 || !isOperatorUnchecked(pos + 1, R.id.op_pct)) {
+ return false;
+ }
+ Token number = mExpr.get(pos);
+ if (number instanceof Operator) {
+ return false;
+ }
+ if (mExpr.size() == pos + 2) {
+ return true;
+ }
+ if (!(mExpr.get(pos + 2) instanceof Operator)) {
+ return false;
+ }
+ Operator op = (Operator) mExpr.get(pos + 2);
+ return op.id == R.id.op_add || op.id == R.id.op_sub;
+ }
+
+ /**
+ * Compute the multiplicative factor corresponding to an N% addition or subtraction.
+ * @param pos position of Constant or PreEval expression token corresponding to N
+ * @param isSubtraction this is a subtraction, as opposed to addition
+ * @param ec usable evaluation contex; only length matters
+ * @return Rational and CR values; position is pos + 2, i.e. after percent sign
+ */
+ private EvalRet getPercentFactor(int pos, boolean isSubtraction, EvalContext ec)
+ throws SyntaxException {
+ EvalRet tmp = evalUnary(pos, ec);
+ BoundedRational ratVal = isSubtraction ? BoundedRational.negate(tmp.ratVal)
+ : tmp.ratVal;
+ CR crVal = isSubtraction ? tmp.val.negate() : tmp.val;
+ ratVal = BoundedRational.add(BoundedRational.ONE,
+ BoundedRational.multiply(ratVal, RATIONAL_ONE_HUNDREDTH));
+ if (ratVal == null) {
+ crVal = CR.ONE.add(crVal.multiply(REAL_ONE_HUNDREDTH));
+ } else {
+ crVal = ratVal.CRValue();
+ }
+ return new EvalRet(pos + 2 /* after percent sign */, crVal, ratVal);
+ }
+
private EvalRet evalExpr(int i, EvalContext ec) throws SyntaxException {
EvalRet tmp = evalTerm(i, ec);
boolean is_plus;
@@ -1030,20 +1082,30 @@ class CalculatorExpr {
BoundedRational ratVal = tmp.ratVal;
while ((is_plus = isOperator(cpos, R.id.op_add, ec))
|| isOperator(cpos, R.id.op_sub, ec)) {
- tmp = evalTerm(cpos+1, ec);
- if (is_plus) {
- ratVal = BoundedRational.add(ratVal, tmp.ratVal);
+ if (isPercent(cpos + 1)) {
+ tmp = getPercentFactor(cpos + 1, !is_plus, ec);
+ ratVal = BoundedRational.multiply(ratVal, tmp.ratVal);
if (ratVal == null) {
- crVal = crVal.add(tmp.val);
+ crVal = crVal.multiply(tmp.val);
} else {
crVal = ratVal.CRValue();
}
} else {
- ratVal = BoundedRational.subtract(ratVal, tmp.ratVal);
- if (ratVal == null) {
- crVal = crVal.subtract(tmp.val);
+ tmp = evalTerm(cpos + 1, ec);
+ if (is_plus) {
+ ratVal = BoundedRational.add(ratVal, tmp.ratVal);
+ if (ratVal == null) {
+ crVal = crVal.add(tmp.val);
+ } else {
+ crVal = ratVal.CRValue();
+ }
} else {
- crVal = ratVal.CRValue();
+ ratVal = BoundedRational.subtract(ratVal, tmp.ratVal);
+ if (ratVal == null) {
+ crVal = crVal.subtract(tmp.val);
+ } else {
+ crVal = ratVal.CRValue();
+ }
}
}
cpos = tmp.pos;