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;
                        int space = str.indexOf(' ');
                        String numPart;
                        String unitPart;
+                       boolean forceExact = false;
                        if (space == -1) {
                                numPart = str;
                                unitPart = "";
                        if (space == -1) {
                                numPart = str;
                                unitPart = "";
+                               forceExact = true;
                        } else {
                                numPart = str.substring(0, space);
                                unitPart = str.substring(space + 1);
                        } 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) {
                        double num;
                        double unc;
                        if (numPart.charAt(numPart.length() - 1) == exactIndicator) {
+                               forceExact = true;
                                numPart = numPart.substring(0, numPart.length() - 1);
                                numPart = numPart.substring(0, numPart.length() - 1);
+                       }
+                       num = Double.parseDouble(numPart);
+                       if (forceExact)
                                unc = 0;
                                unc = 0;
-                               num = Double.parseDouble(numPart);
-                       } else {
-                               num = Double.parseDouble(numPart);
+                       else {
                                // Determine sig figs
                                int echar = numPart.indexOf('e');
                                if (echar == -1) {
                                // Determine sig figs
                                int echar = numPart.indexOf('e');
                                if (echar == -1) {
@@ -113,7 +117,7 @@ public class Measurement {
        }
        
        private static double expOf(double d) {
        }
        
        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) {
        }
        
        private static int sigFigsOf(double num, double unc) {
index 5ea44ab..273365b 100644 (file)
@@ -71,6 +71,20 @@ public class MeasurementMath {
                        up);
        }
        
                        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;
        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);
        }
        
                        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;
        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("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("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.",
                        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);
        }
                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 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));
        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 {
 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[] {
        
        public static final Unit[] basicUnits = new Unit[] {
-               SECOND, METER, GRAM, COULOMB, KELVIN,
+               SECOND, METER, GRAM, COULOMB, KELVIN, MOLE,
        }; // Don't mutate
        
        }; // 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[] {
        
        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;
        }; // 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('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);
                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 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 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);
                                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.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 "));
        }
 }
        }
 }