Permalink
Browse files

Implemented a variant of Math.floor and Math.ceil which tolerate smal…

…l rounding errors in the passed floating point number.

R=chrishenry
DELTA=63 (62 added, 0 deleted, 1 changed)


Revision created by MOE tool push_codebase.
MOE_MIGRATION=6171


git-svn-id: http://closure-library.googlecode.com/svn/trunk@2454 0b95b8e8-c90f-11de-9d4f-f947ee5921c8
  • Loading branch information...
1 parent 47ac18b commit d239fcda39f351008505081797caea8db67e4bdf pallosp@google.com committed Jan 22, 2013
Showing with 62 additions and 1 deletion.
  1. +1 −1 closure/goog/deps.js
  2. +32 −0 closure/goog/math/math.js
  3. +29 −0 closure/goog/math/math_test.html
@@ -384,7 +384,7 @@ goog.addDependency('math/interpolator/pchip1.js', ['goog.math.interpolator.Pchip
goog.addDependency('math/interpolator/spline1.js', ['goog.math.interpolator.Spline1'], ['goog.array', 'goog.math', 'goog.math.interpolator.Interpolator1', 'goog.math.tdma']);
goog.addDependency('math/line.js', ['goog.math.Line'], ['goog.math', 'goog.math.Coordinate']);
goog.addDependency('math/long.js', ['goog.math.Long'], []);
-goog.addDependency('math/math.js', ['goog.math'], ['goog.array']);
+goog.addDependency('math/math.js', ['goog.math'], ['goog.array', 'goog.asserts']);
goog.addDependency('math/matrix.js', ['goog.math.Matrix'], ['goog.array', 'goog.math', 'goog.math.Size']);
goog.addDependency('math/range.js', ['goog.math.Range'], []);
goog.addDependency('math/rangeset.js', ['goog.math.RangeSet'], ['goog.array', 'goog.iter.Iterator', 'goog.iter.StopIteration', 'goog.math.Range']);
@@ -19,6 +19,7 @@
goog.provide('goog.math');
goog.require('goog.array');
+goog.require('goog.asserts');
/**
@@ -353,3 +354,34 @@ goog.math.isInt = function(num) {
goog.math.isFiniteNumber = function(num) {
return isFinite(num) && !isNaN(num);
};
+
+
+/**
+ * A tweaked variant of {@code Math.floor} which tolerates if the passed number
+ * is infinitesimally smaller than the closest integer. It often happens with
+ * the results of floating point calculations because of the finite precision
+ * of the intermediate results. For example {@code Math.floor(Math.log(1000) /
+ * Math.LN10) == 2}, not 3 as one would expect.
+ * @param {number} num A number.
+ * @param {number=} opt_epsilon An infinitesimally small positive number, the
+ * rounding error to tolerate.
+ * @return {number} The largest integer less than or equal to {@code num}.
+ */
+goog.math.safeFloor = function(num, opt_epsilon) {
+ goog.asserts.assert(!goog.isDef(opt_epsilon) || opt_epsilon > 0);
+ return Math.floor(num + (opt_epsilon || 2e-15));
+};
+
+
+/**
+ * A tweaked variant of {@code Math.ceil}. See {@code goog.math.safeFloor} for
+ * details.
+ * @param {number} num A number.
+ * @param {number=} opt_epsilon An infinitesimally small positive number, the
+ * rounding error to tolerate.
+ * @return {number} The smallest integer greater than or equal to {@code num}.
+ */
+goog.math.safeCeil = function(num, opt_epsilon) {
+ goog.asserts.assert(!goog.isDef(opt_epsilon) || opt_epsilon > 0);
+ return Math.ceil(num - (opt_epsilon || 2e-15));
+};
@@ -249,6 +249,35 @@
assertTrue(goog.math.isFiniteNumber(1));
assertTrue(goog.math.isFiniteNumber(Math.PI));
}
+
+ function testSafeFloor() {
+ assertEquals(0, goog.math.safeFloor(0));
+ assertEquals(0, goog.math.safeFloor(1e-15));
+ assertEquals(0, goog.math.safeFloor(-1e-15));
+ assertEquals(-1, goog.math.safeFloor(-3e-15));
+ assertEquals(4, goog.math.safeFloor(5 - 3e-15));
+ assertEquals(5, goog.math.safeFloor(5 - 1e-15));
+ assertEquals(-5, goog.math.safeFloor(-5 - 1e-15));
+ assertEquals(-6, goog.math.safeFloor(-5 - 3e-15));
+ assertEquals(3, goog.math.safeFloor(2.91, 0.1));
+ assertEquals(2, goog.math.safeFloor(2.89, 0.1));
+ // Tests some real life examples with the default epsilon value.
+ assertEquals(0, goog.math.safeFloor(Math.log(1000) / Math.LN10 - 3));
+ assertEquals(21, goog.math.safeFloor(Math.log(1e+21) / Math.LN10));
+ }
+
+ function testSafeCeil() {
+ assertEquals(0, goog.math.safeCeil(0));
+ assertEquals(0, goog.math.safeCeil(1e-15));
+ assertEquals(0, goog.math.safeCeil(-1e-15));
+ assertEquals(1, goog.math.safeCeil(3e-15));
+ assertEquals(6, goog.math.safeCeil(5 + 3e-15));
+ assertEquals(5, goog.math.safeCeil(5 + 1e-15));
+ assertEquals(-4, goog.math.safeCeil(-5 + 3e-15));
+ assertEquals(-5, goog.math.safeCeil(-5 + 1e-15));
+ assertEquals(3, goog.math.safeCeil(3.09, 0.1));
+ assertEquals(4, goog.math.safeCeil(3.11, 0.1));
+ }
</script>
</body>
</html>

0 comments on commit d239fcd

Please sign in to comment.