2

I would like to test a double for a maximum percision of 3 or less. What is the best way to do this in Java?

20.44567567 <- Fail
20.444 <- Pass
20.1 <- Pass
20 <- Pass
Mike Flynn
  • 22,342
  • 54
  • 182
  • 341
  • 2
    The question doesn't make a lot of sense. Internally, `double` values are IEEE-754 floating point values. The concept of precision only applies when converting to a string representation (or to a BCD, such as with `BigDecimal`). Besides, isn't 20.1 the same value as 20.10000? – Ted Hopp Mar 29 '13 at 20:06
  • Use the `BigDecimal` type. There are very handy methods for handling this kind of tasks like - `obj.precision()`, `obj.scale()`. – Lion Mar 29 '13 at 20:08
  • I went ahead and parsed it to a BigDecimal and not a double. – Mike Flynn Mar 29 '13 at 20:18
  • Why would anyone down vote this question, there is some good feedback on here. – Mike Flynn Mar 29 '13 at 21:21
  • Precision is being used in this question in a different way to how the term is used in databases and the typical meaning of arithmetic precision. The example outputs show testing the number of decimal places not the precision as `20.444` has 3 decimal places but has significant digits (precision) of 5. If you are testing for a precision of 3 then `1230000`, `123`, `1.23` and `0.000123` would all pass and `12340000`, `1234`, `1.234` and `0.0001234` would all fail. – MT0 Jan 22 '19 at 14:57

4 Answers4

5

1) Do not use double. Floating point logic is approximated at best. Use BigDecimal instead.

2) I think BigDecimal already has a way of setting a precision. If not, just multiply by 1000 and trunc. Do the operation, get a new number, and compare to the original one. If it is different, fail.

