Various fixes and enhancements:
authorMatt McCutchen <hashproduct@gmail.com>
Sun, 9 Sep 2007 21:29:06 +0000 (17:29 -0400)
committerMatt McCutchen <hashproduct@gmail.com>
Sun, 9 Sep 2007 21:29:06 +0000 (17:29 -0400)
- Pure numbers are now exact by default.
- Fix mishandling of sig-figs for negative numbers.
- Add MROOTINT, MEXP, and MLN functions.
- Add a sixth basic unit (mol) and some more derived units.
- Add a minimal README.
- Add a demo spreadsheet.

README [new file with mode: 0644]
measurements-demo.ods [new file with mode: 0644]
src/net/mattmccutchen/measurements/Measurement.java
src/net/mattmccutchen/measurements/MeasurementMath.java
src/net/mattmccutchen/measurements/MeasurementsAddIn.java
src/net/mattmccutchen/measurements/Unit.java
src/net/mattmccutchen/measurements/XMeasurementsAddIn.idl
test/net/mattmccutchen/measurements/TestMeasurement.java

diff --git a/README b/README
new file mode 100644 (file)
index 0000000..5d2e723
--- /dev/null
+++ b/README
@@ -0,0 +1,12 @@
+This is Measurements, an OpenOffice.org Calc add-in that provides
+functions for tracking units of measure and significant figures.
+
+See: http://mattmccutchen.net/measurements/
+
+Legal
+-----
+I, Matt McCutchen, the sole author of the original Measurements add-in, waive my
+copyright to it, placing it in the public domain.  The add-in comes with
+absolutely no warranty.
+
+-- Matt McCutchen <matt@mattmccutchen.net>
diff --git a/measurements-demo.ods b/measurements-demo.ods
new file mode 100644 (file)
index 0000000..165e3c8
Binary files /dev/null and b/measurements-demo.ods differ
index 1ddaa50..0e88986 100644 (file)
@@ -23,9 +23,11 @@ public class Measurement {
                        int space = str.indexOf(' ');
                        String numPart;
                        String unitPart;
+                       boolean forceExact = false;
                        if (space == -1) {
                                numPart = str;
                                unitPart = "";
+                               forceExact = true;
                        } else {
                                numPart = str.substring(0, space);
                                unitPart = str.substring(space + 1);
@@ -33,11 +35,13 @@ public class Measurement {
                        double num;
                        double unc;
                        if (numPart.charAt(numPart.length() - 1) == exactIndicator) {
+                               forceExact = true;
                                numPart = numPart.substring(0, numPart.length() - 1);
+                       }
+                       num = Double.parseDouble(numPart);
+                       if (forceExact)
                                unc = 0;
-                               num = Double.parseDouble(numPart);
-                       } else {
-                               num = Double.parseDouble(numPart);
+                       else {
                                // Determine sig figs
                                int echar = numPart.indexOf('e');
                                if (echar == -1) {
@@ -113,7 +117,7 @@ public class Measurement {
        }
        
        private static double expOf(double d) {
-               return (d == 0) ? 0 : Math.floor(Math.log10(d));
+               return (d == 0) ? 0 : Math.floor(Math.log10(Math.abs(d)));
        }
        
        private static int sigFigsOf(double num, double unc) {
index 5ea44ab..273365b 100644 (file)
@@ -71,6 +71,20 @@ public class MeasurementMath {
                        up);
        }
        
+       public static Measurement rootint(Measurement a, int b) {
+               if (a == null)
+                       return null;
+               int[] up = new int[Unit.basicUnits.length];
+               for (int i = 0; i < Unit.basicUnits.length; i++) {
+                       if (Math.abs(a.unitPowers[i]) % b != 0)
+                               return null;
+                       up[i] = a.unitPowers[i] / b;
+               }
+               return new Measurement(Math.pow(a.number, 1.0/b),
+                       a.uncertainty / b * Math.pow(a.number, (1.0/b)-1),
+                       up);
+       }
+       
        public static Measurement pow(Measurement a, Measurement b) {
                if (a == null || !isPureNumber(a) || b == null || !isPureNumber(b))
                        return null;
@@ -81,6 +95,22 @@ public class MeasurementMath {
                        Measurement.pureNumberUnitPowers);
        }
        
+       public static Measurement exp(Measurement m) {
+               if (m == null || !isPureNumber(m))
+                       return null;
+               return new Measurement(Math.exp(m.number),
+                       m.uncertainty * Math.exp(m.number),
+                       Measurement.pureNumberUnitPowers);
+       }
+       
+       public static Measurement ln(Measurement m) {
+               if (m == null || !isPureNumber(m))
+                       return null;
+               return new Measurement(Math.log(m.number),
+                       m.uncertainty / m.number,
+                       Measurement.pureNumberUnitPowers);
+       }
+       
        public static double cmp(Measurement a, Measurement b) {
                if (a == null || b == null || !unitsSame(a, b))
                        return Double.NaN;
index 5f56dec..0c02f46 100644 (file)
@@ -42,9 +42,17 @@ public class MeasurementsAddIn extends AddInBase<AddInHelper>
                        new FunctionInfo("mpowint", "mpowint", "Raises a measurement to an integer power.",
                                Arrays.asList(new ArgumentInfo("base", "Base"),
                                        new ArgumentInfo("exp", "Exponent"))),
+                       new FunctionInfo("mrootint", "mrootint", "Takes an integer square root of a measurement.  Does not allow fractional powers of units in the result.",
+                               Arrays.asList(new ArgumentInfo("base", "Base"),
+                                       new ArgumentInfo("exp", "Exponent"))),
                        new FunctionInfo("mpow", "mpow", "Raises one measurement to the power of another.  Both must be pure numbers.",
                                Arrays.asList(new ArgumentInfo("base", "Base"),
                                        new ArgumentInfo("exp", "Exponent"))),
+                       new FunctionInfo("mexp", "mexp", "Raises e (2.718...) to the power of a measurement.",
+                               Arrays.asList(new ArgumentInfo("m", "Measurement"))),
+                       new FunctionInfo("mln", "mln", "Takes the natural logarithm of a measurement.  " +
+                               "The measurement must be a pure number, so you may have to rewrite a difference of logarithms as a logarithm of a quotient.",
+                               Arrays.asList(new ArgumentInfo("m", "Measurement"))),
                        new FunctionInfo("mcmp", "mcmp",
                                "Returns the difference between two measurements, expressed in units of the sum of their uncertainties.  " +
                                "You can compare measurements very flexibly by checking the result against a tolerance.",
@@ -100,10 +108,23 @@ public class MeasurementsAddIn extends AddInBase<AddInHelper>
                return Measurement.format(MeasurementMath.powint(
                        Measurement.parseCode(a), b), true);
        }
+       public String mrootint(String a, String bstr) {
+               int b = Integer.parseInt(bstr);
+               return Measurement.format(MeasurementMath.rootint(
+                       Measurement.parseCode(a), b), true);
+       }
        public String mpow(String a, String b) {
                return Measurement.format(MeasurementMath.pow(
                        Measurement.parseCode(a), Measurement.parseCode(b)), true);
        }
+       public String mexp(String m) {
+               return Measurement.format(MeasurementMath.exp(
+                       Measurement.parseCode(m)), true);
+       }
+       public String mln(String m) {
+               return Measurement.format(MeasurementMath.ln(
+                       Measurement.parseCode(m)), true);
+       }
        public double mcmp(String a, String b) {
                return MeasurementMath.cmp(
                        Measurement.parseCode(a), Measurement.parseCode(b));
index 25d33ac..4becce2 100644 (file)
@@ -3,21 +3,28 @@ package net.mattmccutchen.measurements;
 import java.util.*;
 
 public class Unit {
-       public static final Unit SECOND  = new Unit("s"   , 1.0, 1, 0, 0, 0, 0);
-       public static final Unit METER   = new Unit("m"   , 1.0, 0, 1, 0, 0, 0);
-       public static final Unit GRAM    = new Unit("g"   , 1.0, 0, 0, 1, 0, 0);
-       public static final Unit COULOMB = new Unit("coul", 1.0, 0, 0, 0, 1, 0);
-       public static final Unit KELVIN  = new Unit("K"   , 1.0, 0, 0, 0, 0, 1);
+       public static final Unit SECOND  = new Unit("s"   , 1.0, 1, 0, 0, 0, 0, 0);
+       public static final Unit METER   = new Unit("m"   , 1.0, 0, 1, 0, 0, 0, 0);
+       public static final Unit GRAM    = new Unit("g"   , 1.0, 0, 0, 1, 0, 0, 0);
+       public static final Unit COULOMB = new Unit("coul", 1.0, 0, 0, 0, 1, 0, 0);
+       public static final Unit KELVIN  = new Unit("K"   , 1.0, 0, 0, 0, 0, 1, 0);
+       public static final Unit MOLE    = new Unit("mol" , 1.0, 0, 0, 0, 0, 0, 1);
        
        public static final Unit[] basicUnits = new Unit[] {
-               SECOND, METER, GRAM, COULOMB, KELVIN,
+               SECOND, METER, GRAM, COULOMB, KELVIN, MOLE,
        }; // Don't mutate
        
-       public static final Unit LITER = new Unit("L", 1e-3, 0, 3, 0, 0, 0);
-       public static final Unit PERCENT = new Unit("%", 1e-2, 0, 0, 0, 0, 0);
+       public static final Unit PERCENT    = new Unit("%", 1e-2, 0, 0, 0, 0, 0, 0);
+       public static final Unit NEWTON     = new Unit("N", 1e+3,-2, 1, 1, 0, 0, 0);
+       public static final Unit JOULE      = new Unit("J", 1e+3,-2, 2, 1, 0, 0, 0);
+       public static final Unit WATT       = new Unit("W", 1e+3,-3, 2, 1, 0, 0, 0);
+       public static final Unit VOLT       = new Unit("V", 1e+3,-2, 2, 1,-1, 0, 0);
+       public static final Unit AMP        = new Unit("A", 1.0 ,-1, 0, 0, 1, 0, 0);
+       public static final Unit LITER      = new Unit("L", 1e-3, 0, 3, 0, 0, 0, 0);
        
        public static final Unit[] allUnits = new Unit[] {
-               PERCENT, SECOND, METER, GRAM, COULOMB, KELVIN, LITER,
+               SECOND, METER, GRAM, COULOMB, KELVIN, MOLE,
+               PERCENT, NEWTON, JOULE, WATT, VOLT, AMP, LITER,
        }; // Don't mutate
        
        public final String symbol;
@@ -60,6 +67,7 @@ public class Unit {
                prefixes.put('E', 1e+18);
                prefixes.put('Z', 1e+21);
                prefixes.put('Y', 1e+24);
+               prefixes.put('c', 1e-02);
                prefixes.put('m', 1e-03);
                prefixes.put('u', 1e-06); // micro -> u: oh well
                prefixes.put('n', 1e-09);
index 0a6bc80..fcb6904 100644 (file)
@@ -14,7 +14,10 @@ module net {
                                string mmul([in] string a, [in] string b);
                                string mdiv([in] string a, [in] string b);
                                string mpowint([in] string a, [in] string b);
+                               string mrootint([in] string a, [in] string b);
                                string mpow([in] string a, [in] string b);
+                               string mexp([in] string a);
+                               string mln([in] string a);
                                double mcmp([in] string a, [in] string b);
                                
                                string mcleanstr([in] string m);
index 3745dde..4f120a2 100644 (file)
@@ -23,5 +23,6 @@ public class TestMeasurement {
                System.out.println(ma.mmul("50.00 mL", "1.000 mg mL^-1"));
                System.out.println(ma.mstras("50.00 mL", "L"));
                System.out.println(ma.mstras("50.00 mL", "YL"));
+               System.out.println(ma.mln(".365 "));
        }
 }