Package org.apache.sis.math

# Class DecimalFunctions

Object
Static
DecimalFunctions

public final class DecimalFunctions extends Static
Functions working on `float` and `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:

- Post-parsing methods `floatToDouble(float)` and `deltaForDoubleToDecimal(double)`:
  - for compensating error when the base 10 representation is considered definitive.
- Pre-formatting methods `fractionDigitsForValue(double)` and `fractionDigitsForDelta(double, boolean)`:
  - for formatting numbers using the exact amount of significant digits for a given precision.

Methods in this class are usually not recommended for intermediate calculations, since base 10 is not more "real" than base 2 for natural phenomenon.

Since:
  0.4
See Also:
  - `MathFunctions​.pow10(int)`
  - `Math​.log10(double)`

Defined in the `sis-utility` module

- ## Method Summary

  Modifier and Type
  Method
  Description
  `static double`
  `deltaForDoubleToDecimal(double value)`
  Returns the difference between the given `double` value and the representation of that value in base 10.
  `static boolean`
  `equalsIgnoreMissingFractionDigits(double accurate, double approximate)`
  Returns `true` if the given numbers or equal or differ only by `accurate` having more non-zero trailing decimal fraction digits than `approximate`.
  `static double`
  `floatToDouble(float value)`
  Converts the given `float` value to a `double` with the extra decimal fraction digits set to zero.
  `static int`
  `floorLog10(double x)`
  Computes `(int) floor(log10(x))`.
  `static int`
  `fractionDigitsForDelta(double accuracy, boolean strict)`
  Returns the number of fraction digits needed for formatting in base 10 numbers of the given accuracy.
  `static int`
  `fractionDigitsForValue(double value)`
  Returns the number of significant fraction digits when formatting the given number in base 10.
  `static int`
  `fractionDigitsForValue(double value, int uncertainDigits)`
  Returns the number of significant fraction digits, potentially minus trailing digits that may be rounding error.

  ### Methods inherited from class Object

  `clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

