diff options
-rw-r--r-- | src/main/java/sevenUnits/utils/UncertainDouble.java | 108 | ||||
-rw-r--r-- | src/test/java/sevenUnits/unit/UnitTest.java | 72 | ||||
-rw-r--r-- | src/test/java/sevenUnits/utils/UncertainDoubleTest.java | 28 |
3 files changed, 104 insertions, 104 deletions
diff --git a/src/main/java/sevenUnits/utils/UncertainDouble.java b/src/main/java/sevenUnits/utils/UncertainDouble.java index 66d8103..1f7ab79 100644 --- a/src/main/java/sevenUnits/utils/UncertainDouble.java +++ b/src/main/java/sevenUnits/utils/UncertainDouble.java @@ -35,20 +35,20 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { * The exact value 0 */ public static final UncertainDouble ZERO = UncertainDouble.of(0, 0); - + static final String NUMBER_REGEX = "(\\d+(?:[\\.,]\\d+))"; - + /** * A regular expression that can recognize toString forms */ static final Pattern TO_STRING = Pattern.compile(NUMBER_REGEX - // optional "� [number]" - + "(?:\\s*(?:�|\\+-)\\s*" + NUMBER_REGEX + ")?"); - + // optional "± [number]" + + "(?:\\s*(?:±|\\+-)\\s*" + NUMBER_REGEX + ")?"); + /** * Gets an UncertainDouble from a double string. The uncertainty of the * double will be one of the lowest decimal place of the number. For example, - * "12345.678" will become 12345.678 � 0.001. + * "12345.678" will become 12345.678 ± 0.001. * * @throws NumberFormatException if the argument is not a number * @@ -59,13 +59,13 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { final double uncertainty = Math.pow(10, -value.scale()); return UncertainDouble.of(value.doubleValue(), uncertainty); } - + /** * Parses a string in the form of {@link UncertainDouble#toString(boolean)} * and returns the corresponding {@code UncertainDouble} instance. * <p> * This method allows some alternative forms of the string representation, - * such as using "+-" instead of "�". + * such as using "+-" instead of "±". * * @param s string to parse * @return {@code UncertainDouble} instance @@ -75,11 +75,11 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { public static final UncertainDouble fromString(String s) { Objects.requireNonNull(s, "s may not be null"); final Matcher matcher = TO_STRING.matcher(s); - + if (!matcher.matches()) throw new IllegalArgumentException( - "Could not parse stirng \"" + s + "\"."); - + "Could not parse string \"" + s + "\"."); + double value, uncertainty; try { value = Double.parseDouble(matcher.group(1)); @@ -87,7 +87,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { throw new IllegalArgumentException( "String " + s + " not in correct format."); } - + final String uncertaintyString = matcher.group(2); if (uncertaintyString == null) { uncertainty = 0; @@ -99,10 +99,10 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { "String " + s + " not in correct format."); } } - + return UncertainDouble.of(value, uncertainty); } - + /** * Gets an {@code UncertainDouble} from its value and <b>absolute</b> * uncertainty. @@ -112,7 +112,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { public static final UncertainDouble of(double value, double uncertainty) { return new UncertainDouble(value, uncertainty); } - + /** * Gets an {@code UncertainDouble} from its value and <b>relative</b> * uncertainty. @@ -123,11 +123,11 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { double relativeUncertainty) { return new UncertainDouble(value, value * relativeUncertainty); } - + private final double value; - + private final double uncertainty; - + /** * @param value * @param uncertainty @@ -138,7 +138,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { // uncertainty should only ever be positive this.uncertainty = Math.abs(uncertainty); } - + /** * Compares this {@code UncertainDouble} with another * {@code UncertainDouble}. @@ -156,7 +156,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { public final int compareTo(UncertainDouble o) { return Double.compare(this.value, o.value); } - + /** * Returns the quotient of {@code this} and {@code other}. * @@ -167,7 +167,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { return UncertainDouble.ofRelative(this.value / other.value, Math .hypot(this.relativeUncertainty(), other.relativeUncertainty())); } - + /** * Returns the quotient of {@code this} and the exact value {@code other}. * @@ -176,7 +176,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { public final UncertainDouble dividedByExact(double other) { return UncertainDouble.of(this.value / other, this.uncertainty / other); } - + @Override public final boolean equals(Object obj) { if (this == obj) @@ -190,7 +190,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { return false; return true; } - + /** * @param other another {@code UncertainDouble} * @return true iff this and {@code other} are within each other's @@ -202,7 +202,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { return Math.abs(this.value - other.value) <= Math.min(this.uncertainty, other.uncertainty); } - + /** * Gets the preferred scale for rounding a value for toString. * @@ -217,19 +217,19 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { // the value is rounded to the same number of decimal places as the // uncertainty. final BigDecimal bigUncertainty = BigDecimal.valueOf(this.uncertainty); - + // the scale that will give the uncertainty two decimal places final int twoDecimalPlacesScale = bigUncertainty.scale() - bigUncertainty.precision() + 2; final BigDecimal roundedUncertainty = bigUncertainty .setScale(twoDecimalPlacesScale, RoundingMode.HALF_EVEN); - + if (roundedUncertainty.unscaledValue().intValue() >= 20) return twoDecimalPlacesScale - 1; // one decimal place else return twoDecimalPlacesScale; } - + @Override public final int hashCode() { final int prime = 31; @@ -238,7 +238,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { result = prime * result + Double.hashCode(this.uncertainty); return result; } - + /** * @return true iff the value has no uncertainty * @@ -247,7 +247,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { public final boolean isExact() { return this.uncertainty == 0; } - + /** * Returns the difference of {@code this} and {@code other}. * @@ -258,7 +258,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { return UncertainDouble.of(this.value - other.value, Math.hypot(this.uncertainty, other.uncertainty)); } - + /** * Returns the difference of {@code this} and the exact value {@code other}. * @@ -267,7 +267,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { public final UncertainDouble minusExact(double other) { return UncertainDouble.of(this.value - other, this.uncertainty); } - + /** * Returns the sum of {@code this} and {@code other}. * @@ -278,7 +278,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { return UncertainDouble.of(this.value + other.value, Math.hypot(this.uncertainty, other.uncertainty)); } - + /** * Returns the sum of {@code this} and the exact value {@code other}. * @@ -287,7 +287,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { public final UncertainDouble plusExact(double other) { return UncertainDouble.of(this.value + other, this.uncertainty); } - + /** * @return relative uncertainty * @since 2020-09-07 @@ -295,7 +295,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { public final double relativeUncertainty() { return this.uncertainty / this.value; } - + /** * Returns the product of {@code this} and {@code other}. * @@ -306,7 +306,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { return UncertainDouble.ofRelative(this.value * other.value, Math .hypot(this.relativeUncertainty(), other.relativeUncertainty())); } - + /** * Returns the product of {@code this} and the exact value {@code other}. * @@ -315,7 +315,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { public final UncertainDouble timesExact(double other) { return UncertainDouble.of(this.value * other, this.uncertainty * other); } - + /** * Returns the result of {@code this} raised to the exponent {@code other}. * @@ -323,15 +323,15 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { */ public final UncertainDouble toExponent(UncertainDouble other) { Objects.requireNonNull(other, "other may not be null"); - + final double result = Math.pow(this.value, other.value); final double relativeUncertainty = Math.hypot( other.value * this.relativeUncertainty(), Math.log(this.value) * other.uncertainty); - + return UncertainDouble.ofRelative(result, relativeUncertainty); } - + /** * Returns the result of {@code this} raised the exact exponent * {@code other}. @@ -342,7 +342,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { return UncertainDouble.ofRelative(Math.pow(this.value, other), this.relativeUncertainty() * other); } - + /** * Returns a string representation of this {@code UncertainDouble}. * <p> @@ -365,12 +365,12 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { public final String toString() { return this.toString(!this.isExact(), RoundingMode.HALF_EVEN); } - + /** * Returns a string representation of this {@code UncertainDouble}. * <p> * If {@code showUncertainty} is true, the string will be of the form "VALUE - * � UNCERTAINTY", and if it is false the string will be of the form "VALUE" + * ± UNCERTAINTY", and if it is false the string will be of the form "VALUE" * <p> * VALUE represents a string representation of this {@code UncertainDouble}'s * value. If the uncertainty is non-zero, the string will be rounded to the @@ -385,11 +385,11 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { * * <pre> * UncertainDouble.of(3.27, 0.22).toString(false) = "3.3" - * UncertainDouble.of(3.27, 0.22).toString(true) = "3.3 � 0.2" + * UncertainDouble.of(3.27, 0.22).toString(true) = "3.3 ± 0.2" * UncertainDouble.of(3.27, 0.13).toString(false) = "3.27" - * UncertainDouble.of(3.27, 0.13).toString(true) = "3.27 � 0.13" + * UncertainDouble.of(3.27, 0.13).toString(true) = "3.27 ± 0.13" * UncertainDouble.of(-5.01, 0).toString(false) = "-5.01" - * UncertainDouble.of(-5.01, 0).toString(true) = "-5.01 � 0.0" + * UncertainDouble.of(-5.01, 0).toString(true) = "-5.01 ± 0.0" * </pre> * * @since 2020-09-07 @@ -397,31 +397,31 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { public final String toString(boolean showUncertainty, RoundingMode roundingMode) { String valueString, uncertaintyString; - + // generate the string representation of value and uncertainty if (this.isExact()) { uncertaintyString = "0.0"; valueString = Double.toString(this.value); - + } else { // round the value and uncertainty according to getDisplayScale() final BigDecimal bigValue = BigDecimal.valueOf(this.value); final BigDecimal bigUncertainty = BigDecimal.valueOf(this.uncertainty); - + final int displayScale = this.getDisplayScale(); final BigDecimal roundedUncertainty = bigUncertainty .setScale(displayScale, roundingMode); final BigDecimal roundedValue = bigValue.setScale(displayScale, roundingMode); - + valueString = roundedValue.toString(); uncertaintyString = roundedUncertainty.toString(); } - - // return "value" or "value � uncertainty" depending on showUncertainty - return valueString + (showUncertainty ? " � " + uncertaintyString : ""); + + // return "value" or "value ± uncertainty" depending on showUncertainty + return valueString + (showUncertainty ? " ± " + uncertaintyString : ""); } - + /** * @return absolute uncertainty * @since 2020-09-07 @@ -429,7 +429,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { public final double uncertainty() { return this.uncertainty; } - + /** * @return value without uncertainty * @since 2020-09-07 diff --git a/src/test/java/sevenUnits/unit/UnitTest.java b/src/test/java/sevenUnits/unit/UnitTest.java index c93043b..4fb26a3 100644 --- a/src/test/java/sevenUnits/unit/UnitTest.java +++ b/src/test/java/sevenUnits/unit/UnitTest.java @@ -42,17 +42,17 @@ import sevenUnits.utils.UncertainDouble; class UnitTest { /** A random number generator */ private static final Random rng = ThreadLocalRandom.current(); - + @Test public void testAdditionAndSubtraction() { final LinearUnit inch = Metric.METRE.times(0.0254) .withName(NameSymbol.of("inch", "in")); final LinearUnit foot = Metric.METRE.times(0.3048) .withName(NameSymbol.of("foot", "ft")); - + assertEquals(inch.plus(foot), Metric.METRE.times(0.3302)); assertEquals(foot.minus(inch), Metric.METRE.times(0.2794)); - + // test with LinearUnitValue final LinearUnitValue value1 = LinearUnitValue.getExact(Metric.METRE, 15); final LinearUnitValue value2 = LinearUnitValue.getExact(foot, 120); @@ -60,70 +60,70 @@ class UnitTest { 0.5); final LinearUnitValue value4 = LinearUnitValue.getExact(Metric.KILOGRAM, 60); - + // make sure addition is done correctly assertEquals(51.576, value1.plus(value2).getValueExact(), 0.001); assertEquals(15.5, value1.plus(value3).getValueExact()); assertEquals(52.076, value1.plus(value2).plus(value3).getValueExact(), 0.001); - + // make sure addition uses the correct unit, and is still associative // (ignoring floating-point rounding errors) assertEquals(Metric.METRE, value1.plus(value2).getUnit()); assertEquals(Metric.METRE, value1.plus(value2).plus(value3).getUnit()); assertEquals(foot, value2.plus(value1).getUnit()); assertTrue(value1.plus(value2).equals(value2.plus(value1), true)); - + // make sure errors happen when they should assertThrows(IllegalArgumentException.class, () -> value1.plus(value4)); assertThrows(IllegalArgumentException.class, () -> value1.minus(value4)); } - + @Test public void testConversion() { final LinearUnit metre = Metric.METRE; final Unit inch = metre.times(0.0254); - + final UnitValue value = UnitValue.of(inch, 75); - + assertEquals(1.9, inch.convertTo(metre, 75), 0.01); assertEquals(1.9, value.convertTo(metre).getValue(), 0.01); - + // try random stuff for (int i = 0; i < 1000; i++) { // initiate random values final double conversionFactor = UnitTest.rng.nextDouble() * 1000000; final double testValue = UnitTest.rng.nextDouble() * 1000000; final double expected = testValue * conversionFactor; - + // test final Unit unit = Metric.METRE.times(conversionFactor); final double actual = unit.convertToBase(testValue); - + assertEquals(actual, expected, expected * DecimalComparison.DOUBLE_EPSILON); } } - + @Test public void testEquals() { final LinearUnit metre = Metric.METRE; final Unit meter = Metric.BaseUnits.METRE.asLinearUnit(); - + assertEquals(metre, meter); } - + @Test public void testIsMetric() { final Unit metre = Metric.METRE; final Unit megasecond = Metric.SECOND.withPrefix(Metric.MEGA); final Unit hour = Metric.HOUR; - + assertTrue(metre.isMetric()); assertTrue(megasecond.isMetric()); assertFalse(hour.isMetric()); } - + @Test public void testMultiplicationAndDivision() { // test unit-times-unit multiplication @@ -131,29 +131,29 @@ class UnitTest { .times(Metric.METRE.toExponent(2)) .dividedBy(Metric.SECOND.toExponent(2)); final LinearUnit actualJoule = Metric.JOULE; - + assertEquals(generatedJoule, actualJoule); - + // test multiplication by conversion factors final LinearUnit kilometre = Metric.METRE.times(1000); final LinearUnit hour = Metric.SECOND.times(3600); final LinearUnit generatedKPH = kilometre.dividedBy(hour); - + final LinearUnit actualKPH = Metric.METRE.dividedBy(Metric.SECOND) .dividedBy(3.6); - + assertEquals(generatedKPH, actualKPH); } - + @Test public void testPrefixes() { final LinearUnit generatedKilometre = Metric.METRE .withPrefix(Metric.KILO); final LinearUnit actualKilometre = Metric.METRE.times(1000); - + assertEquals(generatedKilometre, actualKilometre); } - + /** * Tests converting an uncertain LinearUnitValue to a string. * @@ -163,13 +163,13 @@ class UnitTest { public void testValueToString1() { final LinearUnitValue value = LinearUnitValue.of(Metric.METRE, UncertainDouble.of(10, 0.24)); - - assertEquals("(10.0 � 0.2) m", value.toString()); - assertEquals("(10.0 � 0.2) m", + + assertEquals("(10.0 ± 0.2) m", value.toString()); + assertEquals("(10.0 ± 0.2) m", value.toString(true, RoundingMode.HALF_EVEN)); assertEquals("10.0 m", value.toString(false, RoundingMode.HALF_EVEN)); } - + /** * Tests converting a certain LinearUnitValue to a string. * @@ -179,13 +179,13 @@ class UnitTest { public void testValueToString2() { final LinearUnitValue value = LinearUnitValue.of(Metric.METRE, UncertainDouble.of(10, 0)); - + assertEquals("10.0 m", value.toString()); - assertEquals("(10.0 � 0.0) m", + assertEquals("(10.0 ± 0.0) m", value.toString(true, RoundingMode.HALF_EVEN)); assertEquals("10.0 m", value.toString(false, RoundingMode.HALF_EVEN)); } - + /** * Tests converting an unnamed LinearUnitValue to a string. * @@ -196,11 +196,11 @@ class UnitTest { final LinearUnitValue value = LinearUnitValue.of( Metric.METRE.withName(NameSymbol.EMPTY), UncertainDouble.of(10, 0.24)); - + assertEquals("10.0 unnamed unit (= 10.0 m)", value.toString(false, RoundingMode.HALF_EVEN)); } - + /** * Tests converting a named UnitValue to a string. * @@ -209,10 +209,10 @@ class UnitTest { @Test public void testValueToString4() { final UnitValue value = UnitValue.of(BritishImperial.FAHRENHEIT, 80); - + assertEquals("80.0 \u00B0F", value.toString()); } - + /** * Tests converting an unnamed UnitValue to a string. * @@ -222,7 +222,7 @@ class UnitTest { public void testValueToString5() { final UnitValue value = UnitValue .of(USCustomary.FAHRENHEIT.withName(NameSymbol.EMPTY), 50); - + assertEquals("50.0 unnamed unit (= 283.15 K)", value.toString()); } } diff --git a/src/test/java/sevenUnits/utils/UncertainDoubleTest.java b/src/test/java/sevenUnits/utils/UncertainDoubleTest.java index 36b373b..b251b31 100644 --- a/src/test/java/sevenUnits/utils/UncertainDoubleTest.java +++ b/src/test/java/sevenUnits/utils/UncertainDoubleTest.java @@ -43,36 +43,36 @@ class UncertainDoubleTest { assertTrue(of(2.0, 0.5).compareTo(of(1.0, 0.1)) > 0); assertTrue(of(2.0, 0.5).compareTo(of(3.0, 0.1)) < 0); } - + /** * Tests the ___exact operations */ @Test final void testExactOperations() { final UncertainDouble x = UncertainDouble.of(Math.PI, 0.1); - + // slightly different because roundoff errors final UncertainDouble x1 = UncertainDouble.of(Math.PI + Math.E - Math.E, 0.1); final UncertainDouble x2 = UncertainDouble.of(Math.PI * Math.E / Math.E, 0.1); - + // get results final UncertainDouble result1 = x.plusExact(Math.E).minusExact(Math.E); final UncertainDouble result2 = x.timesExact(Math.E) .dividedByExact(Math.E); - + // test that these operations work & don't change uncertainty assertEquals(x1, result1); assertTrue(x.equivalent(result1)); assertEquals(x2, result2); assertTrue(x.equivalent(result2)); - + // exponents are different assertEquals(Math.pow(Math.PI, Math.E), x.toExponentExact(Math.E).value()); } - + /** * Test for {@link UncertainDouble#fromRoundedString} * @@ -82,29 +82,29 @@ class UncertainDoubleTest { final void testFromRoundedString() { assertEquals(of(12345.678, 0.001), fromRoundedString("12345.678")); } - + /** * Test for {@link UncertainDouble#fromString} */ @Test final void testFromString() { // valid strings - assertEquals(of(2.0, 0.5), fromString("2.0 � 0.5")); + assertEquals(of(2.0, 0.5), fromString("2.0 ± 0.5")); assertEquals(of(2.0, 0.5), fromString("2.0 +- 0.5")); assertEquals(of(2.0, 0.0), fromString("2.0")); - + // invalid strings - for (final String s : List.of("2.A", "A", "2.0 � .", "� 3.5")) { + for (final String s : List.of("2.A", "A", "2.0 ± ", " ± 3.5")) { assertThrows(IllegalArgumentException.class, () -> fromString(s)); } - + // back and forth - assertEquals("2.0 � 0.5", of(2.0, 0.5).toString()); + assertEquals("2.0 ± 0.5", of(2.0, 0.5).toString()); assertEquals("2.0", of(2.0, 0).toString()); } - + @Test final void testHashCode() { - assertEquals(of(2.0, 0.5).hashCode(), fromString("2.0 � 0.5").hashCode()); + assertEquals(of(2.0, 0.5).hashCode(), fromString("2.0 ± 0.5").hashCode()); } } |