diff options
Diffstat (limited to 'src/org/unitConverter/math/UncertainDouble.java')
-rw-r--r-- | src/org/unitConverter/math/UncertainDouble.java | 419 |
1 files changed, 0 insertions, 419 deletions
diff --git a/src/org/unitConverter/math/UncertainDouble.java b/src/org/unitConverter/math/UncertainDouble.java deleted file mode 100644 index 3651bd5..0000000 --- a/src/org/unitConverter/math/UncertainDouble.java +++ /dev/null @@ -1,419 +0,0 @@ -/** - * Copyright (C) 2020 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 - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - */ -package org.unitConverter.math; - -import java.math.BigDecimal; -import java.math.RoundingMode; -import java.util.Objects; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * A double with an associated uncertainty value. For example, 3.2 ± 0.2. - * <p> - * All methods in this class throw a NullPointerException if any of their - * arguments is null. - * - * @since 2020-09-07 - */ -public final class UncertainDouble implements Comparable<UncertainDouble> { - /** - * The exact value 0 - */ - public static final UncertainDouble ZERO = UncertainDouble.of(0, 0); - - /** - * A regular expression that can recognize toString forms - */ - private static final Pattern TO_STRING = Pattern - .compile("([a-zA-Z_0-9\\.\\,]+)" // a number - // optional "± [number]" - + "(?:\\s*(?:±|\\+-)\\s*([a-zA-Z_0-9\\.\\,]+))?"); - - /** - * 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 "±". - * - * @param s string to parse - * @return {@code UncertainDouble} instance - * @throws IllegalArgumentException if the string is invalid - * @since 2020-09-07 - */ - public static final UncertainDouble fromString(String s) { - Objects.requireNonNull(s, "s may not be null"); - final Matcher matcher = TO_STRING.matcher(s); - - double value, uncertainty; - try { - value = Double.parseDouble(matcher.group(1)); - } catch (IllegalStateException | NumberFormatException e) { - throw new IllegalArgumentException( - "String " + s + " not in correct format."); - } - - final String uncertaintyString = matcher.group(2); - if (uncertaintyString == null) { - uncertainty = 0; - } else { - try { - uncertainty = Double.parseDouble(uncertaintyString); - } catch (final NumberFormatException e) { - throw new IllegalArgumentException( - "String " + s + " not in correct format."); - } - } - - return UncertainDouble.of(value, uncertainty); - } - - /** - * Gets an {@code UncertainDouble} from its value and <b>absolute</b> - * uncertainty. - * - * @since 2020-09-07 - */ - 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. - * - * @since 2020-09-07 - */ - public static final UncertainDouble ofRelative(double value, - double relativeUncertainty) { - return new UncertainDouble(value, value * relativeUncertainty); - } - - private final double value; - - private final double uncertainty; - - /** - * @param value - * @param uncertainty - * @since 2020-09-07 - */ - private UncertainDouble(double value, double uncertainty) { - this.value = value; - // uncertainty should only ever be positive - this.uncertainty = Math.abs(uncertainty); - } - - /** - * Compares this {@code UncertainDouble} with another - * {@code UncertainDouble}. - * <p> - * This method only compares the values, not the uncertainties. So 3.1 ± 0.5 - * is considered less than 3.2 ± 0.5, even though they are equivalent. - * <p> - * <b>Note:</b> The natural ordering of this class is inconsistent with - * equals. Specifically, if two {@code UncertainDouble} instances {@code a} - * and {@code b} have the same value but different uncertainties, - * {@code a.compareTo(b)} will return 0 but {@code a.equals(b)} will return - * {@code false}. - */ - @Override - public final int compareTo(UncertainDouble o) { - return Double.compare(this.value, o.value); - } - - /** - * Returns the quotient of {@code this} and {@code other}. - * - * @since 2020-09-07 - */ - public final 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())); - } - - /** - * Returns the quotient of {@code this} and the exact value {@code other}. - * - * @since 2020-09-07 - */ - 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) - 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) - return false; - return true; - } - - /** - * @param other another {@code UncertainDouble} - * @return true iff this and {@code other} are within each other's - * uncertainty range. - * @since 2020-09-07 - */ - public final 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); - } - - /** - * Gets the preferred scale for rounding a value for toString. - * - * @since 2020-09-07 - */ - private final int getDisplayScale() { - // round based on uncertainty - // if uncertainty starts with 1 (ignoring zeroes and the decimal - // point), rounds - // so that uncertainty has 2 significant digits. - // 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); - - // 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; - int result = 1; - result = prime * result + Double.hashCode(this.value); - result = prime * result + Double.hashCode(this.uncertainty); - return result; - } - - /** - * @return true iff the value has no uncertainty - * - * @since 2020-09-07 - */ - public final boolean isExact() { - return this.uncertainty == 0; - } - - /** - * Returns the difference of {@code this} and {@code other}. - * - * @since 2020-09-07 - */ - public final 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)); - } - - /** - * Returns the difference of {@code this} and the exact value {@code other}. - * - * @since 2020-09-07 - */ - public final UncertainDouble minusExact(double other) { - return UncertainDouble.of(this.value - other, this.uncertainty); - } - - /** - * Returns the sum of {@code this} and {@code other}. - * - * @since 2020-09-07 - */ - public final 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)); - } - - /** - * Returns the sum of {@code this} and the exact value {@code other}. - * - * @since 2020-09-07 - */ - public final UncertainDouble plusExact(double other) { - return UncertainDouble.of(this.value + other, this.uncertainty); - } - - /** - * @return relative uncertainty - * @since 2020-09-07 - */ - public final double relativeUncertainty() { - return this.uncertainty / this.value; - } - - /** - * Returns the product of {@code this} and {@code other}. - * - * @since 2020-09-07 - */ - public final 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())); - } - - /** - * Returns the product of {@code this} and the exact value {@code other}. - * - * @since 2020-09-07 - */ - 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}. - * - * @since 2020-09-07 - */ - 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}. - * - * @since 2020-09-07 - */ - public final UncertainDouble toExponentExact(double other) { - return UncertainDouble.ofRelative(Math.pow(this.value, other), - this.relativeUncertainty() * other); - } - - /** - * 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. - * - * <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 - */ - @Override - public final String toString() { - return this.toString(!this.isExact()); - } - - /** - * 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" - * <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 - * same precision as the uncertainty, otherwise it will not be rounded. The - * string is still rounded if {@code showUncertainty} is false.<br> - * UNCERTAINTY represents a string representation of this - * {@code UncertainDouble}'s uncertainty. If the uncertainty ends in 1X - * (where X represents any digit) it will be rounded to two significant - * 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.13).toString(false) = "3.27" - * 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" - * </pre> - * - * @since 2020-09-07 - */ - public final String toString(boolean showUncertainty) { - 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.HALF_EVEN); - final BigDecimal roundedValue = bigValue.setScale(displayScale, - RoundingMode.HALF_EVEN); - - valueString = roundedValue.toString(); - uncertaintyString = roundedUncertainty.toString(); - } - - // return "value" or "value ± uncertainty" depending on showUncertainty - return valueString + (showUncertainty ? " ± " + uncertaintyString : ""); - } - - /** - * @return absolute uncertainty - * @since 2020-09-07 - */ - public final double uncertainty() { - return this.uncertainty; - } - - /** - * @return value without uncertainty - * @since 2020-09-07 - */ - public final double value() { - return this.value; - } -} |