Skip to content

Commit bf4b205

Browse files
committed
Addition and subtraction use correct result precision
1 parent 8e763c3 commit bf4b205

File tree

7 files changed

+538
-108
lines changed

7 files changed

+538
-108
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ types you need to work with from the rest of your code.
1414

1515
### Changed
1616

17+
- Addition and subtraction use correct result precision
1718
- Update toString methods for a few measurement types
1819
- Write Good suggestions for README
1920

lib/src/measurement.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,11 +90,11 @@ abstract class Measurement<T extends Measurement<T>> implements Comparable<T> {
9090

9191
/// Returns a measurement equivalent to the sum of two others.
9292
T operator +(final T other) =>
93-
_construct(si + other.si, Precision.add(_precision, other._precision));
93+
_construct(si + other.si, Precision.addition(this, other));
9494

9595
/// Returns a measurement equivalent to the difference between two others.
9696
T operator -(final T other) =>
97-
_construct(si - other.si, Precision.add(_precision, other._precision));
97+
_construct(si - other.si, Precision.addition(this, -other));
9898

9999
/// Returns a measurement equivalent to a multiple of this.
100100
T operator *(final double multiplier) =>

lib/src/precision.dart

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,57 @@ class Precision {
4949
Precision.combine(final Precision a, final Precision b)
5050
: this(math.min(a.precision, b.precision));
5151

52+
@Deprecated("Use 'addition()' instead")
53+
Precision.add(final Precision a, final Precision b) : this._max();
54+
5255
/// Combines two [Precision]s per the "addition rule".
5356
///
5457
/// Use this when adding or subtracting two numbers to determine the correct
5558
/// output precision. See [Wikipedia on Precision Arithmetic](
5659
/// https://en.wikipedia.org/wiki/Significant_figures#Arithmetic)
5760
/// for details.
61+
static Precision addition<T extends Measurement<T>>(
62+
final Measurement<T> a, final Measurement<T> b) {
63+
if (a.isInfinite || b.isInfinite) {
64+
return Precision._max();
65+
}
66+
final precisionA = digitsAfterDecimal(a);
67+
final precisionB = digitsAfterDecimal(b);
68+
final beforeDecimal = digitsBeforeDecimal(a._preciseSI() + b._preciseSI());
69+
return Precision(beforeDecimal + math.min(precisionA, precisionB));
70+
}
71+
72+
/// Calculates the number of significant digits after the decimal point.
5873
///
59-
/// Note that currently the result will always have maximum precision (for the
60-
/// time being - future versions will fix this).
61-
Precision.add(final Precision a, final Precision b) : this._max();
74+
/// This only considers digits that fall within the measurement's precision,
75+
/// e.g.:
76+
///
77+
/// ```dart
78+
/// digitsAfterDecimal(meters(1.2345)); // 4
79+
/// digitsAfterDecimal(meters(1.2345, precision: Precision(3))); // 2
80+
/// ```
81+
static int digitsAfterDecimal(final Measurement measurement) {
82+
if (measurement.isInfinite || measurement.isNaN) {
83+
return 0;
84+
}
85+
final string = measurement._preciseSI().toStringAsExponential();
86+
final locationOfE = string.indexOf('e');
87+
return math.max(
88+
measurement.precision -
89+
int.parse(string.substring(locationOfE + 1)) -
90+
1,
91+
0);
92+
}
93+
94+
/// Calculates the number of digits (significant or not) before the decimal.
95+
static int digitsBeforeDecimal(final double number) {
96+
if (number.isInfinite || number.isNaN) {
97+
return Precision.max.precision;
98+
}
99+
final string = number.toStringAsExponential();
100+
return math.max(
101+
int.parse(string.substring(string.indexOf('e') + 1)) + 1, 0);
102+
}
62103

63104
/// Interprets the specified number according to this Precision.
64105
double withPrecision(final double value) =>

lib/src/temperature.dart

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ class Temperature implements Comparable<Temperature> {
7474
/// Returns `true` if this is infinite.
7575
bool get isInfinite => _kelvin.isInfinite;
7676

77+
/// Returns the digits of precision this measurement has.
78+
int get precision => _precision.precision;
79+
7780
@override
7881
bool operator ==(final dynamic other) =>
7982
other is Temperature &&
@@ -108,7 +111,8 @@ class Temperature implements Comparable<Temperature> {
108111
/// ```
109112
Temperature operator +(final TemperatureChange change) =>
110113
Temperature.ofKelvin(_kelvin + change.si,
111-
precision: Precision.add(_precision, change._precision));
114+
precision: Precision.addition(
115+
kelvin(_kelvin, precision: _precision), change));
112116

113117
/// Creates a [Temperature] representing the application of a [Temperature]
114118
/// and the opposite of a [TemperatureChange].
@@ -120,15 +124,17 @@ class Temperature implements Comparable<Temperature> {
120124
/// ```
121125
Temperature operator -(final TemperatureChange change) =>
122126
Temperature.ofKelvin(_kelvin - change.si,
123-
precision: Precision.add(_precision, change._precision));
127+
precision: Precision.addition(
128+
kelvin(_kelvin, precision: _precision), -change));
124129

125130
/// Returns the difference between this and another [Temperature].
126131
///
127132
/// The resulting [TemperatureChange] will be negative if this is smaller than
128133
/// the other [Temperature].
129134
TemperatureChange difference(final Temperature other) =>
130135
kelvin(_kelvin - other._kelvin,
131-
precision: Precision.add(_precision, other._precision));
136+
precision: Precision.addition(kelvin(_kelvin, precision: _precision),
137+
kelvin(other._kelvin, precision: other._precision)));
132138

133139
@override
134140
String toString() => '$asKelvin K';

test/measurement_test.dart

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -883,16 +883,16 @@ void main() {
883883
// then
884884
expect(result.si, double.negativeInfinity);
885885
});
886-
test('precision is maxed', () {
886+
test('precision is set correctly', () {
887887
// given
888-
final unit1 = meters(2, precision: Precision(5));
889-
final unit2 = meters(3, precision: Precision(5));
888+
final unit1 = meters(97.101, precision: Precision(5));
889+
final unit2 = meters(3.456789, precision: Precision(8));
890890

891891
// when
892892
final result = unit1 + unit2;
893893

894894
// then
895-
expect(result.precision, Precision.max.precision);
895+
expect(result.precision, 6);
896896
});
897897
});
898898

@@ -952,16 +952,16 @@ void main() {
952952
// then
953953
expect(result.si, double.infinity);
954954
});
955-
test('precision is maxed', () {
955+
test('precision is set correctly', () {
956956
// given
957-
final unit1 = meters(2, precision: Precision(5));
958-
final unit2 = meters(3, precision: Precision(5));
957+
final unit1 = meters(3.45, precision: Precision(3));
958+
final unit2 = meters(3.21, precision: Precision(5));
959959

960960
// when
961961
final result = unit1 - unit2;
962962

963963
// then
964-
expect(result.precision, Precision.max.precision);
964+
expect(result.precision, 2);
965965
});
966966
});
967967

0 commit comments

Comments
 (0)