Reputation: 24325
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
Reputation: 168232
There are multiple possible interpretations of the term "precision":
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
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
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
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