possible rounding bug for currency (Locale=de_CH)

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.

Activity

Show:
TracBot
July 1, 2018, 12:12 AM
Trac Comment by notes—1970-01-01T01:22:32.000Z

ICU4C fix resubmitted as 5343

TracBot
July 1, 2018, 12:12 AM
Trac Comment by auditor—1970-01-01T01:22:33.000Z
  • Mon Dec 5 14:23:54 2005 weiv changed notes2: assign: "" to "mark", priority: "" to "critical", target: "UNSCH" to "", comments: "" to "

    ",

  • 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: "

    " 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

TracBot
July 1, 2018, 12:12 AM
Trac Comment by Mark Edward Davis <mark.davis@c66d9d543b9863e0—2005-12-08T16:46:30.000Z

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;

Fixed

Assignee

Mark Davis

Reporter

TracBot

Components

Labels

None

Reviewer

None

Priority

blocks-release

Time Needed

None

Fix versions