I need to implement a small metric converter class which has the main task to convert metrics. For example convert meters to miles. An additional acceptance criteria is to round the results with one digit accuracy except if the number is less than 0.1, then it should be rounded to 2 digits of accuracy.
I have programmed for rounding following method:
public static String round(double input) {
DecimalFormat df;
if(input < 0.1) {
df = new DecimalFormat("#.##");
} else {
df = new DecimalFormat("#.#");
}
df.setRoundingMode(RoundingMode.HALF_UP);
Locale currentLocale = LocaleContextHolder.getLocale();
df.setDecimalFormatSymbols(DecimalFormatSymbols.getInstance(currentLocale));
return df.format(input);
}
Now i wrote some unit tests in order to validate the code.
package de.helper;
import org.junit.jupiter.api.Test;
import org.springframework.context.i18n.LocaleContextHolder;
import java.util.Locale;
import static org.assertj.core.api.Assertions.assertThat;
class MetricsConverterTest {
@Test
public void roundNumberWith1DigitAccuracy() {
//arrange
LocaleContextHolder.setLocale(Locale.GERMAN);
//act
String actualRoundDown = MetricsConverter.round(10.123456);
String actualRoundUp = MetricsConverter.round(10.153456);
//assert
assertThat(actualRoundDown).isEqualTo("10,1");
assertThat(actualRoundUp).isEqualTo("10,2");
}
@Test
public void roundSmallNumberWith2DigitAccuracy() {
//arrange
LocaleContextHolder.setLocale(Locale.GERMAN);
//act
String actualRoundDown = MetricsConverter.round(0.091);
String actualRoundUp = MetricsConverter.round(0.076);
//assert
assertThat(actualRoundDown).isEqualTo("0,09");
assertThat(actualRoundUp).isEqualTo("0,08");
}
@Test
public void roundSmallNumberWith2DigitAccuracyStrangeEdgeCase() {
//arrange
LocaleContextHolder.setLocale(Locale.GERMAN);
//act
String actualRoundUp = MetricsConverter.round(0.075);
//assert
assertThat(actualRoundUp).isEqualTo("0,08");
}
@Test
public void roundSmallNumberEdgeCase1DigitAccuracy() {
//arrange
LocaleContextHolder.setLocale(Locale.GERMAN);
//act
String actualRoundUp = MetricsConverter.round(0.095);
//assert
assertThat(actualRoundUp).isEqualTo("0,1");
}
}
But the test method roundSmallNumberWith2DigitAccuracyStrangeEdgeCase fails. The actual output is "0.07" but for my understanding it should be 0.08. What do I miss?
The value
0.075cannot be represented by adoublewith absolute precision. You can see that if you print the value ofnew BigDecimal(0.075):That number is definitely closer to 0.07 than it is to 0.08, hence you get the result that you see.