diff options
Diffstat (limited to 'src/org/unitConverter/unit')
-rw-r--r-- | src/org/unitConverter/unit/LinearUnit.java | 302 | ||||
-rw-r--r-- | src/org/unitConverter/unit/LinearUnitValue.java | 252 | ||||
-rw-r--r-- | src/org/unitConverter/unit/UnitDatabase.java | 6 | ||||
-rw-r--r-- | src/org/unitConverter/unit/UnitTest.java | 7 |
4 files changed, 233 insertions, 334 deletions
diff --git a/src/org/unitConverter/unit/LinearUnit.java b/src/org/unitConverter/unit/LinearUnit.java index 762572a..2d63ca7 100644 --- a/src/org/unitConverter/unit/LinearUnit.java +++ b/src/org/unitConverter/unit/LinearUnit.java @@ -20,89 +20,83 @@ import java.util.Objects; import org.unitConverter.math.DecimalComparison; import org.unitConverter.math.ObjectProduct; +import org.unitConverter.math.UncertainDouble; /** - * A unit that can be expressed as a product of its base and a number. For example, kilometres, inches and pounds. + * A unit that can be expressed as a product of its base and a number. For + * example, kilometres, inches and pounds. * * @author Adrien Hopkins * @since 2019-10-16 */ public final class LinearUnit extends Unit { /** - * Gets a {@code LinearUnit} from a unit and a value. For example, converts '59 °F' to a linear unit with the value - * of '288.15 K' + * Gets a {@code LinearUnit} from a unit and a value. For example, converts + * '59 °F' to a linear unit with the value of '288.15 K' * - * @param unit - * unit to convert - * @param value - * value to convert + * @param unit unit to convert + * @param value value to convert * @return value expressed as a {@code LinearUnit} * @since 2019-10-16 - * @throws NullPointerException - * if unit is null + * @throws NullPointerException if unit is null */ public static LinearUnit fromUnitValue(final Unit unit, final double value) { - return new LinearUnit(Objects.requireNonNull(unit, "unit must not be null.").getBase(), + return new LinearUnit( + Objects.requireNonNull(unit, "unit must not be null.").getBase(), unit.convertToBase(value), NameSymbol.EMPTY); } - + /** - * Gets a {@code LinearUnit} from a unit and a value. For example, converts '59 °F' to a linear unit with the value - * of '288.15 K' + * Gets a {@code LinearUnit} from a unit and a value. For example, converts + * '59 °F' to a linear unit with the value of '288.15 K' * - * @param unit - * unit to convert - * @param value - * value to convert - * @param ns - * name(s) and symbol of unit + * @param unit unit to convert + * @param value value to convert + * @param ns name(s) and symbol of unit * @return value expressed as a {@code LinearUnit} * @since 2019-10-21 - * @throws NullPointerException - * if unit or ns is null + * @throws NullPointerException if unit or ns is null */ - public static LinearUnit fromUnitValue(final Unit unit, final double value, final NameSymbol ns) { - return new LinearUnit(Objects.requireNonNull(unit, "unit must not be null.").getBase(), + public static LinearUnit fromUnitValue(final Unit unit, final double value, + final NameSymbol ns) { + return new LinearUnit( + Objects.requireNonNull(unit, "unit must not be null.").getBase(), unit.convertToBase(value), ns); } - + /** - * Gets a {@code LinearUnit} from a unit base and a conversion factor. In other words, gets the product of - * {@code unitBase} and {@code conversionFactor}, expressed as a {@code LinearUnit}. + * Gets a {@code LinearUnit} from a unit base and a conversion factor. In + * other words, gets the product of {@code unitBase} and + * {@code conversionFactor}, expressed as a {@code LinearUnit}. * - * @param unitBase - * unit base to multiply by - * @param conversionFactor - * number to multiply base by + * @param unitBase unit base to multiply by + * @param conversionFactor number to multiply base by * @return product of base and conversion factor * @since 2019-10-16 - * @throws NullPointerException - * if unitBase is null + * @throws NullPointerException if unitBase is null */ - public static LinearUnit valueOf(final ObjectProduct<BaseUnit> unitBase, final double conversionFactor) { + public static LinearUnit valueOf(final ObjectProduct<BaseUnit> unitBase, + final double conversionFactor) { return new LinearUnit(unitBase, conversionFactor, NameSymbol.EMPTY); } - + /** - * Gets a {@code LinearUnit} from a unit base and a conversion factor. In other words, gets the product of - * {@code unitBase} and {@code conversionFactor}, expressed as a {@code LinearUnit}. + * Gets a {@code LinearUnit} from a unit base and a conversion factor. In + * other words, gets the product of {@code unitBase} and + * {@code conversionFactor}, expressed as a {@code LinearUnit}. * - * @param unitBase - * unit base to multiply by - * @param conversionFactor - * number to multiply base by - * @param ns - * name(s) and symbol of unit + * @param unitBase unit base to multiply by + * @param conversionFactor number to multiply base by + * @param ns name(s) and symbol of unit * @return product of base and conversion factor * @since 2019-10-21 - * @throws NullPointerException - * if unitBase is null + * @throws NullPointerException if unitBase is null */ - public static LinearUnit valueOf(final ObjectProduct<BaseUnit> unitBase, final double conversionFactor, - final NameSymbol ns) { + public static LinearUnit valueOf(final ObjectProduct<BaseUnit> unitBase, + final double conversionFactor, final NameSymbol ns) { return new LinearUnit(unitBase, conversionFactor, ns); } - + /** * The value of this unit as represented in its base form. Mathematically, * @@ -113,21 +107,20 @@ public final class LinearUnit extends Unit { * @since 2019-10-16 */ private final double conversionFactor; - + /** * Creates the {@code LinearUnit}. * - * @param unitBase - * base of linear unit - * @param conversionFactor - * conversion factor between base and unit + * @param unitBase base of linear unit + * @param conversionFactor conversion factor between base and unit * @since 2019-10-16 */ - private LinearUnit(final ObjectProduct<BaseUnit> unitBase, final double conversionFactor, final NameSymbol ns) { + private LinearUnit(final ObjectProduct<BaseUnit> unitBase, + final double conversionFactor, final NameSymbol ns) { super(unitBase, ns); this.conversionFactor = conversionFactor; } - + /** * {@inheritDoc} * @@ -137,7 +130,32 @@ public final class LinearUnit extends Unit { protected double convertFromBase(final double value) { return value / this.getConversionFactor(); } - + + /** + * Converts an {@code UncertainDouble} value expressed in this unit to an + * {@code UncertainValue} value expressed in {@code other}. + * + * @param other unit to convert to + * @param value value to convert + * @return converted value + * @since 2019-09-07 + * @throws IllegalArgumentException if {@code other} is incompatible for + * conversion with this unit (as tested by + * {@link Unit#canConvertTo}). + * @throws NullPointerException if value or other is null + */ + public UncertainDouble convertTo(LinearUnit other, UncertainDouble value) { + Objects.requireNonNull(other, "other must not be null."); + Objects.requireNonNull(value, "value may not be null."); + if (this.canConvertTo(other)) + return value.timesExact( + this.getConversionFactor() / other.getConversionFactor()); + else + throw new IllegalArgumentException( + String.format("Cannot convert from %s to %s.", this, other)); + + } + /** * {@inheritDoc} * @@ -147,12 +165,20 @@ public final class LinearUnit extends Unit { protected double convertToBase(final double value) { return value * this.getConversionFactor(); } - + + /** + * Converts an {@code UncertainDouble} to the base unit. + * + * @since 2020-09-07 + */ + UncertainDouble convertToBase(final UncertainDouble value) { + return value.timesExact(this.getConversionFactor()); + } + /** * Divides this unit by a scalar. * - * @param divisor - * scalar to divide by + * @param divisor scalar to divide by * @return quotient * @since 2018-12-23 * @since v0.1.0 @@ -160,26 +186,26 @@ public final class LinearUnit extends Unit { public LinearUnit dividedBy(final double divisor) { return valueOf(this.getBase(), this.getConversionFactor() / divisor); } - + /** * Returns the quotient of this unit and another. * - * @param divisor - * unit to divide by + * @param divisor unit to divide by * @return quotient of two units - * @throws NullPointerException - * if {@code divisor} is null + * @throws NullPointerException if {@code divisor} is null * @since 2018-12-22 * @since v0.1.0 */ public LinearUnit dividedBy(final LinearUnit divisor) { Objects.requireNonNull(divisor, "other must not be null"); - + // divide the units - final ObjectProduct<BaseUnit> base = this.getBase().dividedBy(divisor.getBase()); - return valueOf(base, this.getConversionFactor() / divisor.getConversionFactor()); + final ObjectProduct<BaseUnit> base = this.getBase() + .dividedBy(divisor.getBase()); + return valueOf(base, + this.getConversionFactor() / divisor.getConversionFactor()); } - + /** * {@inheritDoc} * @@ -191,9 +217,10 @@ public final class LinearUnit extends Unit { return false; final LinearUnit other = (LinearUnit) obj; return Objects.equals(this.getBase(), other.getBase()) - && DecimalComparison.equals(this.getConversionFactor(), other.getConversionFactor()); + && DecimalComparison.equals(this.getConversionFactor(), + other.getConversionFactor()); } - + /** * @return conversion factor * @since 2019-10-16 @@ -201,7 +228,7 @@ public final class LinearUnit extends Unit { public double getConversionFactor() { return this.conversionFactor; } - + /** * {@inheritDoc} * @@ -209,18 +236,20 @@ public final class LinearUnit extends Unit { */ @Override public int hashCode() { - return 31 * this.getBase().hashCode() + DecimalComparison.hash(this.getConversionFactor()); + return 31 * this.getBase().hashCode() + + DecimalComparison.hash(this.getConversionFactor()); } - + /** - * @return whether this unit is equivalent to a {@code BaseUnit} (i.e. there is a {@code BaseUnit b} where + * @return whether this unit is equivalent to a {@code BaseUnit} (i.e. there + * is a {@code BaseUnit b} where * {@code b.asLinearUnit().equals(this)} returns {@code true}.) * @since 2019-10-16 */ public boolean isBase() { return this.isCoherent() && this.getBase().isSingleObject(); } - + /** * @return whether this unit is coherent (i.e. has conversion factor 1) * @since 2019-10-16 @@ -228,70 +257,73 @@ public final class LinearUnit extends Unit { public boolean isCoherent() { return this.getConversionFactor() == 1; } - + /** * Returns the difference of this unit and another. * <p> - * Two units can be subtracted if they have the same base. Note that {@link #canConvertTo} can be used to determine - * this. If {@code subtrahend} does not meet this condition, an {@code IllegalArgumentException} will be thrown. + * Two units can be subtracted if they have the same base. Note that + * {@link #canConvertTo} can be used to determine this. If {@code subtrahend} + * does not meet this condition, an {@code IllegalArgumentException} will be + * thrown. * </p> * - * @param subtrahend - * unit to subtract + * @param subtrahend unit to subtract * @return difference of units - * @throws IllegalArgumentException - * if {@code subtrahend} is not compatible for subtraction as described above - * @throws NullPointerException - * if {@code subtrahend} is null + * @throws IllegalArgumentException if {@code subtrahend} is not compatible + * for subtraction as described above + * @throws NullPointerException if {@code subtrahend} is null * @since 2019-03-17 * @since v0.2.0 */ public LinearUnit minus(final LinearUnit subtrahend) { Objects.requireNonNull(subtrahend, "addend must not be null."); - + // reject subtrahends that cannot be added to this unit if (!this.getBase().equals(subtrahend.getBase())) - throw new IllegalArgumentException( - String.format("Incompatible units for subtraction \"%s\" and \"%s\".", this, subtrahend)); - + throw new IllegalArgumentException(String.format( + "Incompatible units for subtraction \"%s\" and \"%s\".", this, + subtrahend)); + // subtract the units - return valueOf(this.getBase(), this.getConversionFactor() - subtrahend.getConversionFactor()); + return valueOf(this.getBase(), + this.getConversionFactor() - subtrahend.getConversionFactor()); } - + /** * Returns the sum of this unit and another. * <p> - * Two units can be added if they have the same base. Note that {@link #canConvertTo} can be used to determine this. - * If {@code addend} does not meet this condition, an {@code IllegalArgumentException} will be thrown. + * Two units can be added if they have the same base. Note that + * {@link #canConvertTo} can be used to determine this. If {@code addend} + * does not meet this condition, an {@code IllegalArgumentException} will be + * thrown. * </p> * - * @param addend - * unit to add + * @param addend unit to add * @return sum of units - * @throws IllegalArgumentException - * if {@code addend} is not compatible for addition as described above - * @throws NullPointerException - * if {@code addend} is null + * @throws IllegalArgumentException if {@code addend} is not compatible for + * addition as described above + * @throws NullPointerException if {@code addend} is null * @since 2019-03-17 * @since v0.2.0 */ public LinearUnit plus(final LinearUnit addend) { Objects.requireNonNull(addend, "addend must not be null."); - + // reject addends that cannot be added to this unit if (!this.getBase().equals(addend.getBase())) - throw new IllegalArgumentException( - String.format("Incompatible units for addition \"%s\" and \"%s\".", this, addend)); - + throw new IllegalArgumentException(String.format( + "Incompatible units for addition \"%s\" and \"%s\".", this, + addend)); + // add the units - return valueOf(this.getBase(), this.getConversionFactor() + addend.getConversionFactor()); + return valueOf(this.getBase(), + this.getConversionFactor() + addend.getConversionFactor()); } - + /** * Multiplies this unit by a scalar. * - * @param multiplier - * scalar to multiply by + * @param multiplier scalar to multiply by * @return product * @since 2018-12-23 * @since v0.1.0 @@ -299,39 +331,39 @@ public final class LinearUnit extends Unit { public LinearUnit times(final double multiplier) { return valueOf(this.getBase(), this.getConversionFactor() * multiplier); } - + /** * Returns the product of this unit and another. * - * @param multiplier - * unit to multiply by + * @param multiplier unit to multiply by * @return product of two units - * @throws NullPointerException - * if {@code multiplier} is null + * @throws NullPointerException if {@code multiplier} is null * @since 2018-12-22 * @since v0.1.0 */ public LinearUnit times(final LinearUnit multiplier) { Objects.requireNonNull(multiplier, "other must not be null"); - + // multiply the units - final ObjectProduct<BaseUnit> base = this.getBase().times(multiplier.getBase()); - return valueOf(base, this.getConversionFactor() * multiplier.getConversionFactor()); + final ObjectProduct<BaseUnit> base = this.getBase() + .times(multiplier.getBase()); + return valueOf(base, + this.getConversionFactor() * multiplier.getConversionFactor()); } - + /** * Returns this unit but to an exponent. * - * @param exponent - * exponent to exponentiate unit to + * @param exponent exponent to exponentiate unit to * @return exponentiated unit * @since 2019-01-15 * @since v0.1.0 */ public LinearUnit toExponent(final int exponent) { - return valueOf(this.getBase().toExponent(exponent), Math.pow(this.conversionFactor, exponent)); + return valueOf(this.getBase().toExponent(exponent), + Math.pow(this.conversionFactor, exponent)); } - + /** * @return a string providing a definition of this unit * @since 2019-10-21 @@ -339,10 +371,13 @@ public final class LinearUnit extends Unit { @Override public String toString() { return this.getPrimaryName().orElse("Unnamed unit") - + (this.getSymbol().isPresent() ? String.format(" (%s)", this.getSymbol().get()) : "") + ", " - + Double.toString(this.conversionFactor) + " * " + this.getBase().toString(u -> u.getSymbol().get()); + + (this.getSymbol().isPresent() + ? String.format(" (%s)", this.getSymbol().get()) + : "") + + ", " + Double.toString(this.conversionFactor) + " * " + + this.getBase().toString(u -> u.getSymbol().get()); } - + @Override public LinearUnit withName(final NameSymbol ns) { return valueOf(this.getBase(), this.getConversionFactor(), ns); @@ -351,37 +386,38 @@ public final class LinearUnit extends Unit { /** * Returns the result of applying {@code prefix} to this unit. * <p> - * If this unit and the provided prefix have a primary name, the returned unit will have a primary name (prefix's - * name + unit's name). <br> - * If this unit and the provided prefix have a symbol, the returned unit will have a symbol. <br> - * This method ignores alternate names of both this unit and the provided prefix. + * If this unit and the provided prefix have a primary name, the returned + * unit will have a primary name (prefix's name + unit's name). <br> + * If this unit and the provided prefix have a symbol, the returned unit will + * have a symbol. <br> + * This method ignores alternate names of both this unit and the provided + * prefix. * - * @param prefix - * prefix to apply + * @param prefix prefix to apply * @return unit with prefix * @since 2019-03-18 * @since v0.2.0 - * @throws NullPointerException - * if prefix is null + * @throws NullPointerException if prefix is null */ public LinearUnit withPrefix(final UnitPrefix prefix) { final LinearUnit unit = this.times(prefix.getMultiplier()); - + // create new name and symbol, if possible final String name; - if (this.getPrimaryName().isPresent() && prefix.getPrimaryName().isPresent()) { + if (this.getPrimaryName().isPresent() + && prefix.getPrimaryName().isPresent()) { name = prefix.getPrimaryName().get() + this.getPrimaryName().get(); } else { name = null; } - + final String symbol; if (this.getSymbol().isPresent() && prefix.getSymbol().isPresent()) { symbol = prefix.getSymbol().get() + this.getSymbol().get(); } else { symbol = null; } - + return unit.withName(NameSymbol.ofNullable(name, symbol)); } } diff --git a/src/org/unitConverter/unit/LinearUnitValue.java b/src/org/unitConverter/unit/LinearUnitValue.java index 5685a6d..d86d344 100644 --- a/src/org/unitConverter/unit/LinearUnitValue.java +++ b/src/org/unitConverter/unit/LinearUnitValue.java @@ -3,12 +3,11 @@ */ package org.unitConverter.unit; -import java.math.BigDecimal; -import java.math.RoundingMode; import java.util.Objects; import java.util.Optional; import org.unitConverter.math.DecimalComparison; +import org.unitConverter.math.UncertainDouble; /** * A possibly uncertain value expressed in a linear unit. @@ -33,7 +32,8 @@ public final class LinearUnitValue { public static final LinearUnitValue getExact(final LinearUnit unit, final double value) { return new LinearUnitValue( - Objects.requireNonNull(unit, "unit must not be null"), value, 0); + Objects.requireNonNull(unit, "unit must not be null"), + UncertainDouble.of(value, 0)); } /** @@ -46,41 +46,23 @@ public final class LinearUnitValue { * @since 2020-07-26 */ public static final LinearUnitValue of(final LinearUnit unit, - final double value, final double uncertainty) { + final UncertainDouble value) { return new LinearUnitValue( - Objects.requireNonNull(unit, "unit must not be null"), value, - uncertainty); - } - - /** - * Gets an uncertain {@code LinearUnitValue} - * - * @param unit unit to express with - * @param value value to express - * @param relativeUncertainty relative uncertainty of value - * @return uncertain {@code LinearUnitValue} instance - * @since 2020-07-28 - */ - public static final LinearUnitValue ofRelative(final LinearUnit unit, - final double value, final double relativeUncertainty) { - return LinearUnitValue.of(unit, value, relativeUncertainty * value); + Objects.requireNonNull(unit, "unit must not be null"), + Objects.requireNonNull(value, "value may not be null")); } private final LinearUnit unit; - private final double value; - private final double uncertainty; + private final UncertainDouble value; /** - * @param unit unit to express as - * @param value value to express - * @param uncertainty absolute uncertainty of value + * @param unit unit to express as + * @param value value to express * @since 2020-07-26 */ - private LinearUnitValue(final LinearUnit unit, final double value, - final double uncertainty) { + private LinearUnitValue(final LinearUnit unit, final UncertainDouble value) { this.unit = unit; this.value = value; - this.uncertainty = uncertainty; } /** @@ -89,7 +71,7 @@ public final class LinearUnitValue { * @since 2020-08-04 */ public final UnitValue asUnitValue() { - return UnitValue.of(this.unit, this.value); + return UnitValue.of(this.unit, this.value.value()); } /** @@ -110,8 +92,7 @@ public final class LinearUnitValue { * @since 2020-07-26 */ public final LinearUnitValue convertTo(final LinearUnit other) { - return LinearUnitValue.of(other, this.unit.convertTo(other, this.value), - this.unit.convertTo(other, this.uncertainty)); + return LinearUnitValue.of(other, this.unit.convertTo(other, this.value)); } /** @@ -122,8 +103,7 @@ public final class LinearUnitValue { * @since 2020-07-28 */ public LinearUnitValue dividedBy(final double divisor) { - return LinearUnitValue.of(this.unit, this.value / divisor, - this.uncertainty / divisor); + return LinearUnitValue.of(this.unit, this.value.dividedByExact(divisor)); } /** @@ -134,10 +114,8 @@ public final class LinearUnitValue { * @since 2020-07-28 */ public LinearUnitValue dividedBy(final LinearUnitValue divisor) { - return LinearUnitValue.ofRelative(this.unit.dividedBy(divisor.unit), - this.value / divisor.value, - Math.hypot(this.getRelativeUncertainty(), - divisor.getRelativeUncertainty())); + return LinearUnitValue.of(this.unit.dividedBy(divisor.unit), + this.value.dividedBy(divisor.value)); } /** @@ -154,12 +132,8 @@ public final class LinearUnitValue { return false; final LinearUnitValue other = (LinearUnitValue) obj; return Objects.equals(this.unit.getBase(), other.unit.getBase()) - && Double.doubleToLongBits( - this.unit.convertToBase(this.getValue())) == Double - .doubleToLongBits( - other.unit.convertToBase(other.getValue())) - && Double.doubleToLongBits(this.getRelativeUncertainty()) == Double - .doubleToLongBits(other.getRelativeUncertainty()); + && this.unit.convertToBase(this.value) + .equals(other.unit.convertToBase(other.value)); } /** @@ -180,9 +154,7 @@ public final class LinearUnitValue { final LinearUnitValue other = (LinearUnitValue) obj; return Objects.equals(this.unit.getBase(), other.unit.getBase()) && DecimalComparison.equals(this.unit.convertToBase(this.value), - other.unit.convertToBase(other.value)) - && DecimalComparison.equals(this.getRelativeUncertainty(), - other.getRelativeUncertainty()); + other.unit.convertToBase(other.value)); } /** @@ -195,32 +167,11 @@ public final class LinearUnitValue { if (other == null || !Objects.equals(this.unit.getBase(), other.unit.getBase())) return false; - final double thisBaseValue = this.unit.convertToBase(this.value); - final double otherBaseValue = other.unit.convertToBase(other.value); - final double thisBaseUncertainty = this.unit - .convertToBase(this.uncertainty); - final double otherBaseUncertainty = other.unit - .convertToBase(other.uncertainty); - return Math.abs(thisBaseValue - otherBaseValue) <= Math - .min(thisBaseUncertainty, otherBaseUncertainty); - } - - /** - * @return relative uncertainty of value - * - * @since 2020-07-26 - */ - public final double getRelativeUncertainty() { - return this.uncertainty / this.value; - } - - /** - * @return absolute uncertainty of value - * - * @since 2020-07-26 - */ - public final double getUncertainty() { - return this.uncertainty; + final LinearUnit base = LinearUnit.valueOf(this.unit.getBase(), 1); + final LinearUnitValue thisBase = this.convertTo(base); + final LinearUnitValue otherBase = other.convertTo(base); + + return thisBase.value.equivalent(otherBase.value); } /** @@ -237,24 +188,22 @@ public final class LinearUnitValue { * * @since 2020-07-26 */ - public final double getValue() { + public final UncertainDouble getValue() { return this.value; } + /** + * @return the exact value + * @since 2020-09-07 + */ + public final double getValueExact() { + return this.value.value(); + } + @Override public int hashCode() { return Objects.hash(this.unit.getBase(), - this.unit.convertToBase(this.getValue()), - this.getRelativeUncertainty()); - } - - /** - * @return true iff the value has no uncertainty - * - * @since 2020-07-26 - */ - public final boolean isExact() { - return this.uncertainty == 0; + this.unit.convertToBase(this.getValue())); } /** @@ -276,8 +225,8 @@ public final class LinearUnitValue { this.unit, subtrahend.unit)); final LinearUnitValue otherConverted = subtrahend.convertTo(this.unit); - return LinearUnitValue.of(this.unit, this.value - otherConverted.value, - Math.hypot(this.uncertainty, otherConverted.uncertainty)); + return LinearUnitValue.of(this.unit, + this.value.minus(otherConverted.value)); } /** @@ -298,8 +247,8 @@ public final class LinearUnitValue { addend.unit)); final LinearUnitValue otherConverted = addend.convertTo(this.unit); - return LinearUnitValue.of(this.unit, this.value + otherConverted.value, - Math.hypot(this.uncertainty, otherConverted.uncertainty)); + return LinearUnitValue.of(this.unit, + this.value.plus(otherConverted.value)); } /** @@ -310,8 +259,7 @@ public final class LinearUnitValue { * @since 2020-07-28 */ public LinearUnitValue times(final double multiplier) { - return LinearUnitValue.of(this.unit, this.value * multiplier, - this.uncertainty * multiplier); + return LinearUnitValue.of(this.unit, this.value.timesExact(multiplier)); } /** @@ -322,10 +270,8 @@ public final class LinearUnitValue { * @since 2020-07-28 */ public LinearUnitValue times(final LinearUnitValue multiplier) { - return LinearUnitValue.ofRelative(this.unit.times(multiplier.unit), - this.value * multiplier.value, - Math.hypot(this.getRelativeUncertainty(), - multiplier.getRelativeUncertainty())); + return LinearUnitValue.of(this.unit.times(multiplier.unit), + this.value.times(multiplier.value)); } /** @@ -336,14 +282,13 @@ public final class LinearUnitValue { * @since 2020-07-28 */ public LinearUnitValue toExponent(final int exponent) { - return LinearUnitValue.ofRelative(this.unit.toExponent(exponent), - Math.pow(this.value, exponent), - this.getRelativeUncertainty() * Math.sqrt(exponent)); + return LinearUnitValue.of(this.unit.toExponent(exponent), + this.value.toExponentExact(exponent)); } @Override public String toString() { - return this.toString(!this.isExact()); + return this.toString(!this.value.isExact()); } /** @@ -363,107 +308,22 @@ public final class LinearUnitValue { final Optional<String> symbol = this.unit.getSymbol(); final String chosenName = symbol.orElse(primaryName.orElse(null)); - final double baseValue = this.unit.convertToBase(this.value); - final double baseUncertainty = this.unit.convertToBase(this.uncertainty); + final UncertainDouble baseValue = this.unit.convertToBase(this.value); // get rounded strings - String valueString, baseValueString, uncertaintyString, - baseUncertaintyString; - if (this.isExact()) { - valueString = Double.toString(this.value); - baseValueString = Double.toString(baseValue); - uncertaintyString = "0"; - baseUncertaintyString = "0"; - } else { - final BigDecimal bigValue = BigDecimal.valueOf(this.value); - final BigDecimal bigUncertainty = BigDecimal.valueOf(this.uncertainty); - - // 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. - BigDecimal roundedUncertainty = bigUncertainty.setScale( - bigUncertainty.scale() - bigUncertainty.precision() + 2, - RoundingMode.HALF_EVEN); - if (roundedUncertainty.unscaledValue().intValue() >= 20) { - roundedUncertainty = bigUncertainty.setScale( - bigUncertainty.scale() - bigUncertainty.precision() + 1, - RoundingMode.HALF_EVEN); - } - final BigDecimal roundedValue = bigValue - .setScale(roundedUncertainty.scale(), RoundingMode.HALF_EVEN); - - valueString = roundedValue.toString(); - uncertaintyString = roundedUncertainty.toString(); - - if (primaryName.isEmpty() && symbol.isEmpty()) { - final BigDecimal bigBaseValue = BigDecimal.valueOf(baseValue); - final BigDecimal bigBaseUncertainty = BigDecimal - .valueOf(baseUncertainty); - - BigDecimal roundedBaseUncertainty = bigBaseUncertainty - .setScale( - bigBaseUncertainty.scale() - - bigBaseUncertainty.precision() + 2, - RoundingMode.HALF_EVEN); - if (roundedBaseUncertainty.unscaledValue().intValue() >= 20) { - roundedBaseUncertainty = bigBaseUncertainty - .setScale( - bigBaseUncertainty.scale() - - bigBaseUncertainty.precision() + 1, - RoundingMode.HALF_EVEN); - } - final BigDecimal roundedBaseValue = bigBaseValue.setScale( - roundedBaseUncertainty.scale(), RoundingMode.HALF_EVEN); - - baseValueString = roundedBaseValue.toString(); - baseUncertaintyString = roundedBaseUncertainty.toString(); - } else { - // unused - baseValueString = ""; - baseUncertaintyString = ""; - } - } + // if showUncertainty is true, add brackets around the string + final String valueString = showUncertainty ? "(" + : "" + this.value.toString(showUncertainty) + + (showUncertainty ? ")" : ""); + final String baseValueString = showUncertainty ? "(" + : "" + baseValue.toString(showUncertainty) + + (showUncertainty ? ")" : ""); // create string - if (showUncertainty) { - if (primaryName.isEmpty() && symbol.isEmpty()) - return String.format("(%s ± %s) unnamed unit (= %s ± %s %s)", - valueString, uncertaintyString, baseValueString, - baseUncertaintyString, this.unit.getBase()); - else - return String.format("(%s ± %s) %s", valueString, uncertaintyString, - chosenName); - } else { - // truncate excess zeroes - if (valueString.contains(".")) { - while (valueString.endsWith("0")) { - valueString = valueString.substring(0, valueString.length() - 1); - } - if (valueString.endsWith(".")) { - valueString = valueString.substring(0, valueString.length() - 1); - } - } - - if (baseValueString.contains(".")) { - while (baseValueString.endsWith("0")) { - baseValueString = baseValueString.substring(0, - baseValueString.length() - 1); - } - if (baseValueString.endsWith(".")) { - baseValueString = baseValueString.substring(0, - baseValueString.length() - 1); - } - } - - if (primaryName.isEmpty() && symbol.isEmpty()) - return String.format("%s unnamed unit (= %s %s)", valueString, - baseValueString, this.unit.getBase()); - else - return String.format("%s %s", valueString, chosenName); - } + if (primaryName.isEmpty() && symbol.isEmpty()) + return String.format("%s unnamed unit (= %s %s)", valueString, + baseValueString, this.unit.getBase()); + else + return String.format("%s %s", valueString, chosenName); } } diff --git a/src/org/unitConverter/unit/UnitDatabase.java b/src/org/unitConverter/unit/UnitDatabase.java index 9812bd0..9ca9617 100644 --- a/src/org/unitConverter/unit/UnitDatabase.java +++ b/src/org/unitConverter/unit/UnitDatabase.java @@ -46,6 +46,7 @@ import org.unitConverter.math.ConditionalExistenceCollections; import org.unitConverter.math.DecimalComparison; import org.unitConverter.math.ExpressionParser; import org.unitConverter.math.ObjectProduct; +import org.unitConverter.math.UncertainDouble; /** * A database of units, prefixes and dimensions, and their names. @@ -1134,7 +1135,7 @@ public final class UnitDatabase { // exponent function - first check if o2 is a number, if (exponentValue.canConvertTo(SI.ONE)) { // then check if it is an integer, - final double exponent = exponentValue.getValue(); + final double exponent = exponentValue.getValueExact(); if (DecimalComparison.equals(exponent % 1, 0)) // then exponentiate return base.toExponent((int) (exponent + 0.5)); @@ -1650,7 +1651,8 @@ public final class UnitDatabase { final BigDecimal number = new BigDecimal(name); final double uncertainty = Math.pow(10, -number.scale()); - return LinearUnitValue.of(SI.ONE, number.doubleValue(), uncertainty); + return LinearUnitValue.of(SI.ONE, + UncertainDouble.of(number.doubleValue(), uncertainty)); } catch (final NumberFormatException e) { return LinearUnitValue.getExact(this.getLinearUnit(name), 1); } diff --git a/src/org/unitConverter/unit/UnitTest.java b/src/org/unitConverter/unit/UnitTest.java index ff83805..c0711dc 100644 --- a/src/org/unitConverter/unit/UnitTest.java +++ b/src/org/unitConverter/unit/UnitTest.java @@ -56,9 +56,10 @@ class UnitTest { final LinearUnitValue value4 = LinearUnitValue.getExact(SI.KILOGRAM, 60); // make sure addition is done correctly - assertEquals(51.576, value1.plus(value2).getValue(), 0.001); - assertEquals(15.5, value1.plus(value3).getValue()); - assertEquals(52.076, value1.plus(value2).plus(value3).getValue(), 0.001); + 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) |