Make static evaluation of java arithmethic JS compliant.

JavaScript always performs floating point operations in 64-bit
precision IEEE-754. Whereas Java has two floating point types.
The float type has a precision that nominally corresponds to
 32-bit precision IEEE-754, and the double type to 64-bit
precision IEEE-754. On top Java can do most float operaions
in a precision that is at least the one required unless (strictfp
is specified).

This patch makes sure that static evaluation optimizations are
always performed in JavaScript semantics.

Bug: issue 8909.
Change-Id: I32f68009d689acf842674b932ed1f511763e337a
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JFloatLiteral.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JFloatLiteral.java
index 197ea05..05ef771 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JFloatLiteral.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JFloatLiteral.java
@@ -23,10 +23,10 @@
  */
 public class JFloatLiteral extends JValueLiteral {
 
-  public static final JFloatLiteral ZERO = new JFloatLiteral(SourceOrigin.UNKNOWN, Float
-      .intBitsToFloat(0));
+  public static final JFloatLiteral ZERO = new JFloatLiteral(SourceOrigin.UNKNOWN, Double
+      .longBitsToDouble(0));
 
-  public static JFloatLiteral get(float value) {
+  public static JFloatLiteral get(double value) {
     return isZero(value) ? ZERO : new JFloatLiteral(SourceOrigin.UNKNOWN, value);
   }
 
@@ -34,13 +34,13 @@
    * Does this value match the exact 0 bit pattern? (This precludes
    * canonicalizing -0.0 as 0.0).
    */
-  private static boolean isZero(float value) {
-    return Float.floatToRawIntBits(value) == 0;
+  private static boolean isZero(double value) {
+    return Double.doubleToRawLongBits(value) == 0L;
   }
 
-  private final float value;
+  private final double value;
 
-  public JFloatLiteral(SourceInfo sourceInfo, float value) {
+  public JFloatLiteral(SourceInfo sourceInfo, double value) {
     super(sourceInfo);
     this.value = value;
   }
@@ -50,13 +50,13 @@
     return JPrimitiveType.FLOAT;
   }
 
-  public float getValue() {
+  public double getValue() {
     return value;
   }
 
   @Override
   public Object getValueObj() {
-    return new Float(value);
+    return new Double(value);
   }
 
   @Override
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
index 1c81594..a013234 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
@@ -782,7 +782,7 @@
     return JDoubleLiteral.get(d);
   }
 
-  public JFloatLiteral getLiteralFloat(float f) {
+  public JFloatLiteral getLiteralFloat(double f) {
     return JFloatLiteral.get(f);
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java b/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java
index 9180b42..2b5afe5 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java
@@ -37,6 +37,7 @@
 import com.google.gwt.dev.jjs.ast.JExpressionStatement;
 import com.google.gwt.dev.jjs.ast.JField;
 import com.google.gwt.dev.jjs.ast.JFieldRef;
+import com.google.gwt.dev.jjs.ast.JFloatLiteral;
 import com.google.gwt.dev.jjs.ast.JForStatement;
 import com.google.gwt.dev.jjs.ast.JIfStatement;
 import com.google.gwt.dev.jjs.ast.JInstanceOf;
@@ -66,6 +67,7 @@
 import com.google.gwt.dev.jjs.ast.JVisitor;
 import com.google.gwt.dev.jjs.ast.JWhileStatement;
 import com.google.gwt.dev.jjs.ast.js.JMultiExpression;
+import com.google.gwt.dev.util.Ieee754_64_Arithmetic;
 import com.google.gwt.dev.util.log.speedtracer.CompilerEventType;
 import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger;
 import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Event;
@@ -793,11 +795,8 @@
       if (isTypeBoolean(lhs)) {
         return toBoolean(lhs) == toBoolean(rhs);
       }
-      if (isTypeDouble(lhs) || isTypeDouble(rhs)) {
-        return toDouble(lhs) == toDouble(rhs);
-      }
-      if (isTypeFloat(lhs) || isTypeFloat(rhs)) {
-        return toFloat(lhs) == toFloat(rhs);
+      if (isTypeFloatingPoint(lhs) || isTypeFloatingPoint(rhs)) {
+        return Ieee754_64_Arithmetic.eq(toDouble(lhs), toDouble(rhs));
       }
       if (isTypeLong(lhs) || isTypeLong(rhs)) {
         return toLong(lhs) == toLong(rhs);
@@ -833,11 +832,11 @@
             return true;
           }
           if (isTypeDouble(exp)) {
-            ctx.replaceMe(program.getLiteralDouble(-toDouble(exp)));
+            ctx.replaceMe(program.getLiteralDouble(Ieee754_64_Arithmetic.neg(toDouble(exp))));
             return true;
           }
           if (isTypeFloat(exp)) {
-            ctx.replaceMe(program.getLiteralFloat(-toFloat(exp)));
+            ctx.replaceMe(program.getLiteralFloat(Ieee754_64_Arithmetic.neg(toDouble(exp))));
             return true;
           }
           return false;
@@ -876,26 +875,26 @@
         case MUL:
         case DIV:
         case MOD: {
-          if (isTypeFloatOrDouble(lhs) || isTypeFloatOrDouble(rhs)) {
+          if (isTypeFloatingPoint(lhs) || isTypeFloatingPoint(rhs)) {
             // do the op on doubles and cast back
             double left = toDouble(lhs);
             double right = toDouble(rhs);
             double res;
             switch (op) {
               case ADD:
-                res = left + right;
+                res = Ieee754_64_Arithmetic.add(left, right);
                 break;
               case SUB:
-                res = left - right;
+                res = Ieee754_64_Arithmetic.subtract(left, right);
                 break;
               case MUL:
-                res = left * right;
+                res = Ieee754_64_Arithmetic.multiply(left, right);
                 break;
               case DIV:
-                res = left / right;
+                res = Ieee754_64_Arithmetic.divide(left, right);
                 break;
               case MOD:
-                res = left % right;
+                res = Ieee754_64_Arithmetic.mod(left, right);
                 break;
               default:
                 assert false;
@@ -904,7 +903,7 @@
             if (isTypeDouble(lhs) || isTypeDouble(rhs)) {
               ctx.replaceMe(program.getLiteralDouble(res));
             } else {
-              ctx.replaceMe(program.getLiteralFloat((float) res));
+              ctx.replaceMe(program.getLiteralFloat(res));
             }
             return true;
           } else if (isTypeIntegral(lhs) && isTypeIntegral(rhs)) {
@@ -955,23 +954,23 @@
         case LTE:
         case GT:
         case GTE: {
-          if (isTypeFloatOrDouble(lhs) ||  isTypeFloatOrDouble(lhs)) {
+          if (isTypeFloatingPoint(lhs) ||  isTypeFloatingPoint(lhs)) {
             // operate on doubles
             double left = toDouble(lhs);
             double right = toDouble(rhs);
             boolean res;
             switch (op) {
               case LT:
-                res = left < right;
+                res = Ieee754_64_Arithmetic.lt(left, right);
                 break;
               case LTE:
-                res = left <= right;
+                res = Ieee754_64_Arithmetic.le(left, right);
                 break;
               case GT:
-                res = left > right;
+                res = Ieee754_64_Arithmetic.gt(left, right);
                 break;
               case GTE:
-                res = left >= right;
+                res = Ieee754_64_Arithmetic.ge(left, right);
                 break;
               default:
                 assert false;
@@ -1209,37 +1208,28 @@
     }
 
     private boolean isLiteralNegativeOne(JExpression exp) {
-      if (exp instanceof JValueLiteral) {
-        JValueLiteral lit = (JValueLiteral) exp;
-        if (isTypeIntegral(lit)) {
-          if (toLong(lit) == -1) {
-            return true;
-          }
-        }
-        if (isTypeFloatOrDouble(lit)) {
-          if (toDouble(lit) == -1.0) {
-            return true;
-          }
-        }
+      if (!(exp instanceof JValueLiteral)) {
+        return false;
+      }
+      JValueLiteral lit = (JValueLiteral) exp;
+      if (isTypeIntegral(lit) && toLong(lit) == -1) {
+        return true;
+      }
+      if (isTypeFloatingPoint(lit) && toDouble(lit) == -1.0) {
+        return true;
       }
       return false;
     }
 
     private boolean isLiteralOne(JExpression exp) {
-      if (exp instanceof JValueLiteral) {
-        JValueLiteral lit = (JValueLiteral) exp;
-        if (isTypeIntegral(lit)) {
-          if (toLong(lit) == 1) {
-            return true;
-          }
-        }
-        if (isTypeFloatOrDouble(lit)) {
-          if (toDouble(lit) == 1.0) {
-            return true;
-          }
-        }
+      if (!(exp instanceof JValueLiteral)) {
+        return false;
       }
-      return false;
+      JValueLiteral lit = (JValueLiteral) exp;
+      if (isTypeIntegral(lit) && toLong(lit) == 1) {
+        return true;
+      }
+      return isTypeFloatingPoint(lit) && toDouble(lit) == 1.0;
     }
 
     private boolean isLiteralZero(JExpression exp) {
@@ -1278,11 +1268,11 @@
     /**
      * Return whether the type of the expression is float or double.
      */
-    private boolean isTypeFloatOrDouble(JExpression exp) {
-      return isTypeFloatOrDouble(exp.getType());
+    private boolean isTypeFloatingPoint(JExpression exp) {
+      return isTypeFloatingPoint(exp.getType());
     }
 
-    private boolean isTypeFloatOrDouble(JType type) {
+    private boolean isTypeFloatingPoint(JType type) {
       return ((type == program.getTypePrimitiveDouble()) || (type == program
           .getTypePrimitiveFloat()));
     }
@@ -1632,7 +1622,7 @@
         ctx.replaceMe(Simplifier.cast(type, lhs));
         return true;
       }
-      if (isLiteralZero(lhs) && !isTypeFloatOrDouble(type)) {
+      if (isLiteralZero(lhs) && !isTypeFloatingPoint(type)) {
         ctx.replaceMe(simplifyNegate(Simplifier.cast(type, rhs)));
         return true;
       }
@@ -1683,10 +1673,6 @@
       }
     }
 
-    private float toFloat(JValueLiteral x) {
-      return (float) toDouble(x);
-    }
-
     /**
      * Cast a Java wrapper class (Integer, Double, Float, etc.) to a long.
      */
@@ -1897,12 +1883,12 @@
       if (type == char.class && maybeLit instanceof JCharLiteral) {
         return Character.valueOf(((JCharLiteral) maybeLit).getValue());
       }
+      if (type == double.class && maybeLit instanceof JFloatLiteral) {
+        return new Double(((JFloatLiteral) maybeLit).getValue());
+      }
       if (type == double.class && maybeLit instanceof JDoubleLiteral) {
         return new Double(((JDoubleLiteral) maybeLit).getValue());
       }
-      if (type == float.class && maybeLit instanceof JIntLiteral) {
-        return new Float(((JIntLiteral) maybeLit).getValue());
-      }
       if (type == int.class && maybeLit instanceof JIntLiteral) {
         return Integer.valueOf(((JIntLiteral) maybeLit).getValue());
       }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java
index d761f21..65b0040 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java
@@ -462,7 +462,7 @@
 
   @Override
   public boolean visit(JFloatLiteral x, Context ctx) {
-    printFloatLiteral(x.getValue());
+    printDoubleLiteral(x.getValue());
     return false;
   }
 
@@ -1043,11 +1043,6 @@
     }
   }
 
-  protected void printFloatLiteral(float value) {
-    print(Float.toString(value));
-    print('f');
-  }
-
   protected void printLongLiteral(long value) {
     print(Long.toString(value));
     print('L');
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/gflow/constants/ConstantsAssumption.java b/dev/core/src/com/google/gwt/dev/jjs/impl/gflow/constants/ConstantsAssumption.java
index a93e3c2..024dfc1 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/gflow/constants/ConstantsAssumption.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/gflow/constants/ConstantsAssumption.java
@@ -136,9 +136,9 @@
     }
 
     if (literal1 instanceof JFloatLiteral) {
-      int bits1 = Float.floatToRawIntBits(
+      long bits1 = Double.doubleToRawLongBits(
           ((JFloatLiteral) literal1).getValue());
-      int bits2 = Float.floatToRawIntBits(
+      long bits2 = Double.doubleToRawLongBits(
           ((JFloatLiteral) literal2).getValue());
       return bits1 == bits2;
     }
diff --git a/dev/core/src/com/google/gwt/dev/js/JsStaticEval.java b/dev/core/src/com/google/gwt/dev/js/JsStaticEval.java
index 87acfe0..1da3276 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsStaticEval.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsStaticEval.java
@@ -50,6 +50,7 @@
 import com.google.gwt.dev.js.ast.JsVisitor;
 import com.google.gwt.dev.js.ast.JsWhile;
 import com.google.gwt.dev.js.rhino.ScriptRuntime;
+import com.google.gwt.dev.util.Ieee754_64_Arithmetic;
 import com.google.gwt.dev.util.log.speedtracer.CompilerEventType;
 import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger;
 import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Event;
@@ -182,19 +183,22 @@
         trySimplifyEqAndRefEq(x, arg1, arg2, ctx);
       } else if (op == JsBinaryOperator.NEQ || op == JsBinaryOperator.REF_NEQ) {
         trySimplifyNeAndRefNe(x, arg1, arg2, ctx);
-      } else if (op == JsBinaryOperator.ADD) {
-        trySimplifyAdd(x, arg1, arg2, ctx);
-      } else  {
-       switch (op) {
-         case GT:
-         case GTE:
-         case LT:
-         case LTE:
-           trySimplifyCompare(x, arg1, arg2, op, ctx);
-           break;
-         default:
-           break;
-       }
+      } else if (arg1 instanceof JsValueLiteral && arg2 instanceof JsValueLiteral) {
+         switch (op) {
+           case ADD:
+           case SUB:
+           case MUL:
+           case DIV:
+           case MOD:
+           case GT:
+           case GTE:
+           case LT:
+           case LTE:
+             trySimplifyOp(x, arg1, arg2, op, ctx);
+             break;
+           default:
+             break;
+         }
       }
     }
 
@@ -565,37 +569,6 @@
       }
     }
 
-    private JsExpression simplifyCompare(JsExpression original, JsExpression arg1,
-        JsExpression arg2, JsBinaryOperator op) {
-      assert (original != null);
-
-      // TODO(cromwellian) handle all types
-      if (arg1 instanceof JsNumberLiteral && arg2 instanceof JsNumberLiteral) {
-          double num1 = ((JsNumberLiteral) arg1).getValue();
-          double num2 = ((JsNumberLiteral) arg2).getValue();
-          boolean result = false;
-          switch(op) {
-            case LT:
-              result = num1 < num2;
-              break;
-            case LTE:
-              result = num1 <= num2;
-              break;
-            case GT:
-              result = num1 > num2;
-              break;
-            case GTE:
-              result = num1 >= num2;
-              break;
-            default:
-              throw new InternalCompilerException("Can't handle simplify of op " + op);
-          }
-        return JsBooleanLiteral.get(result);
-      }
-      // no simplification made
-      return original;
-    }
-
     private JsExpression simplifyEqAndRefEq(JsExpression original, JsExpression arg1,
         JsExpression arg2) {
       assert (original != null);
@@ -653,25 +626,62 @@
     }
 
     /**
-     * Simplify a + b.
+     * Simplify a op b.
      */
-    private void trySimplifyAdd(JsExpression original, JsExpression arg1,
-        JsExpression arg2, JsContext ctx) {
-      if (arg1 instanceof JsValueLiteral && arg2 instanceof JsValueLiteral) {
-        SourceInfo info = original.getSourceInfo();
-        // case: number + number
-        if (arg1 instanceof JsNumberLiteral && arg2 instanceof JsNumberLiteral) {
-          double value = ((JsNumberLiteral) arg1).getValue()
-              + ((JsNumberLiteral) arg2).getValue();
-          ctx.replaceMe(new JsNumberLiteral(info, value));
-        } else {
-          // cases: number + string or string + number
-          StringBuilder result = new StringBuilder();
-          if (appendLiteral(result, (JsValueLiteral) arg1)
-              && appendLiteral(result, (JsValueLiteral) arg2)) {
-            ctx.replaceMe(new JsStringLiteral(info, result.toString()));
-          }
+    private void trySimplifyOp(JsExpression original, JsExpression arg1, JsExpression arg2,
+        JsBinaryOperator op, JsContext ctx) {
+      SourceInfo info = original.getSourceInfo();
+
+      if (op == JsBinaryOperator.ADD &&
+          (arg1 instanceof JsStringLiteral || arg2 instanceof JsStringLiteral)) {
+        // cases: number + string or string + number
+        StringBuilder result = new StringBuilder();
+        if (appendLiteral(result, (JsValueLiteral) arg1)
+            && appendLiteral(result, (JsValueLiteral) arg2)) {
+          ctx.replaceMe(new JsStringLiteral(info, result.toString()));
         }
+        return;
+      }
+
+      if (arg1 instanceof JsNumberLiteral && arg2 instanceof JsNumberLiteral) {
+        double num1 = ((JsNumberLiteral) arg1).getValue();
+        double num2 = ((JsNumberLiteral) arg2).getValue();
+        Object result;
+
+        switch (op) {
+          case ADD:
+            result = Ieee754_64_Arithmetic.add(num1, num2);
+            break;
+          case SUB:
+            result = Ieee754_64_Arithmetic.subtract(num1, num2);
+            break;
+          case MUL:
+            result = Ieee754_64_Arithmetic.multiply(num1, num2);
+            break;
+          case DIV:
+            result = Ieee754_64_Arithmetic.divide(num1, num2);
+            break;
+          case MOD:
+            result = Ieee754_64_Arithmetic.mod(num1, num2);
+            break;
+          case LT:
+            result = Ieee754_64_Arithmetic.lt(num1, num2);
+            break;
+          case LTE:
+            result = Ieee754_64_Arithmetic.le(num1, num2);
+            break;
+          case GT:
+            result = Ieee754_64_Arithmetic.gt(num1, num2);
+            break;
+          case GTE:
+            result = Ieee754_64_Arithmetic.ge(num1, num2);
+            break;
+          default:
+            throw new InternalCompilerException("Can't handle simplify of op " + op);
+        }
+        ctx.replaceMe(result instanceof Double ?
+            new JsNumberLiteral(info, ((Double) result).doubleValue()) :
+            JsBooleanLiteral.get(((Boolean) result).booleanValue()));
       }
     }
 
@@ -774,14 +784,6 @@
       return toReturn;
     }
 
-    private void trySimplifyCompare(JsExpression original, JsExpression arg1,
-        JsExpression arg2, JsBinaryOperator op, JsContext ctx) {
-      JsExpression updated = simplifyCompare(original, arg1, arg2, op);
-      if (updated != original) {
-        ctx.replaceMe(updated);
-      }
-    }
-
     private void trySimplifyEqAndRefEq(JsExpression original, JsExpression arg1,
         JsExpression arg2, JsContext ctx) {
       JsExpression updated = simplifyEqAndRefEq(original, arg1, arg2);
diff --git a/dev/core/src/com/google/gwt/dev/util/Ieee754_64_Arithmetic.java b/dev/core/src/com/google/gwt/dev/util/Ieee754_64_Arithmetic.java
new file mode 100644
index 0000000..05f79c5
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/util/Ieee754_64_Arithmetic.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.util;
+
+/**
+ * A class to perform arithmetic that is consistent with JavaScript.
+ */
+public strictfp class Ieee754_64_Arithmetic {
+
+  public static double add(double a, double b) {
+    return a + b;
+  }
+
+  public static double multiply(double a, double b) {
+    return a * b;
+  }
+
+  public static double divide(double a, double b) {
+    return a / b;
+  }
+
+  public static double subtract(double a, double b) {
+    return a - b;
+  }
+
+  public static double mod(double a, double b) {
+    return a % b;
+  }
+
+  public static boolean eq(double a, double b) {
+    return a == b;
+  }
+
+  public static boolean gt(double a, double b) {
+    return a > b;
+  }
+
+  public static boolean ge(double a, double b) {
+    return a >= b;
+  }
+
+  public static boolean le(double a, double b) {
+    return a <= b;
+  }
+
+  public static boolean lt(double a, double b) {
+    return a < b;
+  }
+
+  public static double neg(double a) {
+    return -a;
+  }
+ }
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/DeadCodeEliminationTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/DeadCodeEliminationTest.java
index 85bd45e..4502725 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/impl/DeadCodeEliminationTest.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/DeadCodeEliminationTest.java
@@ -305,11 +305,18 @@
     // Verify that float/double subtracts from zero aren't replaced, since they
     // are needed for obscure IEEE754 functionality -- specifically, converting
     // 0.0 - v into -v means the sign of the result is the opposite of the input
-    // rathe than always being positive.
-    optimize("float", "return 0.0F - f;").intoString("return 0.0f - EntryPoint.f;");
+    // rather than always being positive.
+    optimize("float", "return 0.0F - f;").intoString("return 0.0 - EntryPoint.f;");
     optimize("double", "return 0.0 - d;").intoString("return 0.0 - EntryPoint.d;");
   }
 
+  public void testFloatingPoint() throws Exception {
+    // Internally we represent float literals as double, so here we make sure that 1.1f is
+    // is printed as a double with the right precision.
+    optimize("float", "return 1.1f;").intoString("return " + String.format("%.16g", (double) 1.1f) +
+        ";");
+  }
+
   public void testMultiExpression_RedundantClinitRemoval() throws Exception {
     addSnippetClassDecl(
         "static class A  { "
diff --git a/dev/core/test/com/google/gwt/dev/js/JsStaticEvalTest.java b/dev/core/test/com/google/gwt/dev/js/JsStaticEvalTest.java
index ad8facc..bdad22d 100644
--- a/dev/core/test/com/google/gwt/dev/js/JsStaticEvalTest.java
+++ b/dev/core/test/com/google/gwt/dev/js/JsStaticEvalTest.java
@@ -45,7 +45,7 @@
     assertEquals("a()&&b,c();", optimize("a() && b, c()"));
 
     // Don't damage math expressions
-    assertEquals("alert(seconds/(60*60));",
+    assertEquals("alert(seconds/3600);",
         optimize("alert(seconds / (60 * 60))"));
     assertEquals("alert(1-(1-foo));", optimize("alert(1 - (1 - foo))"));
 
diff --git a/user/test/com/google/gwt/dev/jjs/test/CompilerMiscRegressionTest.java b/user/test/com/google/gwt/dev/jjs/test/CompilerMiscRegressionTest.java
index 1fa7281..0932be7 100644
--- a/user/test/com/google/gwt/dev/jjs/test/CompilerMiscRegressionTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/CompilerMiscRegressionTest.java
@@ -25,6 +25,8 @@
 import com.google.gwt.dev.jjs.test.overrides.package2.SomeSubSubClassInAnotherPackage;
 import com.google.gwt.dev.jjs.test.overrides.package3.SomeInterface;
 import com.google.gwt.dev.jjs.test.overrides.package3.SomePackageConfusedParent;
+import com.google.gwt.junit.DoNotRunWith;
+import com.google.gwt.junit.Platform;
 import com.google.gwt.junit.client.GWTTestCase;
 
 import java.util.ArrayList;
@@ -258,6 +260,23 @@
        regExp1.test(str), regExp2.test(str));
   }-*/;
 
+  /**
+   * Test for issue 8909.
+   * <p>
+   * DevMode does not conform to JS arithmetic semantics and this method tests exactly that.
+   */
+  @DoNotRunWith(Platform.Devel)
+  public void testStaticEvaluationSematics() {
+    float num = getRoundedValue(1.005f);
+    assertEquals(1.00, num, 0.001);
+  }
+
+  private float getRoundedValue(float parameter) {
+    float local = parameter;
+    local = local * 100f;
+    return Math.round(local) / 100f;
+  }
+
   private static void assertEqualContents(float[] expected, float[] actual) {
 
     assertEquals("Array length mismatch", expected.length, actual.length);
diff --git a/user/test/com/google/gwt/validation/rebind/GwtSpecificValidatorCreatorTest.java b/user/test/com/google/gwt/validation/rebind/GwtSpecificValidatorCreatorTest.java
index aa9399b..ae62513 100644
--- a/user/test/com/google/gwt/validation/rebind/GwtSpecificValidatorCreatorTest.java
+++ b/user/test/com/google/gwt/validation/rebind/GwtSpecificValidatorCreatorTest.java
@@ -62,7 +62,9 @@
   }
 
   public void testAsLiteral_1_1f() {
-    assertLiteral("1.1f", 1.1f);
+    // Internally we represent float literals as double, so here we make sure that 1.1f is
+    // is printed as a double with the right precision.
+    assertLiteral(String.format("%.16g", (double) 1.1f), 1.1f);
   }
 
   public void testAsLiteral_1L() {