SJuan76
  • 24,532
  • 6
  • 47
  • 87
  • You are correct on the second point, the [`scale`](http://docs.oracle.com/javase/1.5.0/docs/api/java/math/BigDecimal.html#scale()) method will return the number of digits after the decimal point. – Boris the Spider Mar 29 '13 at 20:07
  • I went with this approach and it worked great with the .scale() function. – Mike Flynn Mar 29 '13 at 20:18
  • @boomz : The performance issue is definitely negligible against the issues of using the `double` and/or `float` types. – Lion Mar 29 '13 at 20:26
  • @Lion I am not agree with you. `BigDecimal` is using `String` and it is slower than `double`. The performance of `double` is better than `BigDecimal` for at least 2 times. (more than this!) – boomz Mar 29 '13 at 20:34
  • 1
    @boomz : I didn't say the performance of `BigDecimal` is higher than that of `double`. I just said - the performance issue is avoidable, since `double` and/or `float` types have their issues which are comparatively more critical than performance. – Lion Mar 29 '13 at 20:42
1

This passes your tests:

package com.sandbox;

import org.junit.Test;

import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;

public class SandboxTest {


    @Test
    public void testPrecision() {
        assertFalse(precisionLessThanOrEqualTo3(20.44567567));
        assertTrue(precisionLessThanOrEqualTo3(20.444));
        assertTrue(precisionLessThanOrEqualTo3(20.1));
        assertTrue(precisionLessThanOrEqualTo3(20));
    }

    private boolean precisionLessThanOrEqualTo3(double x) {
        return String.valueOf(x).replaceAll(".*\\.", "").length() <= 3;
    }
}
Daniel Kaplan
  • 62,768
  • 50
  • 234
  • 356
1

You can use the following code:

boolean f (double in){
    if (in*1000 > (float)(int)(in*1000))
        return false;
    return true;
}
boomz
  • 657
  • 5
  • 21
0

There are multiple possible interpretations of the term "precision":

  • The arithmetic precision is the total number of significant figures (before and after the decimal point).
  • The number of digits in the fractional part of the number (or the number of decimal places).
  • Particularly in databases, precision can be measured with a fixed number of decimal places (the scale) included in the count so a NUMBER(6,2) column would store numbers that have at most 6 digits which is always composed of 2 decimal places and 4 integer digits.

For your examples, you appear to be measuring precision as the maximum number of decimal places.

Each of these can be tested using:

import java.math.BigDecimal;

import org.junit.Test;
import static org.junit.Assert.*;


public class Precision
{
  /**
   * Tests to see whether the number has up to the given number of
   * decimal places.
   * 
   * @param value The value to test.
   * @param scale The maximum number of decimal places. 
   * @return      <code>true</code> if the value has up to the
   *              expected number of decimal places.
   */
  static final boolean hasDecimalPlaces(
      final double value,
      final int    scale
  )
  {
    try
    {
      new BigDecimal( Double.toString( value ) ).setScale( scale );
      return true;
    }
    catch ( final ArithmeticException ex )
    {
      return false;
    }
  }

  /**
   * Tests to see whether the number has up to the given number of
   * significant figures.
   * 
   * @param value     The value to test.
   * @param precision The maximum number of significant figures to
   *                  test for.
   * @return          <code>true</code> if the value has up to the
   *                  expected number of significant figures. 
   */
  static final boolean hasSignificantFigures(
      final double value,
      final int    precision
  )
  {
    try
    {
      return new BigDecimal( Double.toString( value ) ).stripTrailingZeros().precision() <= precision;
    }
    catch ( final ArithmeticException ex )
    {
      return false;
    }
  }

  /**
   * Tests to see whether the number has at most the given number of
   * decimal places and, when represented at that maximum number of
   * decimal places, has up to the given number of digits.
   * 
   * @param value     The number to test.
   * @param precision The maximum number of digits to test for.
   * @param scale     The maximum number of decimal places.
   * @return          <code>true</code> if the value can be represented
   *                  at the given scale and, at that scale, is up to
   *                  the given precision.
   */
  static final boolean hasDigitsAtScale(
      final double value,
      final int    precision,
      final int    scale
  )
  {
    try
    {
      return new BigDecimal( Double.toString( value ) ).setScale( scale ).precision() <= precision;
    }
    catch ( final ArithmeticException ex )
    {
      return false;
    }
  }

  @Test
  public void testScale(){
    assertTrue( hasDecimalPlaces( 20d, 3 ) );
    assertTrue( hasDecimalPlaces( 20.123d, 3 ) );
    assertFalse( hasDecimalPlaces( 20.1234d, 3 ) );
  }

  @Test
  public void testPrecision(){
    assertTrue(  hasSignificantFigures(    20d,      3 ) );
    assertTrue(  hasSignificantFigures(   120d,      3 ) );
    assertTrue(  hasSignificantFigures(  1230d,      3 ) );
    assertFalse( hasSignificantFigures( 12340d,      3 ) );
    assertTrue(  hasSignificantFigures(    20.1d,    3 ) );
    assertFalse( hasSignificantFigures(    20.12d,   3 ) );
    assertTrue(  hasSignificantFigures(     0.123d,  3 ) );
    assertFalse( hasSignificantFigures(     0.1234d, 3 ) );
    assertTrue(  hasSignificantFigures(     0.0000999d,  3 ) );
    assertFalse( hasSignificantFigures(     0.00009999d, 3 ) );
  }

  @Test
  public void testPrecisionAndScale(){
    assertTrue(  hasDigitsAtScale(     0d,      3, 0 ) );
    assertFalse( hasDigitsAtScale(     0.01d,   3, 0 ) );
    assertFalse( hasDigitsAtScale(     0.1d,    3, 0 ) );
    assertTrue(  hasDigitsAtScale(     1d,      3, 0 ) );
    assertTrue(  hasDigitsAtScale(    10d,      3, 0 ) );
    assertTrue(  hasDigitsAtScale(   100d,      3, 0 ) );
    assertFalse( hasDigitsAtScale(  1000d,      3, 0 ) );
    assertFalse( hasDigitsAtScale( 10000d,      3, 0 ) );

    assertTrue(  hasDigitsAtScale(     0d,      3, 1 ) );
    assertFalse( hasDigitsAtScale(     0.01d,   3, 1 ) );
    assertTrue(  hasDigitsAtScale(     0.1d,    3, 1 ) );
    assertTrue(  hasDigitsAtScale(     1d,      3, 1 ) );
    assertTrue(  hasDigitsAtScale(    10d,      3, 1 ) );
    assertFalse( hasDigitsAtScale(   100d,      3, 1 ) );
    assertFalse( hasDigitsAtScale(  1000d,      3, 1 ) );
    assertFalse( hasDigitsAtScale( 10000d,      3, 1 ) );

    assertTrue(  hasDigitsAtScale(     0d,      3, -1 ) );
    assertFalse( hasDigitsAtScale(     0.01d,   3, -1 ) );
    assertFalse( hasDigitsAtScale(     0.1d,    3, -1 ) );
    assertFalse( hasDigitsAtScale(     1d,      3, -1 ) );
    assertTrue(  hasDigitsAtScale(    10d,      3, -1 ) );
    assertTrue(  hasDigitsAtScale(   100d,      3, -1 ) );
    assertTrue(  hasDigitsAtScale(  1000d,      3, -1 ) );
    assertFalse( hasDigitsAtScale( 10000d,      3, -1 ) );
  }
}
MT0
  • 143,790
  • 11
  • 59
  • 117