Ticket #4863 (closed defect: fixed)

Bug contains 2 commit(s) | SVN Diffs for #4863

 

Opened 3 years ago

possible rounding bug for currency (Locale=de_CH)

Reported by: joachim(at)wemove.com Assigned to: mark
Priority: critical Milestone: 3.6
Component: unknown Version: 3.4
Keywords: closed Cc:
Load: Xref: 5343
Java Version: sunjdk1.4.x Operating System: all
Project (C/J): ICU4J Weeks: 0
Review: grhoten

Description

possible rounding bug:

locale=de_CH, isocode=CHF, price=39.95

com.ibm.icu.text.NumberFormat nf = com.ibm.icu.text.NumberFormat.getCurrencyInstance(locale); nf.setCurrency(com.ibm.icu.util.Currency.getInstance(isoCode)); localePrice = nf.format(price);

localePrice is 40.0

switzerland does have a 5-Rappen-Rounding, but this should lead to 39.95 and not 40.0.

Attachments

Change History

12/31/69 17:22:32 changed by notes

ICU4C fix resubmitted as 5343

12/31/69 17:22:33 changed by auditor

  • Mon Dec 5 14:23:54 2005 weiv changed notes2: assign: "" to "mark", priority: "" to "critical", target: "UNSCH" to "", comments: "" to "!discuss!",
  • Mon Dec 5 14:23:54 2005 weiv moved from incoming to formatting
  • Thu Dec 8 09:46:30 2005 mark sent reply 1
  • Mon Dec 12 13:44:23 2005 weiv changed notes2: comments: "!discuss!" to "",
  • Tue Dec 13 15:18:34 2005 mark changed notes2: target: "UNSCH" to "3.6",
  • Tue Dec 13 15:19:45 2005 mark changed notes2: assign: "mark" to "mark, rhoten",
  • Fri Mar 31 13:54:41 2006 ram changed notes2: assign: "mark, rhoten" to "george",
  • Thu Aug 24 14:31:49 2006 grhoten changed notes2: assign: "george" to "mark", xref: "" to "5343",
  • Thu Aug 24 14:31:49 2006 grhoten changed notes
  • Thu Aug 24 14:31:49 2006 grhoten moved from formatting to fixed
  • Thu Aug 24 14:31:53 2006 grhoten changed notes2: review: "" to "grhoten",
  • Sun Oct 22 07:30:54 2006 grhoten moved from fixed to closed

12/08/05 08:46:30 changed by Mark Edward Davis <mark.davis(at)jtcsv.com>

I verified the bug.

The workaround for now is to use: ((DecimalFormat)nf).setRoundingMode(BigDecimal.ROUND_HALF_DOWN);

Here is test code showing the problem:

static void checkrounding(boolean useWorkaround) {

ULocale locale = new ULocale("de_CH"); String isoCode = "CHF"; com.ibm.icu.text.NumberFormat nf = com.ibm.icu.text.NumberFormat

.getCurrencyInstance(locale);

nf.setCurrency(com.ibm.icu.util.Currency.getInstance(isoCode)); if (useWorkaround) {

System.out.println("* Using work-around"); ((DecimalFormat)nf) .setRoundingMode(BigDecimal.ROUND_HALF_DOWN);

} // use increments of 1000 to avoid accumulated rounding in test double basePrice = 39950; for (double delta = -30; delta <= 30; delta += 5) {

double price = (basePrice + delta) / 1000.0; String localePrice = nf.format(price); System.out.println(price + "\t=>\t" + localePrice);

}

}

and output:

39.92 => SFr. 39.90 39.925 => SFr. 39.90 39.93 => SFr. 39.95 39.935 => SFr. 39.95 39.94 => SFr. 39.95 39.945 => SFr. 39.95 39.95 => SFr. 40.00 39.955 => SFr. 39.95 39.96 => SFr. 39.95 39.965 => SFr. 39.95 39.97 => SFr. 39.95 39.975 => SFr. 40.00 39.98 => SFr. 40.00 * Using work-around 39.92 => SFr. 39.90 39.925 => SFr. 39.90 39.93 => SFr. 39.95 39.935 => SFr. 39.95 39.94 => SFr. 39.95 39.945 => SFr. 39.95 39.95 => SFr. 39.95 39.955 => SFr. 39.95 39.96 => SFr. 39.95 39.965 => SFr. 39.95 39.97 => SFr. 39.95 39.975 => SFr. 39.95 39.98 => SFr. 40.00

The suspect code is in DecimalFormat. I don't know who did this originally, but it is tricky and needs clear comments (as well as the bugfix!)

// Handle complex cases double ceil = Math.ceil(div); double ceildiff = (ceil * roundingInc) - number; double floor = Math.floor(div); double floordiff = number - (floor * roundingInc); switch (mode) { case java.math.BigDecimal.ROUND_HALF_EVEN:

// We should be able to just return Math.rint(a), but this // doesn't work in some VMs. if (ceildiff != floordiff) {

return (Math.rint(div)) * roundingInc;

} floor /= 2.0; return (floor == Math.floor(floor) ? Math.floor(div)

: (Math.floor(div) + 1.0))

  • roundingInc;

Add/Change #4863 (possible rounding bug for currency (Locale=de_CH))




Anti spam check: