Repository: josdejong/mathjs
Pull request #653 (2016-04-19 21:59:12)

Unit-less units become numeric values when predictable:false
Unit-less units are now converted to numeric values when predictable:false, which addresses #651.


Patch:
diff --git a/lib/type/unit/Unit.js b/lib/type/unit/Unit.js
index 031e47870..b6f4a41c1 100644
--- a/lib/type/unit/Unit.js
+++ b/lib/type/unit/Unit.js
@@ -655,7 +655,8 @@ function factory (type, config, load, typed, math) {
 
     // Trigger simplification of the unit list at some future time
     res.isUnitListSimplified = false;
-    return res;
+
+    return getNumericIfUnitless(res);
   };
 
   /**
@@ -691,7 +692,8 @@ function factory (type, config, load, typed, math) {
 
     // Trigger simplification of the unit list at some future time
     res.isUnitListSimplified = false;
-    return res;
+
+    return getNumericIfUnitless(res);
   };
 
   /**
@@ -727,9 +729,25 @@ function factory (type, config, load, typed, math) {
 
     // Trigger lazy evaluation of the unit list
     res.isUnitListSimplified = false;
-    return res;
+
+    return getNumericIfUnitless(res);
   };
 
+  /**
+   * Return the numeric value of this unit if it is dimensionless, has a value, and config.predictable == false; or the original unit otherwise
+   * @param {Unit} unit
+   * @returns {number | Fraction | BigNumber | Unit}  The numeric value of the unit if conditions are met, or the original unit otherwise
+   */
+  var getNumericIfUnitless = function(unit) {
+    if(unit.equalBase(BASE_UNITS.NONE) && unit.value !== null && !config.predictable) {
+      return unit.value;
+    }
+    else {
+      return unit;
+    }
+  }
+    
+
   /**
    * Calculate the absolute value of a unit
    * @memberof Unit


Test patch:
diff --git a/test/function/arithmetic/divide.test.js b/test/function/arithmetic/divide.test.js
index 49ebd658e..3e181278c 100644
--- a/test/function/arithmetic/divide.test.js
+++ b/test/function/arithmetic/divide.test.js
@@ -147,10 +147,10 @@ describe('divide', function() {
   });
 
   it('should divide one valued unit by a valueless unit and vice-versa', function() {
-    assert.equal(divide(math.unit('4 gal'), math.unit('L')).format(5), '15.142');
-    assert.equal(divide(math.unit('gal'), math.unit('4 L')).format(3), '0.946');
+    assert.equal(divide(math.unit('4 gal'), math.unit('L')).toString(), '15.141648');
+    assert.equal(divide(math.unit('gal'), math.unit('4 L')).toString(), '0.946353');
 
-    assert.equal(divide(math.unit('inch'), math.unit(math.fraction(1), 'cm')), '127/50');
+    assert.equal(divide(math.unit('inch'), math.unit(math.fraction(1), 'cm')).toFraction(), '127/50');
   });
 
   it('should divide (but not simplify) two valueless units', function() {
diff --git a/test/type/unit/Unit.test.js b/test/type/unit/Unit.test.js
index 8532d1283..675c81ee3 100644
--- a/test/type/unit/Unit.test.js
+++ b/test/type/unit/Unit.test.js
@@ -520,14 +520,65 @@ describe('Unit', function() {
 
     });
 
-    it('should simplify units even when they cancel out', function() {
+    it('should simplify units when they cancel out with {predictable: true}', function() {
+      var origConfig = math.config();
+      math.config({predictable: true});
       var unit1 = new Unit (2, "Hz");
       var unit2 = new Unit(2, "s");
       var unit3 = math.multiply(unit1, unit2);
       assert.equal(unit3.toString(), "4");
       assert.equal(unit3.units.length, 0);
+
+      var nounit = math.eval('40m * 40N / (40J)');
+      assert.equal(nounit.toString(), "40");
+      assert.equal(nounit.units.length, 0);
+
+      var a = math.unit('3 s^-1');
+      var b = math.unit('4 s');
+      assert.equal(math.multiply(a, b).type, 'Unit');
+
+      var c = math.unit('8.314 J / mol / K');
+      assert.equal(math.pow(c, 0).type, 'Unit');
+
+      var d = math.unit('60 minute');
+      var e = math.unit('1 s');
+      assert.equal(math.divide(d, e).type, 'Unit');
+
+      math.config(origConfig);
     })
 
+    it('should convert units to appropriate _numeric_ values when they cancel out with {predictable: false}', function() {
+      var origConfig = math.config();
+      math.config({predictable: false});
+
+      assert.equal(typeof(math.eval('40 m * 40 N / (40 J)')), 'number');
+
+      var bigunit = math.unit(math.bignumber(1), 'km');
+      var smallunit = math.unit(math.bignumber(3000000), 'mm');
+      var verybignumber = math.divide(bigunit, smallunit);
+      assert.equal(verybignumber.type, 'BigNumber');
+      assert.equal(verybignumber.toString(), '0.3333333333333333333333333333333333333333333333333333333333333333');
+
+      bigunit = math.unit(math.fraction(1), 'km');
+      smallunit = math.unit(math.fraction(3000000), 'mm');
+      verybignumber = math.divide(bigunit, smallunit);
+      assert.equal(verybignumber.type, 'Fraction');
+      assert.equal(verybignumber.toFraction(), '1/3');
+
+      var a = math.unit('3 s^-1');
+      var b = math.unit('4 s');
+      assert.equal(typeof(math.multiply(a, b)), 'number');
+
+      var c = math.unit('8.314 J / mol / K');
+      assert.equal(typeof(math.pow(c, 0)), 'number');
+
+      var d = math.unit('60 minute');
+      var e = math.unit('1 s');
+      assert.equal(typeof(math.divide(d, e)), 'number');
+
+      math.config(origConfig);
+    });
+
     it('should simplify units according to chosen unit system', function() {
       var unit1 = new Unit(10, "N");
       Unit.setUnitSystem('us');