Why Does 2.01 Turn Into 2.00? Avoid Double Precision Bugs in Money Calculations
The article explains how using double for monetary values can cause precision loss—turning 2.01 into 2.00—illustrates the binary representation issue, demonstrates failing code, and presents a robust solution using Java's BigDecimal with sample conversion methods and performance tests.
Background
A bug was reported where an order price that should be 2.01 was displayed as 2.00. The root cause was identified as the use of double for monetary conversion, which leads to precision loss.
Why 2.01 Becomes 2.00
Decimal numbers like 2.01 cannot be represented exactly in binary floating‑point format. In binary, 2.01 is stored as something like 000000010.009999999999999787, causing a small error that becomes visible after formatting.
Scenarios Where Decimal Conversion Fails
Any situation that converts a monetary amount to double, formats it to two decimal places, and then converts it back to an integer can lose precision. The following loop demonstrates the problem:
for (int money = 0; money < 10000; money++) {
String valueYuan = String.format("%.2f", money * 1.0 / 100);
int value = (int) (Double.valueOf(valueYuan) * 100);
if (value != money) {
System.out.println(String.format("原值: %s, 现值:%s", money, value));
}
}Running this test from 0 to 9,999 reveals 573 values with conversion errors, showing a significant probability of precision loss.
Safer Money Conversion with BigDecimal
Java provides BigDecimal for high‑precision arithmetic. By representing yuan as a String and fen as an int, we can implement reliable conversion methods:
public static String change2Yuan(int money) {
BigDecimal base = BigDecimal.valueOf(money);
BigDecimal yuanBase = base.divide(new BigDecimal(100));
return yuanBase.setScale(2, BigDecimal.ROUND_HALF_UP).toString();
}
public static int change2Fen(String money) {
BigDecimal base = new BigDecimal(money);
BigDecimal fenBase = base.multiply(new BigDecimal(100));
return fenBase.setScale(0, BigDecimal.ROUND_HALF_UP).intValue();
}Verification
Testing the conversion from 0 to 100,000,000 shows zero errors:
int error = 0;
long time = System.currentTimeMillis();
for (int money = 0; money < 100000000; money++) {
String valueYuan = change2Yuan(money);
int value = change2Fen(valueYuan);
if (value != money) {
error++;
}
}
System.out.println("时间:" + (System.currentTimeMillis() - time));
System.out.println(error);Performance Test
Although some claim BigDecimal hurts performance, the test above completes in roughly 2 ms for the full range, indicating acceptable overhead for most applications.
Conclusion
Any code that performs monetary conversion must avoid double to prevent precision loss. Use static analysis tools to detect such patterns and replace them with a utility class based on BigDecimal, as demonstrated.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
ITPUB
Official ITPUB account sharing technical insights, community news, and exciting events.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