- ## Method Details

  - ### floatToDouble

    public static double floatToDouble(float value)
    Converts the given `float` value to a `double` with the extra decimal fraction digits set to zero. This is different than the standard cast in the Java language, which set the extra binary fraction digits to zero. For example `(double) 0.1f` gives 0.10000000149011612 while `floatToDouble(0.1f)` returns 0.1.
    Note: This method is not 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 `Float​.parseFloat(String)` (directly or indirectly), and if that call can not be replaced by a call to `Double​.parseDouble(String)` (for example because the original `String` is not available anymore), then this method may be useful if one consider the `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.
    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:

    >     return Double.parseDouble(Float.toString(value));

    Parameters:
      `value` - the `float` value to convert as a `double`.
    Returns:
      the given value as a `double` with the extra decimal fraction digits set to zero.

  - ### deltaForDoubleToDecimal

    public static double deltaForDoubleToDecimal(double value)
    Returns the difference between the given `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 approximately equivalent to the following code except that it is potentially faster since the actual implementation avoids the creation of `BigDecimal` objects:

    >     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 `value + deltaForDoubleToDecimal(value)` has no effect since the absolute value of the returned delta is always smaller than `Math​.ulp(value) / 2`. To see an effect, a type with more precision than the `double` type is necessary.
    Use case: 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.

    #### Domain of validity

    The current implementation can not compute delta for `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 `NaN` because of insufficient algorithm accuracy. This limitation may change in any future SIS version if we find a better algorithm.
    Parameters:
      `value` - the value for which to get the delta compared to its base 10 representation.
    Returns:
      the delta that would need to be added to the given `double` value for getting a result closer to its base 10 representation, or `NaN` if it can not be computed.

  - ### fractionDigitsForDelta

    public static int fractionDigitsForDelta(double accuracy, boolean strict)
    Returns the number of fraction digits needed for formatting in base 10 numbers of the given accuracy. If the `strict` argument is `true`, then for any given `accuracy` this method returns a value n such as the difference between adjacent numbers formatted in base 10 with n fraction digits will always be equal or smaller than `accuracy`. Examples:
    - `fractionDigitsForDelta(0.001, true)` returns 3.
    - `fractionDigitsForDelta(0.009, true)` returns 3.
    - `fractionDigitsForDelta(0.010, true)` returns 2.
    - `fractionDigitsForDelta(0.099, true)` returns 3 (special case).

    Special cases:

    - If `accuracy` is `NaN` or infinity, then this method returns 0 since those values are not represented by decimal digits.
    - If `accuracy` is 0, then this method returns 324 since 10-324 is the first power of 10 smaller than the minimal strictly positive `double` value (4.9E-324).
      Note: The above value can be understood in an other way: if the first 324 fraction digits are zero, then the IEEE `double` value is guaranteed to be rounded to exactly 0 no matter what the next fraction digits are.
    - If `accuracy` is greater than 1, then this method returns the number of "unnecessary" trailing zeros as a negative number.
      Example: `fractionDigitsForDelta(100, …)` returns -2.
    - If the first non-zero digits of `accuracy` are equal or greater than 95 (e.g. 0.00099) and the `strict` argument is `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.
      Example: If `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.
    Invoking this method is equivalent to computing `(int) -floor(log10(accuracy))` except for the 0, `NaN`, infinities and `0.…95` special cases.
    Parameters:
      `accuracy` - the desired accuracy of numbers to format in base 10.
      `strict` - `true` for checking the `0.…95` special case. If `false`, then the difference between adjacent formatted numbers is not guaranteed to be smaller than `accuracy` in every cases.
    Returns:
      number of fraction digits needed for formatting numbers with the given accuracy. May be negative.
    See Also:
      - `NumberFormat​.setMaximumFractionDigits(int)`

  - ### fractionDigitsForValue

    public static int fractionDigitsForValue(double value)
    Returns the number of significant fraction digits when formatting the given number in base 10. This method does not ignore trailing zeros. For example `fractionDigitsForValue(1.0)` returns 16, because the `double` format can store almost 16 decimal digits after 1.
    Note: We said almost because the very last digit may be able to store only a subset of the [0 … 9] digits.
    Invoking this method is equivalent to invoking `fractionDigitsForDelta(Math.ulp(value), false)`, except that it is potentially faster.

    Special cases:

    - If `value` is `NaN` or infinity, then this method returns 0 since those values are not represented by decimal digits.
    - If `value` is 0, then this method returns 324 since `Math​.ulp(0)` = 4.9E-324.
    Example: This method is useful with `NumberFormat` for formatting all significant digits of a `double` value, padding with trailing zeros if necessary, but no more than necessary.
    Parameters:
      `value` - the value for which to get the number of significant fraction digits.
    Returns:
      the number of significant fraction digits (may be negative), or 0 if `value` is NaN or infinity.
    See Also:
      - `NumberFormat​.setMinimumFractionDigits(int)`

  - ### fractionDigitsForValue

    public static int fractionDigitsForValue(double value, int uncertainDigits)
    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 `fractionDigitsForValue(double)`. Then there is a choice:
    - If after rounding the given `value` to an amount of fraction digits given by (`fractionDigits` - `uncertainDigits`) the 4 last fraction digits before the rounded ones are zero, then this method returns `fractionDigits` - `uncertainDigits`.
    - Otherwise this method returns `fractionDigits`.
    Note: The threshold of 4 trailing fraction digits is arbitrary and may change in any future SIS version.
    Examples:
    - `fractionDigitsForValue(179.12499999999824)` returns 14, the amount of digits after the decimal separator.
    - `fractionDigitsForValue(179.12499999999824, 3)` returns 11 because rounding the 3 last digits (i.e. rounding after the 11th digit) results in 179.125000000000. Since the 4 last fraction digits are zero, the condition for allowing that rounding is met.
    - `fractionDigitsForValue(179.12499999999824, 2)` returns 14 because rounding the 2 last digits (i.e. rounding after the 12th digit) results in 179.124999999998. The condition for 4 trailing zero fraction digits is not met.
    - `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.
    Parameters:
      `value` - the value for which to get the number of significant fraction fraction digits minus rounding error.
      `uncertainDigits` - number of trailing fraction digits which may be rounding error artefacts.
    Returns:
      suggested number of significant digits (may be negative), or 0 if `value` is NaN or infinity.

  - ### floorLog10

    public static int floorLog10(double x)
    Computes `(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.
    Parameters:
      `x` - the value for which to compute the logarithm. Must be greater than zero.
    Returns:
      logarithm of the given value, rounded toward zero.
    Throws:
      `ArithmeticException` - if the given value is zero, negative, infinity or NaN.
    Since:
      1.0
    See Also:
      - `MathFunctions​.pow10(int)`

  - ### equalsIgnoreMissingFractionDigits

    public static boolean equalsIgnoreMissingFractionDigits(double accurate, double approximate)
    Returns `true` if the given numbers or equal or differ only by `accurate` having more non-zero trailing decimal fraction digits than `approximate`.

    Examples
    Accurate | Approximate | Result | Comment
    --- | --- | --- | ---
    0.123456 | 0.123 | true | Differ in digits not specified by `approximate`.
    0.123456 | 0.123000 | true | This method can not distinguish missing digits from trailing zeros.
    0.123456 | 0.123001 | false | No missing digits, and some of them differ.
    0.123 | 0.123456 | false | `approximate` and `accurate` can not be interchanged.

    Use case: this method is useful when `approximate` is a number parsed by `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:

    >     AngleUnit["degree", 0.017453292519943295]      // Expected
    >     AngleUnit["degree", 0.01745329252]             // Given by some providers

    Parameters:
      `accurate` - the most accurate number.
      `approximate` - the number which may have missing decimal fraction digits.
    Returns:
      whether the two number are equal, ignoring missing decimal fraction digits in `approximate`.
    Since:
      1.0