diff options
Diffstat (limited to 'src/main/java/sevenUnits/utils/UncertainDouble.java')
-rw-r--r-- | src/main/java/sevenUnits/utils/UncertainDouble.java | 251 |
1 files changed, 157 insertions, 94 deletions
diff --git a/src/main/java/sevenUnits/utils/UncertainDouble.java b/src/main/java/sevenUnits/utils/UncertainDouble.java index 66d8103..24ada20 100644 --- a/src/main/java/sevenUnits/utils/UncertainDouble.java +++ b/src/main/java/sevenUnits/utils/UncertainDouble.java @@ -1,5 +1,5 @@ /** - * Copyright (C) 2020 Adrien Hopkins + * Copyright (C) 2020-2022, 2024, 2025 Adrien Hopkins * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -19,7 +19,6 @@ package sevenUnits.utils; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.Objects; -import java.util.regex.Matcher; import java.util.regex.Pattern; /** @@ -29,56 +28,59 @@ import java.util.regex.Pattern; * arguments is null. * * @since 2020-09-07 + * @since v0.3.0 */ public final class UncertainDouble implements Comparable<UncertainDouble> { - /** - * The exact value 0 - */ + /** 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 - */ + /** 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. + * + * @param s string to parse + * @return parsed {@code UncertainDouble} + * * @throws NumberFormatException if the argument is not a number * * @since 2022-04-18 + * @since v0.4.0 */ - public static final UncertainDouble fromRoundedString(String s) { - final BigDecimal value = new BigDecimal(s); - final double uncertainty = Math.pow(10, -value.scale()); + public static UncertainDouble fromRoundedString(String s) { + final var value = new BigDecimal(s); + final var 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. + * Parses a string in the form of + * {@link UncertainDouble#toString(boolean, RoundingMode)} 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 * @throws IllegalArgumentException if the string is invalid * @since 2020-09-07 + * @since v0.3.0 */ - public static final UncertainDouble fromString(String s) { + public static UncertainDouble fromString(String s) { Objects.requireNonNull(s, "s may not be null"); - final Matcher matcher = TO_STRING.matcher(s); + final var 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 { @@ -88,7 +90,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { "String " + s + " not in correct format."); } - final String uncertaintyString = matcher.group(2); + final var uncertaintyString = matcher.group(2); if (uncertaintyString == null) { uncertainty = 0; } else { @@ -106,20 +108,32 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { /** * Gets an {@code UncertainDouble} from its value and <b>absolute</b> * uncertainty. - * + * + * @param value double's value + * @param uncertainty double's uncertainty (non-negative) + * @return {@code UncertainDouble} instance with these parameters + * * @since 2020-09-07 + * @since v0.3.0 */ - public static final UncertainDouble of(double value, double uncertainty) { + public static UncertainDouble of(double value, double uncertainty) { return new UncertainDouble(value, uncertainty); } /** * Gets an {@code UncertainDouble} from its value and <b>relative</b> * uncertainty. - * + * + * @param value double's value + * @param relativeUncertainty double's uncertainty (non-negative); the + * absolute uncertainty is equal to this value + * multiplied by {@code relativeUncertainty} + * @return {@code UncertainDouble} instance with these parameters + * * @since 2020-09-07 + * @since v0.3.0 */ - public static final UncertainDouble ofRelative(double value, + public static UncertainDouble ofRelative(double value, double relativeUncertainty) { return new UncertainDouble(value, value * relativeUncertainty); } @@ -132,6 +146,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { * @param value * @param uncertainty * @since 2020-09-07 + * @since v0.3.0 */ private UncertainDouble(double value, double uncertainty) { this.value = value; @@ -153,16 +168,20 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { * {@code false}. */ @Override - public final int compareTo(UncertainDouble o) { + public int compareTo(UncertainDouble o) { return Double.compare(this.value, o.value); } /** * Returns the quotient of {@code this} and {@code other}. - * + * + * @param other number to divide by + * @return quotient + * * @since 2020-09-07 + * @since v0.3.0 */ - public final UncertainDouble dividedBy(UncertainDouble other) { + public UncertainDouble dividedBy(UncertainDouble other) { Objects.requireNonNull(other, "other may not be null"); return UncertainDouble.ofRelative(this.value / other.value, Math .hypot(this.relativeUncertainty(), other.relativeUncertainty())); @@ -170,23 +189,26 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { /** * Returns the quotient of {@code this} and the exact value {@code other}. - * + * + * @param other number to divide by + * @return quotient + * * @since 2020-09-07 + * @since v0.3.0 */ - public final UncertainDouble dividedByExact(double other) { + public UncertainDouble dividedByExact(double other) { return UncertainDouble.of(this.value / other, this.uncertainty / other); } @Override - public final boolean equals(Object obj) { + public boolean equals(Object obj) { if (this == obj) return true; if (!(obj instanceof UncertainDouble)) return false; - final UncertainDouble other = (UncertainDouble) obj; - if (Double.compare(this.value, other.value) != 0) - return false; - if (Double.compare(this.uncertainty, other.uncertainty) != 0) + final var other = (UncertainDouble) obj; + if ((Double.compare(this.value, other.value) != 0) + || (Double.compare(this.uncertainty, other.uncertainty) != 0)) return false; return true; } @@ -196,8 +218,9 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { * @return true iff this and {@code other} are within each other's * uncertainty range. * @since 2020-09-07 + * @since v0.3.0 */ - public final boolean equivalent(UncertainDouble other) { + public boolean equivalent(UncertainDouble other) { Objects.requireNonNull(other, "other may not be null"); return Math.abs(this.value - other.value) <= Math.min(this.uncertainty, other.uncertainty); @@ -205,10 +228,11 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { /** * Gets the preferred scale for rounding a value for toString. - * + * * @since 2020-09-07 + * @since v0.3.0 */ - private final int getDisplayScale() { + private int getDisplayScale() { // round based on uncertainty // if uncertainty starts with 1 (ignoring zeroes and the decimal // point), rounds @@ -216,24 +240,23 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { // otherwise, rounds so that uncertainty has 1 significant digits. // the value is rounded to the same number of decimal places as the // uncertainty. - final BigDecimal bigUncertainty = BigDecimal.valueOf(this.uncertainty); + final var bigUncertainty = BigDecimal.valueOf(this.uncertainty); // the scale that will give the uncertainty two decimal places - final int twoDecimalPlacesScale = bigUncertainty.scale() + final var twoDecimalPlacesScale = bigUncertainty.scale() - bigUncertainty.precision() + 2; - final BigDecimal roundedUncertainty = bigUncertainty + final var roundedUncertainty = bigUncertainty .setScale(twoDecimalPlacesScale, RoundingMode.HALF_EVEN); if (roundedUncertainty.unscaledValue().intValue() >= 20) return twoDecimalPlacesScale - 1; // one decimal place - else - return twoDecimalPlacesScale; + return twoDecimalPlacesScale; } @Override - public final int hashCode() { - final int prime = 31; - int result = 1; + public int hashCode() { + final var prime = 31; + var result = 1; result = prime * result + Double.hashCode(this.value); result = prime * result + Double.hashCode(this.uncertainty); return result; @@ -241,19 +264,24 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { /** * @return true iff the value has no uncertainty - * + * * @since 2020-09-07 + * @since v0.3.0 */ - public final boolean isExact() { + public boolean isExact() { return this.uncertainty == 0; } /** * Returns the difference of {@code this} and {@code other}. - * + * + * @param other number to subtract + * @return result of subtraction + * * @since 2020-09-07 + * @since v0.3.0 */ - public final UncertainDouble minus(UncertainDouble other) { + public UncertainDouble minus(UncertainDouble other) { Objects.requireNonNull(other, "other may not be null"); return UncertainDouble.of(this.value - other.value, Math.hypot(this.uncertainty, other.uncertainty)); @@ -261,19 +289,27 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { /** * Returns the difference of {@code this} and the exact value {@code other}. - * + * + * @param other number to subtract + * @return result of subtraction + * * @since 2020-09-07 + * @since v0.3.0 */ - public final UncertainDouble minusExact(double other) { + public UncertainDouble minusExact(double other) { return UncertainDouble.of(this.value - other, this.uncertainty); } /** * Returns the sum of {@code this} and {@code other}. - * + * + * @param other number to add + * @return result of addition + * * @since 2020-09-07 + * @since v0.3.0 */ - public final UncertainDouble plus(UncertainDouble other) { + public UncertainDouble plus(UncertainDouble other) { Objects.requireNonNull(other, "other may not be null"); return UncertainDouble.of(this.value + other.value, Math.hypot(this.uncertainty, other.uncertainty)); @@ -281,27 +317,36 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { /** * Returns the sum of {@code this} and the exact value {@code other}. - * + * + * @param other number to add + * @return result of addition + * * @since 2020-09-07 + * @since v0.3.0 */ - public final UncertainDouble plusExact(double other) { + public UncertainDouble plusExact(double other) { return UncertainDouble.of(this.value + other, this.uncertainty); } /** * @return relative uncertainty * @since 2020-09-07 + * @since v0.3.0 */ - public final double relativeUncertainty() { + public double relativeUncertainty() { return this.uncertainty / this.value; } /** * Returns the product of {@code this} and {@code other}. - * + * + * @param other number to multiply + * @return product + * * @since 2020-09-07 + * @since v0.3.0 */ - public final UncertainDouble times(UncertainDouble other) { + public UncertainDouble times(UncertainDouble other) { Objects.requireNonNull(other, "other may not be null"); return UncertainDouble.ofRelative(this.value * other.value, Math .hypot(this.relativeUncertainty(), other.relativeUncertainty())); @@ -309,23 +354,31 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { /** * Returns the product of {@code this} and the exact value {@code other}. - * + * + * @param other number to multiply + * @return product + * * @since 2020-09-07 + * @since v0.3.0 */ - public final UncertainDouble timesExact(double other) { + public 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}. - * + * + * @param other exponent + * @return result of exponentation + * * @since 2020-09-07 + * @since v0.3.0 */ - public final UncertainDouble toExponent(UncertainDouble other) { + public 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( + final var result = Math.pow(this.value, other.value); + final var relativeUncertainty = Math.hypot( other.value * this.relativeUncertainty(), Math.log(this.value) * other.uncertainty); @@ -335,10 +388,14 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { /** * Returns the result of {@code this} raised the exact exponent * {@code other}. - * + * + * @param other exponent + * @return result of exponentation + * * @since 2020-09-07 + * @since v0.3.0 */ - public final UncertainDouble toExponentExact(double other) { + public UncertainDouble toExponentExact(double other) { return UncertainDouble.ofRelative(Math.pow(this.value, other), this.relativeUncertainty() * other); } @@ -346,23 +403,24 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { /** * Returns a string representation of this {@code UncertainDouble}. * <p> - * This method returns the same value as {@link #toString(boolean)}, but - * {@code showUncertainty} is true if and only if the uncertainty is - * non-zero. - * + * This method returns the same value as + * {@link #toString(boolean, RoundingMode)}, but {@code showUncertainty} is + * true if and only if the uncertainty is non-zero. + * * <p> * Examples: - * + * * <pre> * UncertainDouble.of(3.27, 0.22).toString() = "3.3 � 0.2" * UncertainDouble.of(3.27, 0.13).toString() = "3.27 � 0.13" * UncertainDouble.of(-5.01, 0).toString() = "-5.01" * </pre> - * + * * @since 2020-09-07 + * @since v0.3.0 */ @Override - public final String toString() { + public String toString() { return this.toString(!this.isExact(), RoundingMode.HALF_EVEN); } @@ -370,7 +428,7 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { * 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 @@ -382,20 +440,24 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { * digits otherwise it will be rounded to one significant digit. * <p> * Examples: - * + * * <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> - * + * + * @param showUncertainty uncertainty is only shown if this parameter is true + * @param roundingMode how to round values + * @return string representation of this {@code UncertainDouble} + * * @since 2020-09-07 + * @since v0.3.0 */ - public final String toString(boolean showUncertainty, - RoundingMode roundingMode) { + public String toString(boolean showUncertainty, RoundingMode roundingMode) { String valueString, uncertaintyString; // generate the string representation of value and uncertainty @@ -405,36 +467,37 @@ public final class UncertainDouble implements Comparable<UncertainDouble> { } else { // round the value and uncertainty according to getDisplayScale() - final BigDecimal bigValue = BigDecimal.valueOf(this.value); - final BigDecimal bigUncertainty = BigDecimal.valueOf(this.uncertainty); + final var bigValue = BigDecimal.valueOf(this.value); + final var bigUncertainty = BigDecimal.valueOf(this.uncertainty); - final int displayScale = this.getDisplayScale(); - final BigDecimal roundedUncertainty = bigUncertainty - .setScale(displayScale, roundingMode); - final BigDecimal roundedValue = bigValue.setScale(displayScale, + final var displayScale = this.getDisplayScale(); + final var roundedUncertainty = bigUncertainty.setScale(displayScale, roundingMode); + final var 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 + * @since v0.3.0 */ - public final double uncertainty() { + public double uncertainty() { return this.uncertainty; } /** * @return value without uncertainty * @since 2020-09-07 + * @since v0.3.0 */ - public final double value() { + public double value() { return this.value; } } |