1 package net.mattmccutchen.measurements;
5 public class Measurement {
6 public static final int dataLen = 2 * 8 + Unit.basicUnits.length * 1;
7 public static final String errorIndicator = "ERROR";
8 public static final char exactIndicator = 'x';
9 public static final int[] pureNumberUnitPowers = new int[Unit.basicUnits.length];
11 public final double number;
12 public final double uncertainty;
13 public final int[] unitPowers; // Don't mutate
15 public Measurement(double number, double uncertainty, int... unitPowers) {
17 this.uncertainty = uncertainty;
18 this.unitPowers = unitPowers;
21 public static Measurement parse(String str) {
23 int space = str.indexOf(' ');
26 boolean forceExact = false;
32 numPart = str.substring(0, space);
33 unitPart = str.substring(space + 1);
37 if (numPart.charAt(numPart.length() - 1) == exactIndicator) {
39 numPart = numPart.substring(0, numPart.length() - 1);
41 num = Double.parseDouble(numPart);
46 int echar = numPart.indexOf('e');
48 echar = numPart.indexOf('E');
50 echar = numPart.length();
52 int decpt = numPart.indexOf('.');
55 // Everything but trailing zeros. Yuck.
57 while (i > 1 /* 0 gets one sig fig */
58 && numPart.charAt(i - 1) == '0')
62 sigFigs = echar - 1; // Everything except the decimal point
63 unc = Math.pow(10.0, expOf(num) - sigFigs);
66 Unit theUnit = Unit.parseUnitString(unitPart);
67 num *= theUnit.factor;
68 unc *= theUnit.factor;
69 return new Measurement(num, unc, theUnit.powers);
70 } catch (Exception e) {
75 public static Measurement parseCode(String str) {
76 if (str.indexOf(errorIndicator) != -1)
78 // Does it have a code?
79 int lbrack = str.indexOf('[');
81 int rbrack = str.indexOf(']', lbrack + 1);
82 String code = str.substring(lbrack + 1, rbrack);
83 byte[] data = Base64.decode(code, Base64.NO_GZIP_MAGIC);
84 return fromData(data);
89 public static Measurement fromData(byte[] data) {
91 System.err.print("Loading data: ");
92 for (int i = 0; i < data.length; i++)
93 System.err.print(" " + data[i]);
96 double num = BitFlicking.loadDouble(data, 0);
97 double unc = BitFlicking.loadDouble(data, 8);
98 int[] up = new int[Unit.basicUnits.length];
99 for (int i = 0; i < up.length; i++)
101 return new Measurement(num, unc, up);
104 public byte[] toData() {
105 byte[] data = new byte[dataLen];
106 BitFlicking.storeDouble(data, number, 0);
107 BitFlicking.storeDouble(data, uncertainty, 8);
108 for (int i = 0; i < unitPowers.length; i++)
109 data[16+i] = (byte) unitPowers[i];
111 System.err.print("Storing data: ");
112 for (int i = 0; i < data.length; i++)
113 System.err.print(" " + data[i]);
114 System.err.println();
119 private static double expOf(double d) {
120 return (d == 0) ? 0 : Math.floor(Math.log10(Math.abs(d)));
123 private static int sigFigsOf(double num, double unc) {
125 return Integer.MAX_VALUE;
126 //System.err.println("number " + number + ", unc " + uncertainty);
127 int sf = (int) Math.round(expOf(num) - Math.log10(unc));
128 //System.err.println("sigFigs: " + sf);
132 private static DecimalFormat formatForSigFigs(int sigFigs) {
133 StringBuilder fmtString = new StringBuilder("0.");
134 for (int i = 0; i < sigFigs-1; i++)
135 fmtString.append('0');
136 fmtString.append("E0");
137 //System.err.println("fmtString is " + fmtString);
138 return new DecimalFormat(fmtString.toString());
141 private static final DecimalFormat exactFormat
142 = new DecimalFormat("0.##########E0");
144 private static void formatNum(double num, double unc, StringBuilder sb) {
145 int sigFigs = sigFigsOf(num, unc);
146 if (num == 0 && unc == 0)
147 sb.append("0" + exactIndicator);
148 else if (sigFigs > 10 && num != 0) {
149 // Put some reasonable number of digits and mark exact
150 sb.append(exactFormat.format(num));
151 sb.append(exactIndicator);
152 } else if (num == 0) {
153 // Handle zero specially
155 sb.append(-(sigFigs - 1));
156 } else if (sigFigs >= 1)
157 sb.append(formatForSigFigs(sigFigs).format(num));
159 sb.append("insignificant");
162 public static String format(Measurement m, boolean withCode) {
164 return errorIndicator;
165 StringBuilder sb = new StringBuilder();
166 formatNum(m.number, m.uncertainty, sb);
169 for (int i = 0; i < m.unitPowers.length; i++)
170 if (m.unitPowers[i] != 0) {
172 sb.append(Unit.basicUnits[i].symbol);
173 if (m.unitPowers[i] != 1) {
175 sb.append(Integer.toString(m.unitPowers[i]));
181 sb.append(Base64.encodeBytes(m.toData(), Base64.DONT_BREAK_LINES));
184 return sb.toString();
187 public static String formatInUnit(Measurement m, Unit u, boolean withCode) {
189 return errorIndicator;
190 for (int i = 0; i < Unit.basicUnits.length; i++)
191 if (m.unitPowers[i] != u.powers[i])
192 return errorIndicator;
193 StringBuilder sb = new StringBuilder();
194 formatNum(m.number / u.factor, m.uncertainty / u.factor, sb);
200 sb.append(Base64.encodeBytes(m.toData(), Base64.DONT_BREAK_LINES));
203 return sb.toString();