Mike Flynn
Mike Flynn

Reputation: 24325

Test Number For Maximum Precision In Java

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

Upvotes: 2

Views: 3489

Answers (4)

MT0
MT0

Reputation: 168232

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 ) );
  }
}

Upvotes: 0

boomz
boomz

Reputation: 667

You can use the following code:

boolean f (double in){
    if (in*1000 > (float)(int)(in*1000))
        return false;
    return true;
}

Upvotes: 1

Daniel Kaplan
Daniel Kaplan

Reputation: 67440

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;
    }
}

Upvotes: 1

SJuan76
SJuan76

Reputation: 24895

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.

Upvotes: 5

Related Questions