-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathclass.js
More file actions
198 lines (198 loc) · 11.6 KB
/
Copy pathclass.js
File metadata and controls
198 lines (198 loc) · 11.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
"use strict";
class UncertainNumber {
//****************************************************************
// GETTERS & METHODS
//****************************************************************
// this.absoluteErrorStr
// format absoluteError up to decimal place as x. Report as exponential. This is its own separate function because this functionality is invoked within .toString and .summary.
get absoluteErrorStr() {
// Round absoluteError to the same placeValue as x.
// round absoluteError to nearest 10^n = 10^n * round(absoluteError * 10^-n)
const powerOf10 = Math.pow(10, this.placeValue);
const inverse = Math.pow(10, -this.placeValue); // .pow seems more accurate than 1 / powerOf10
return (powerOf10 * Math.round(this.absoluteError * inverse)).toString();
}
// this.toString({reportAbsolute = <#false#>})
// Write the number and its uncertainty with the correct amount of accuracy. This is how numbers should be written in science.
toString({ reportAbsolute = false }) {
// Write in exponential form, like "1.23e-2". The number of sigFigs is one more than the number of decimal places, so the number of decimal places is one less than the intended number of sigFigs. If the uncertainty is relative, report to 3 sigFigs.
return this.x.toExponential(this.sigFigs - 1)
+ " ± "
+ (reportAbsolute ? this.absoluteErrorStr : ((this.percentError * 100).toPrecision(3) + "%"));
}
// this.summary
// A summary of this number's accuracy, shown with 1 extra digit, in HTML.
get summary() {
/*Example:
To place value 10^-11.
3 significant figures.
Extra digits:
6.673e-11
Absolute uncertainty: ± 1.00e-14
Relative uncertainty: ± 1.499e-2%
*/
const part1 = `• To place value 10^${this.placeValue}.<br>${this.sigFigs} significant figures.<br>`;
// So that absoluteErrorStr generates with 1 extra digit, artificially increment placeValue, temporarily.
this.placeValue += 1;
const part2 = `Extra digits:<br>${this.x.toExponential(this.sigFigs)}<br>Absolute uncertainty: ± ${this.absoluteErrorStr}<br>Relative uncertainty: ± ${this.percentError.toPrecision(4)}<br>`;
this.placeValue -= 1;
return part1 + part2;
}
//****************************************************************
// CONSTRUCTORS
//****************************************************************
// The raw, basic, memberwise constructor.
/*
new this({
x: <#number#>,
placeValue: <#number#>,
absoluteError: <#number#>,
sigFigs: <#number#>,
percentError: <#number#>
})
*/
constructor({ x = 0, placeValue = -Infinity, absoluteError = 0, sigFigs = 100, percentError = 0 }) {
this.x = x;
this.placeValue = placeValue;
this.absoluteError = absoluteError;
this.sigFigs = sigFigs;
this.percentError = percentError;
console.log("new UncertainNumber: " + this.toString({}));
}
// Construct with a known place value and absolute error.
// this.newFromAbsolute({x: <#0#>, placeValue: <#-Infinity#>, absoluteError: <#0#>})
static newFromAbsolute({ x = 0, placeValue = -Infinity, absoluteError = 0 }) {
/* Calculate the sigFigs count of x when written up to placeValue.
x | placeValue | sigFigs
1 | 0 | 1
20. | 0 | 2
20 | 1 | 1
10 | 2 | nonsensical (0 sig figs)
0.5 | -1 | 1
sigFigs = floor log10(|x|) - placeValue + 1
*/
return new this({
x: x,
placeValue: placeValue,
absoluteError: absoluteError,
sigFigs: Math.floor(Math.log10(Math.abs(x))) - placeValue + 1,
percentError: absoluteError / Math.abs(x) // dx/x = dx / x
});
}
// Construct with a known sig fig count and relative error.
// this.newFromPercent({x: <#0#>, sigFigs: <#100#>, percentError: <#0#>})
static newFromPercent({ x = 0, sigFigs = 100, percentError = 0 }) {
/* Write x to the correct sigFig count, and check what the place value is.
x | sigFigs | placeValue
1 | 1 | 0
11 | 2 | 0
1.1 | 2 | -1
100. | 3 | 0
5.0e2 | 2 | 1
placeValue = place value of 1st sig fig - s + 1
place value of 1st sig fig = floor log |x|
*/
// Note that Math.log10(0) = -Infinity, but that's OK.
return new this({
x: x,
placeValue: Math.floor(Math.log10(Math.abs(x))) - sigFigs + 1,
absoluteError: Math.abs(x) * percentError, // dx = x * dx/x
sigFigs: sigFigs,
percentError: percentError
});
}
// Construct from strings.
// this.New({x: <#string#>, error: <#string#>, isAbsolute: <#boolean#>})
static New({ x, error, isAbsolute }) {
// Calculate the placeValue. Scan forward to determine exponent if there is one. WHen scanning, say you've seen "-330", so the placeValue seems to be 1. Suddenly its "-330.", then "-330.1". now placeValue is -1. Then any further decimals will decrement placeValue.
const digit = /\d/; // .test(string) == true only if string is a digit 0-9.
let isInt = 0; // Trick to avoid more if statements: true = 0, false = -1
let placeValue = 0; // The placeValue, assuming x is an integer.
let breakLoopFlag = false;
for (let i = 0; i < x.length; i += 1) { // Scan each character from the start.
if (breakLoopFlag) {
break;
} // quit loop
const char = x[i];
if (digit.test(char)) {
// If no decimal has been encountered, just add 0. If we are in decimal territory, subtract 1.
placeValue += isInt;
continue; // No further action needed, so to avoid comparing char again, just skip ahead.
}
// if (char == ".") {
// isInt = -1;
// continue;
// }
// if (char == "E" || char == "e") {
// placeValue += parseInt(x.substring(i + 1));
// break;
// }
switch (char) {
case ".":
isInt = -1; // If there are decimal places, it's not an integer.
continue; // Scan next character, restart loop
// If there's a power of 10, determine the exponent and offset the place value by the integer after this e/E, & stop scanning. There woulc't be anything else after the exponent number.
case "e": // fallthrough. Check for e OR E.
case "E":
placeValue += parseInt(x.substring(i + 1));
// Cannot use 'break' because it would just break from this switch statement, not the loop.
breakLoopFlag = true;
}
}
// Varaibles are accessed multiple times below, so store them.
const xNum = parseFloat(x);
const errorNum = parseFloat(error);
return this.newFromAbsolute({
x: xNum,
placeValue: placeValue,
absoluteError: isAbsolute ? errorNum : errorNum * Math.abs(xNum)
}); // deal with error based on type
}
//****************************************************************
// MATH OPERATIONS. All logic is from IB Physics rules in data booklet.
//****************************************************************
// .plus(<#UncertainNumber#>)
plus(that) {
console.log("sum = ${this.x + that.x}");
return UncertainNumber.newFromAbsolute({
x: this.x + that.x,
placeValue: Math.max(this.placeValue, that.placeValue),
absoluteError: this.absoluteError + that.absoluteError
});
}
// .minus(<#UncertainNumber#>)
minus(that) {
return UncertainNumber.newFromAbsolute({
x: this.x - that.x,
placeValue: Math.max(this.placeValue, that.placeValue),
absoluteError: this.absoluteError + that.absoluteError
});
}
// .product(<#UncertainNumber#>)
times(that) {
return UncertainNumber.newFromPercent({
x: this.x * that.x,
sigFigs: Math.min(this.sigFigs, that.sigFigs),
percentError: this.percentError + that.percentError
});
}
// .divideBy(<#UncertainNumber#>)
divideBy(that) {
return UncertainNumber.newFromPercent({
x: this.x / that.x,
sigFigs: Math.min(this.sigFigs, that.sigFigs),
percentError: this.percentError + that.percentError
});
}
// .raiseTo(<#number#>)
raiseTo(that) {
return UncertainNumber.newFromPercent({
x: Math.pow(this.x, that.x),
sigFigs: this.sigFigs,
percentError: Math.abs(this.percentError * that.x)
});
}
}