| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.sis.math; |
| |
| import org.apache.sis.util.Static; |
| import org.apache.sis.util.Workaround; |
| import org.apache.sis.util.ArgumentChecks; |
| import org.apache.sis.internal.util.Numerics; |
| |
| import static org.apache.sis.internal.util.Numerics.SIGNIFICAND_SIZE; |
| |
| |
| /** |
| * Functions working on {@code float} and {@code double} values while taking in account their representation in base 10. |
| * Methods in this class may be helpful when used after parsing or before formatting a number in base 10: |
| * |
| * <ul> |
| * <li>Post-parsing methods {@link #floatToDouble(float)} and {@link #deltaForDoubleToDecimal(double)}: |
| * <ul> |
| * <li>for compensating error when the base 10 representation is considered <cite>definitive</cite>.</li> |
| * </ul> |
| * </li> |
| * <li>Pre-formatting methods {@link #fractionDigitsForValue(double)} and |
| * {@link #fractionDigitsForDelta(double, boolean)}: |
| * <ul> |
| * <li>for formatting numbers using the exact amount of significant digits for a given precision.</li> |
| * </ul> |
| * </li> |
| * </ul> |
| * |
| * Methods in this class are usually <strong>not</strong> recommended for intermediate calculations, |
| * since base 10 is not more "real" than base 2 for natural phenomenon. |
| * |
| * @author Martin Desruisseaux (Geomatys) |
| * @version 1.0 |
| * |
| * @see MathFunctions#pow10(int) |
| * @see Math#log10(double) |
| * |
| * @since 0.4 |
| * @module |
| */ |
| public final class DecimalFunctions extends Static { |
| /** |
| * The greatest power of 10 such as {@code Math.pow(10, EXPONENT_FOR_ZERO) == 0}. |
| * This is the exponent in {@code parseDouble("1E-324")} < {@link Double#MIN_VALUE}, |
| * which is stored as zero because non-representable as a {@code double} value. |
| * The next power, {@code parseDouble("1E-323")}, is a non-zero {@code double} value. |
| * |
| * @see Double#MIN_VALUE |
| */ |
| static final int EXPONENT_FOR_ZERO = -324; |
| |
| /** |
| * The maximal exponent value such as {@code parseDouble("1E+308")} still a finite number. |
| * |
| * @see Double#MAX_VALUE |
| */ |
| static final int EXPONENT_FOR_MAX = 308; |
| |
| /** |
| * Table of integer powers of 10, precomputed both for performance and accuracy reasons. |
| * This table consumes 4.9 kb of memory. We pay this cost because integer powers of ten |
| * are requested often, and {@link Math#pow(double, double)} has slight rounding errors. |
| * |
| * @see #pow10(int) |
| */ |
| @Workaround(library="JDK", version="1.4") |
| private static final double[] POW10 = new double[EXPONENT_FOR_MAX - EXPONENT_FOR_ZERO]; |
| static { |
| final StringBuilder buffer = new StringBuilder("1E"); |
| for (int i=0; i<POW10.length; i++) { |
| buffer.setLength(2); |
| buffer.append(i + (EXPONENT_FOR_ZERO + 1)); |
| /* |
| * Double.parseDouble("1E"+i) gives as good or better numbers than Math.pow(10,i) |
| * for ALL integer powers, but is slower. We hope that the current workaround is only |
| * temporary. See http://developer.java.sun.com/developer/bugParade/bugs/4358794.html |
| */ |
| POW10[i] = Double.parseDouble(buffer.toString()); |
| } |
| } |
| |
| /** |
| * Do not allow instantiation of this class. |
| */ |
| private DecimalFunctions() { |
| } |
| |
| /** |
| * Computes 10 raised to the power of <var>x</var>. This is the implementation of the |
| * public {@link MathFunctions#pow10(int)} method, defined here in order to allow the |
| * JVM to initialize the {@link #POW10} table only when first needed. |
| * |
| * @param x the exponent. |
| * @return 10 raised to the given exponent. |
| */ |
| static double pow10(int x) { |
| x -= EXPONENT_FOR_ZERO + 1; |
| return (x >= 0) ? (x < POW10.length ? POW10[x] : Double.POSITIVE_INFINITY) : 0; |
| } |
| |
| /** |
| * Converts the given {@code float} value to a {@code double} with the extra <em>decimal</em> fraction digits |
| * set to zero. This is different than the standard cast in the Java language, which set the extra <em>binary</em> |
| * fraction digits to zero. |
| * For example {@code (double) 0.1f} gives 0.10000000149011612 while {@code floatToDouble(0.1f)} returns 0.1. |
| * |
| * <div class="note"><b>Note:</b> |
| * This method is <strong>not</strong> more accurate than the standard Java cast – it should be used only when |
| * the base 10 representation of the given value may be of special interest. If the value come from a call to |
| * {@link Float#parseFloat(String)} (directly or indirectly), and if that call can not be replaced by a call to |
| * {@link Double#parseDouble(String)} (for example because the original {@code String} is not available anymore), |
| * then this method may be useful if one consider the {@code String} representation in base 10 as definitive. |
| * But if the value come from an instrument measurement or a calculation, then there is probably no reason to use |
| * this method because base 10 is not more "real" than base 2 or any other base for natural phenomenon.</div> |
| * |
| * This method is equivalent to the following code, except that it is potentially faster since the |
| * actual implementation avoid to format and parse the value: |
| * |
| * {@preformat java |
| * return Double.parseDouble(Float.toString(value)); |
| * } |
| * |
| * @param value the {@code float} value to convert as a {@code double}. |
| * @return the given value as a {@code double} with the extra decimal fraction digits set to zero. |
| */ |
| public static double floatToDouble(final float value) { |
| /* |
| * Decompose value == m × 2^e where m and e are integers. If the exponent is not negative, then |
| * there is no fractional part in the value, in which case there is no rounding error to fix. |
| * (Note: NaN and infinities also have exponent greater than zero). |
| */ |
| final int e = Math.getExponent(value) - Numerics.SIGNIFICAND_SIZE_OF_FLOAT; |
| if (e >= 0) { |
| return value; // Integer, infinity or NaN. |
| } |
| final int m = Numerics.getSignificand(value); |
| assert Math.scalb((float) m, e) == Math.abs(value) : value; |
| /* |
| * Get the factor c for converting the significand m from base 2 to base 10, such as: |
| * |
| * m × (2 ^ e) == m × c × (10 ^ -e₁₀) |
| * |
| * where e₁₀ is the smallest exponent which allow to represent the value without precision lost when (m × c) |
| * is rounded to an integer. Because the number of significant digits in base 2 does not correspond to an |
| * integer number of significand digits in base 10, we have slightly more precision than what the 'float' |
| * value had: we have something between 0 and 1 extraneous digits. |
| * |
| * Note: the conversation factor c is also equals to 1 ULP converted to the units of (m × c). |
| */ |
| final int e10 = -Numerics.toExp10(e); // Range: [0 … 45] inclusive. |
| final double c = Math.scalb(pow10(e10), e); // Range: (1 … 10) exclusive. |
| final double mc = m * c; // Only integer part is meaningful. |
| /* |
| * First, presume that our representation in base 10 has one extranous digit, so we will round |
| * to the tens instead of unities. If the difference appears to not be smaller than half a ULP, |
| * then the last digit was not extranous - we need to keep it. |
| */ |
| double r = Math.rint(mc / 10) * 10; |
| if (Math.abs(r - mc) >= c/2) { |
| r = Math.rint(mc); |
| } |
| r = Math.copySign(Math.scalb(r / c, e), value); |
| assert value == (float) r : value; |
| return r; |
| } |
| |
| /** |
| * Returns the difference between the given {@code double} value and the representation of that value in base 10. |
| * For any value in the method's domain of validity (defined below), this method is <em>approximately</em> |
| * equivalent to the following code except that it is potentially faster since the actual implementation |
| * avoids the creation of {@link java.math.BigDecimal} objects: |
| * |
| * {@preformat java |
| * BigDecimal base2 = new BigDecimal(value); // Exact same value as stored in IEEE 754 format. |
| * BigDecimal base10 = BigDecimal.valueOf(value); // Exact same value as shown by println(value). |
| * return base10.subtract(base2).doubleValue(); |
| * } |
| * |
| * Computing {@code value + deltaForDoubleToDecimal(value)} has no effect since the absolute value of the |
| * returned delta is always smaller than <code>{@linkplain Math#ulp(double) Math.ulp}(value) / 2</code>. |
| * To see an effect, a type with more precision than the {@code double} type is necessary. |
| * |
| * <div class="note"><b>Use case:</b> |
| * Many international standards define values in base 10. For example the conversion factor from inches |
| * to centimetres is defined as exactly 2.54 cm/inch. This is by an internationally accepted definition |
| * since 1959, not an approximation. But the 2.54 value can not be represented exactly in the IEEE 754 |
| * format – the error is approximately 3.6E-17 cm/inch. In the vast majority of cases such tiny error |
| * can be ignored. But in situations where it is desirable to have an error estimation |
| * (e.g. in non-linear equations where errors can grow exponentially), this method can be useful. |
| * Other examples of values defined in base 10 are conversions from feet to metres and |
| * map projection parameters defined by national mapping agencies. |
| * </div> |
| * |
| * <h4>Domain of validity</h4> |
| * The current implementation can not compute delta for {@code abs(value) < 3E-8} approximately, |
| * except for the 0 value which is supported. For any non-zero value closer to zero than the 3E-8 |
| * threshold, this method returns {@code NaN} because of insufficient algorithm accuracy. |
| * This limitation may change in any future SIS version if we find a better algorithm. |
| * |
| * @param value the value for which to get the delta compared to its base 10 representation. |
| * @return the delta that would need to be added to the given {@code double} value for getting a result |
| * closer to its base 10 representation, or {@link Double#NaN NaN} if it can not be computed. |
| */ |
| public static double deltaForDoubleToDecimal(final double value) { |
| /* |
| * Decompose value == m × 2^e where m and e are integers, then get the |
| * factor c for converting the significand m from base 2 to base 10 such as: |
| * |
| * m × (2 ^ e) == m × c × (10 ^ -e₁₀) |
| */ |
| final int e = Math.getExponent(value) - SIGNIFICAND_SIZE; |
| if (e >= 0) { |
| return 0; // Integer, infinity or NaN. |
| } |
| if (e < -24 - SIGNIFICAND_SIZE) { // 2.9802322E-8 threshold found empirically. |
| return (e == -1075) ? 0 : Double.NaN; // Returns 0 for the 0 value, NaN for all others. |
| } |
| final long m = Numerics.getSignificand(value); |
| assert Math.scalb((double) m, e) == Math.abs(value) : value; |
| final int e10 = -Numerics.toExp10(e); // Range: [0 … 324] inclusive. |
| /* |
| * If we were continuing with the same strategy than in floatToDouble(float), we would compute: |
| * |
| * c = Math.scalb(pow10(e10), e); // Range: (1 … 10) exclusive. |
| * |
| * Unfortunately this would require a floating point type with twice the accuracy of 'double', |
| * which we don't have. Instead, we will apply a trick with integer arithmetic. But before to |
| * use integers, we will need to multiply 'c' by a scale factor high enough for promoting all |
| * significant fraction digits to the integer part. This factor is 2^56, explanation follows: |
| * |
| * For reasons explained later we will actually use c/10 instead of c, so the range will be |
| * (0.1 … 1) instead of (1 … 10). The exponent of 0.1 in base 2 is -4. Consequently for any |
| * value between 0.1 and 1, scaling the value by 56 binary places guarantee that the result |
| * will be equals or greater than 2^52. At that threshold, 'double' values can not have |
| * fraction digits. |
| */ |
| final int PRECISION = SIGNIFICAND_SIZE + 4; // Number of bits to use for scaling to integers. |
| double cs = Math.scalb(pow10(e10 - 1), e + PRECISION); // Range: (0.1 × 2^56 … 2^56) exclusive. |
| /* |
| * This is where magic happen: the following multiplication overflow (we would need a 128 bits integer |
| * for representing it), but we don't care because we are interrested only in the fraction digits (the |
| * 56 lower bits because of the scaling discussed in previous comment). In integer arithmetic, the low |
| * bits are always valid even if the multiplication overflow. |
| */ |
| final long ci = (long) cs; |
| long mc = (m * ci) & ((1L << PRECISION) - 1); |
| /* |
| * Because we used c/10 instead than c, the first (leftmost) decimal digit is potentially the last |
| * (rightmost) decimal digit of 'value'. Whether it is really the last 'value' digit or not depends |
| * on the magnitude of last decimal digit compared to 1 ULP. The last digit is: |
| * |
| * lastDigit = (mc * 10) >>> PRECISION; |
| * |
| * The above digit is guaranteed to be in the [0 … 9] range. We will wraparound in the [-5 … 4] range |
| * because the delta must be no more than ±0.5 ULP of the 'value' argument. |
| */ |
| if (mc >= (5L << PRECISION) / 10) { // The 0.5 × 2^56 threshold. |
| mc -= (1L << PRECISION); // Shift [5 … 9] digits to [-5 … -1]. |
| } |
| /* |
| * At this point, 'mc' is less than 0.5 ULP if the last decimal digits were zero. |
| * But if the 'value' argument uses the full precision range of the 'double' type, |
| * then we still have 'mc' greater than 0.5 ULP is some cases. This difficulty happens |
| * because 52 binary digits do not correspond to an integer number of decimal digits. |
| * |
| * For now, I didn't found a better strategy than choosing the last decimal digit in such |
| * a way that 'mc' is as small as possible. We don't really care what this last digit is; |
| * we care only about the remainder after we removed the effet of that last digit. |
| */ |
| if (Math.abs(mc) >= ci/2) { |
| mc %= (1L << PRECISION) / 10; // Remove the effect of last decimal digit. |
| if (mc >= 0) { // Check if changing the sign would make it smaller. |
| if (mc >= (1L << PRECISION) / 20) { |
| mc -= (1L << PRECISION) / 10; |
| } |
| } else { |
| if (mc < (-1L << PRECISION) / 20) { |
| mc += (1L << PRECISION) / 10; |
| } |
| } |
| } |
| /* |
| * We are done: unscale and return. |
| */ |
| final double delta = MathFunctions.xorSign(-Math.scalb(mc / cs, e), value); |
| assert Math.abs(delta) <= Math.ulp(value) / 2 : value; |
| return delta; |
| } |
| |
| /** |
| * Returns the number of fraction digits needed for formatting in base 10 numbers of the given |
| * accuracy. If the {@code strict} argument is {@code true}, then for any given {@code accuracy} |
| * this method returns a value <var>n</var> such as the difference between adjacent numbers |
| * formatted in base 10 with <var>n</var> fraction digits will always be equal or smaller |
| * than {@code accuracy}. Examples: |
| * |
| * <ul> |
| * <li>{@code fractionDigitsForDelta(0.001, true)} returns 3.</li> |
| * <li>{@code fractionDigitsForDelta(0.009, true)} returns 3.</li> |
| * <li>{@code fractionDigitsForDelta(0.010, true)} returns 2.</li> |
| * <li>{@code fractionDigitsForDelta(0.099, true)} returns 3 (special case).</li> |
| * </ul> |
| * |
| * <p>Special cases:</p> |
| * <ul> |
| * <li>If {@code accuracy} is {@link Double#NaN NaN} or infinity, then this method returns 0 |
| * since those values are not represented by decimal digits.</li> |
| * |
| * <li>If {@code accuracy} is 0, then this method returns 324 since 10<sup>-324</sup> is the first power of 10 |
| * smaller than the minimal strictly positive {@code double} value ({@value java.lang.Double#MIN_VALUE}). |
| * |
| * <div class="note"><b>Note:</b> |
| * The above value can be understood in an other way: if the first 324 fraction digits are zero, |
| * then the IEEE {@code double} value is guaranteed to be rounded to exactly 0 no matter what the |
| * next fraction digits are.</div></li> |
| * |
| * <li>If {@code accuracy} is greater than 1, then this method returns |
| * the number of "unnecessary" trailing zeros as a negative number. |
| * |
| * <div class="note"><b>Example:</b> {@code fractionDigitsForDelta(100, …)} returns -2.</div></li> |
| * |
| * <li>If the first non-zero digits of {@code accuracy} are equal or greater than 95 |
| * (e.g. 0.00099) and the {@code strict} argument is {@code true}, then this method |
| * increases the number of needed fraction digits in order to prevent the rounded |
| * number to be collapsed into the next integer value. |
| * |
| * <div class="note"><b>Example:</b> |
| * If {@code accuracy} is 0.95, then a return value of 1 is not sufficient since |
| * the rounded value of 0.95 with 1 fraction digit would be 1.0. Such value would be a |
| * violation of this method contract since the difference between 0 and that formatted |
| * value would be greater than the accuracy. Note that this is not an artificial rule; |
| * this is related to the fact that 0.9999… is mathematically strictly equals to 1.</div></li> |
| * </ul> |
| * |
| * Invoking this method is equivalent to computing <code>(int) |
| * -{@linkplain Math#floor(double) floor}({@linkplain Math#log10(double) log10}(accuracy))</code> |
| * except for the 0, {@code NaN}, infinities and {@code 0.…95} special cases. |
| * |
| * @param accuracy the desired accuracy of numbers to format in base 10. |
| * @param strict {@code true} for checking the {@code 0.…95} special case. |
| * If {@code false}, then the difference between adjacent formatted numbers is not |
| * guaranteed to be smaller than {@code accuracy} in every cases. |
| * @return number of fraction digits needed for formatting numbers with the given accuracy. |
| * May be negative. |
| * |
| * @see java.text.NumberFormat#setMaximumFractionDigits(int) |
| */ |
| public static int fractionDigitsForDelta(double accuracy, final boolean strict) { |
| accuracy = Math.abs(accuracy); |
| int i = MathFunctions.getExponent(accuracy); |
| if (i == Double.MAX_EXPONENT + 1) { |
| return 0; // NaN or infinities. |
| } |
| i = Numerics.toExp10(i); |
| if (accuracy >= pow10(i+1)) { |
| i++; |
| } |
| i = -i; |
| if (strict) { |
| double scale = pow10(i); |
| while ((accuracy *= scale) >= 9.5) { |
| i++; // The 0.…95 special case. |
| accuracy -= Math.floor(accuracy); |
| scale = 10; |
| } |
| } |
| return i; |
| } |
| |
| /** |
| * Returns the number of significant fraction digits when formatting the given number in base 10. |
| * This method does <strong>not</strong> ignore trailing zeros. |
| * For example {@code fractionDigitsForValue(1.0)} returns 16, |
| * because the {@code double} format can store <i>almost</i> 16 decimal digits after 1. |
| * |
| * <div class="note"><b>Note:</b> |
| * We said <cite>almost</cite> because the very last digit may be able to store only a subset |
| * of the [0 … 9] digits.</div> |
| * |
| * Invoking this method is equivalent to invoking <code>{@linkplain #fractionDigitsForDelta(double, boolean) |
| * fractionDigitsForDelta}(Math.{@linkplain Math#ulp(double) ulp}(value), false)</code>, except that it is |
| * potentially faster. |
| * |
| * <p>Special cases:</p> |
| * <ul> |
| * <li>If {@code value} is {@link Double#NaN NaN} or infinity, then this method returns 0 |
| * since those values are not represented by decimal digits.</li> |
| * |
| * <li>If {@code value} is 0, then this method returns 324 since |
| * {@code Math.ulp(0)} = {@value java.lang.Double#MIN_VALUE}.</li> |
| * </ul> |
| * |
| * <div class="note"><b>Example:</b> |
| * This method is useful with {@link java.text.NumberFormat} for formatting all significant digits of a |
| * {@code double} value, padding with trailing zeros if necessary, but no more than necessary.</div> |
| * |
| * @param value the value for which to get the number of significant fraction digits. |
| * @return the number of significant fraction digits (may be negative), or 0 if {@code value} is NaN or infinity. |
| * |
| * @see java.text.NumberFormat#setMinimumFractionDigits(int) |
| */ |
| public static int fractionDigitsForValue(final double value) { |
| /* |
| * We really need Math.getExponent(value) here rather than MathFunctions.getExponent(value). |
| * What we actually want is MathFunctions.getExponent(Math.ulp(value)), but we get the same |
| * result more efficiently if we replace the call to Math.ulp(double) by a SIGNIFICAND_SIZE |
| * subtraction in the exponent, provided that the exponent has NOT been corrected for sub- |
| * normal numbers (in order to reproduce the Math.ulp behavior). |
| */ |
| final int exponent = Math.getExponent(value); |
| if (exponent <= Double.MAX_EXPONENT) { // Exclude NaN and ±∞ cases. |
| return -Numerics.toExp10(exponent - SIGNIFICAND_SIZE); |
| } |
| return 0; |
| } |
| |
| /** |
| * Returns the number of significant fraction digits, potentially minus trailing digits that may be rounding error. |
| * First, this method gets the number of fraction digits as of {@link #fractionDigitsForValue(double)}. Then there |
| * is a choice: |
| * |
| * <ul> |
| * <li>If after rounding the given {@code value} to an amount of fraction digits given by ({@code fractionDigits} |
| * - {@code uncertainDigits}) the 4 last fraction digits before the rounded ones are zero, then this method |
| * returns {@code fractionDigits} - {@code uncertainDigits}.</li> |
| * <li>Otherwise this method returns {@code fractionDigits}.</li> |
| * </ul> |
| * |
| * <div class="note"><b>Note:</b> |
| * The threshold of 4 trailing fraction digits is arbitrary and may change in any future SIS version.</div> |
| * |
| * <div class="note"><b>Examples:</b> |
| * <ul> |
| * <li>{@code fractionDigitsForValue(179.12499999999824)} returns 14, |
| * the amount of digits after the decimal separator.</li> |
| * <li>{@code fractionDigitsForValue(179.12499999999824, 3)} returns 11 because rounding the 3 last digits |
| * (i.e. rounding after the 11<sup>th</sup> digit) results in 179.125000000000. |
| * Since the 4 last fraction digits are zero, the condition for allowing that rounding is met.</li> |
| * <li>{@code fractionDigitsForValue(179.12499999999824, 2)} returns 14 because rounding the 2 last digits |
| * (i.e. rounding after the 12<sup>th</sup> digit) results in 179.124999999998. |
| * The condition for 4 trailing zero fraction digits is not met.</li> |
| * <li>{@code fractionDigitsForValue(179.12499997999999, 3)} returns 14 because rounding the 3 last digits |
| * results in 179.12499997000. The condition for 4 trailing zero fraction digits is not met.</li> |
| * </ul> |
| * </div> |
| * |
| * @param value the value for which to get the number of significant fraction fraction digits minus rounding error. |
| * @param uncertainDigits number of trailing fraction digits which may be rounding error artefacts. |
| * @return suggested number of significant digits (may be negative), or 0 if {@code value} is NaN or infinity. |
| */ |
| public static int fractionDigitsForValue(double value, final int uncertainDigits) { |
| ArgumentChecks.ensurePositive("uncertainDigits", uncertainDigits); |
| int digits = fractionDigitsForValue(value); |
| final int reduced = digits - uncertainDigits; |
| if (reduced > 0) { |
| value *= pow10(reduced); |
| if ((Math.rint(value) % 10000) == 0) { |
| return reduced; |
| } |
| } |
| return digits; |
| } |
| |
| /** |
| * Computes {@code (int) floor(log10(x))}. For values greater than one, this is the number of digits - 1 |
| * in the decimal representation of the given number. For values smaller than one, this is the number of |
| * fraction digits required for showing the first non-zero decimal digit. |
| * |
| * @param x the value for which to compute the logarithm. Must be greater than zero. |
| * @return logarithm of the given value, rounded toward zero. |
| * @throws ArithmeticException if the given value is zero, negative, infinity or NaN. |
| * |
| * @see MathFunctions#pow10(int) |
| * |
| * @since 1.0 |
| */ |
| public static int floorLog10(final double x) { |
| if (x > 0) { |
| int p = Numerics.toExp10(MathFunctions.getExponent(x)); // Rounded twice toward floor (may be too low). |
| final int i = p - EXPONENT_FOR_ZERO; // Convert to index in POW10 array + 1. |
| if (i >= 0 && i < POW10.length) { |
| if (POW10[i] <= x) p++; // If p is too low, adjust. |
| return p; |
| } |
| } |
| throw new ArithmeticException(String.valueOf(x)); |
| } |
| |
| /** |
| * Returns {@code true} if the given numbers or equal or differ only by {@code accurate} |
| * having more non-zero trailing decimal fraction digits than {@code approximate}. |
| * |
| * <table class="sis"> |
| * <caption>Examples</caption> |
| * <tr><th>Accurate</th> <th>Approximate</th> <th>Result</th> <th>Comment</th></tr> |
| * <tr><td>0.123456</td> <td>0.123</td> <td>true</td> <td>Differ in digits not specified by {@code approximate}.</td></tr> |
| * <tr><td>0.123456</td> <td>0.123000</td> <td>true</td> <td>This method can not distinguish missing digits from trailing zeros.</td></tr> |
| * <tr><td>0.123456</td> <td>0.123001</td> <td>false</td> <td>No missing digits, and some of them differ.</td></tr> |
| * <tr><td>0.123</td> <td>0.123456</td> <td>false</td> <td>{@code approximate} and {@code accurate} can not be interchanged.</td></tr> |
| * </table> |
| * |
| * <div class="note"><b>Use case:</b> |
| * this method is useful when {@code approximate} is a number parsed by {@link Double#parseDouble(String)} |
| * and the data producer may have rounded too many fraction digits when formatting the numbers. |
| * In some cases we can suspect what the real value may be and want to ensure that a replacement |
| * would not contradict the provided value. This happen for example in Well Known Text format, |
| * where the following element is sometime written with the conversion factor rounded: |
| * |
| * {@preformat wkt |
| * AngleUnit["degree", 0.017453292519943295] // Expected |
| * AngleUnit["degree", 0.01745329252] // Given by some providers |
| * } |
| * </div> |
| * |
| * @param accurate the most accurate number. |
| * @param approximate the number which may have missing decimal fraction digits. |
| * @return whether the two number are equal, ignoring missing decimal fraction digits in {@code approximate}. |
| * |
| * @since 1.0 |
| */ |
| public static boolean equalsIgnoreMissingFractionDigits(double accurate, double approximate) { |
| final double delta = Math.abs(accurate - approximate); |
| if (delta < 1) { |
| /* |
| * Compute the position of the first digit that differ, expressed as a power of 10. |
| * For example if the numbers are 0.123 and 0.12378, then the first digit to differ |
| * is 7 at position 10⁻⁴. Consequently the position of the last same digit is 10⁻³. |
| * Dividing numbers by that last position result in numbers where all the different |
| * digits are fraction digits (123 and 123.78 in above example). |
| */ |
| int p = Numerics.toExp10(MathFunctions.getExponent(delta)); // Rounded twice toward floor (may be too low). |
| p = Math.max(p - (EXPONENT_FOR_ZERO + 1), 0); // Convert to index in POW10 array. |
| if (p+1 < POW10.length && POW10[p+1] <= delta) p++; // If p was too low, adjust. |
| p = (-2*EXPONENT_FOR_ZERO - 3) - p; // Index of power of opposite sign - 1. |
| if (p >= 0 && p < POW10.length) { |
| double scale = POW10[p]; // Factor for moving difference to fraction digits. |
| assert delta*scale >= 0.1 : delta; |
| final double diffInFractions = approximate * scale; |
| /* |
| * The difference should not be in any digit provided by 'approximate'. |
| * This means that after we moved the difference in fraction digits, |
| * the approximate number should have no such fractions. We use 1 ULP |
| * tolerance because the string representation of 'double' type has a |
| * 0.5 ULP accuracy and the multiplication adds a 0.5 ULP rounding error. |
| */ |
| approximate = Math.rint(diffInFractions); |
| if (Math.abs(approximate - diffInFractions) <= Math.ulp(diffInFractions)) { |
| /* |
| * At this point we determined that all difference (now stored as fraction digits) |
| * are decimal fraction digits that were not specified in the approximate number. |
| * We will compare the approximate number with the accurate one ignoring those digits, |
| * but before doing so we may need to adjust too aggressive scale factor. For example |
| * if the approximate number is 0.123 and the accurate one is 0.123004, then the scale |
| * factor of 100000 is too aggressive; it should be 1000. |
| */ |
| while (approximate % 10 == 0 && scale >= 10) { |
| approximate /= 10; |
| scale /= 10; |
| } |
| accurate *= scale; |
| return Math.abs(approximate - accurate) <= 0.5; |
| } |
| } |
| } |
| return Double.doubleToLongBits(accurate) == Double.doubleToLongBits(approximate); |
| } |
| } |