mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-21 12:40:02 +00:00
ICU-1871 double division inaccurate, changed round() api to handle that.
X-SVN-Rev: 11135
This commit is contained in:
parent
b3bf0e1583
commit
bce4ce451a
2 changed files with 106 additions and 45 deletions
|
@ -1,7 +1,7 @@
|
|||
/*****************************************************************************************
|
||||
* $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/icu/dev/test/format/IntlTestDecimalFormatAPI.java,v $
|
||||
* $Date: 2002/08/01 20:27:21 $
|
||||
* $Revision: 1.3 $
|
||||
* $Date: 2003/02/21 01:49:23 $
|
||||
* $Revision: 1.4 $
|
||||
*
|
||||
*****************************************************************************************
|
||||
**/
|
||||
|
@ -24,9 +24,8 @@
|
|||
|
||||
package com.ibm.icu.dev.test.format;
|
||||
|
||||
import com.ibm.icu.lang.*;
|
||||
import com.ibm.icu.text.*;
|
||||
import com.ibm.icu.util.*;
|
||||
import com.ibm.icu.math.BigDecimal;
|
||||
import java.util.Locale;
|
||||
import java.text.ParsePosition;
|
||||
import java.text.Format;
|
||||
|
@ -37,8 +36,58 @@ public class IntlTestDecimalFormatAPI extends com.ibm.icu.dev.test.TestFmwk
|
|||
public static void main(String[] args) throws Exception {
|
||||
new IntlTestDecimalFormatAPI().run(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Problem 1: simply running
|
||||
* decF4.setRoundingMode(java.math.BigDecimal.ROUND_HALF_UP) does not work
|
||||
* as decF4.setRoundingIncrement(.0001) must also be run.
|
||||
* Problem 2: decF4.format(8.88885) does not return 8.8889 as expected.
|
||||
* You must run decF4.format(new BigDecimal(Double.valueOf(8.88885))) in
|
||||
* order for this to work as expected.
|
||||
* Problem 3: There seems to be no way to set half up to be the default
|
||||
* rounding mode.
|
||||
* We solved the problem with the code at the bottom of this page however
|
||||
* this is not quite general purpose enough to include in icu4j. A static
|
||||
* setDefaultRoundingMode function would solve the problem nicely. Also
|
||||
* decimal places past 20 are not handled properly. A small ammount of work
|
||||
* would make bring this up to snuff.
|
||||
*/
|
||||
public void testJB1871()
|
||||
{
|
||||
// problem 2
|
||||
DecimalFormat dec = new DecimalFormat(",##0.0000");
|
||||
double roundinginc = 0.0001;
|
||||
double number = 8.88885;
|
||||
String expected = "8.8889";
|
||||
|
||||
dec.setRoundingMode(BigDecimal.ROUND_HALF_UP);
|
||||
dec.setRoundingIncrement(roundinginc);
|
||||
if (!dec.format(number).equals(expected)) {
|
||||
errln("Error formating " + number + ", expected " + expected);
|
||||
}
|
||||
|
||||
dec = new DecimalFormat(",##0.0001");
|
||||
dec.setRoundingMode(BigDecimal.ROUND_HALF_UP);
|
||||
if (!dec.format(number).equals(expected)) {
|
||||
errln("Error formating " + number + ", expected " + expected);
|
||||
}
|
||||
|
||||
// testing 20 decimal places
|
||||
dec = new DecimalFormat(",##0.00000000000000000001");
|
||||
BigDecimal bignumber = new BigDecimal("8.888888888888888888885");
|
||||
expected = "8.88888888888888888889";
|
||||
|
||||
dec.setRoundingMode(BigDecimal.ROUND_HALF_UP);
|
||||
if (!dec.format(bignumber).equals(expected)) {
|
||||
errln("Error formating " + bignumber + ", expected " + expected);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// This test checks various generic API methods in DecimalFormat to achieve 100% API coverage.
|
||||
/**
|
||||
* This test checks various generic API methods in DecimalFormat to achieve
|
||||
* 100% API coverage.
|
||||
*/
|
||||
public void TestAPI()
|
||||
{
|
||||
logln("DecimalFormat API test---"); logln("");
|
||||
|
@ -213,20 +262,7 @@ public class IntlTestDecimalFormatAPI extends com.ibm.icu.dev.test.TestFmwk
|
|||
if( ! s3.equals(p2) ) {
|
||||
errln("ERROR: toLocalizedPattern() result did not match pattern applied");
|
||||
}
|
||||
|
||||
// ======= Test getStaticClassID()
|
||||
|
||||
// logln("Testing instanceof()");
|
||||
|
||||
// try {
|
||||
// NumberFormat test = new DecimalFormat();
|
||||
|
||||
// if (! (test instanceof DecimalFormat)) {
|
||||
// errln("ERROR: instanceof failed");
|
||||
// }
|
||||
// }
|
||||
// catch (Exception e) {
|
||||
// errln("ERROR: Couldn't create a DecimalFormat");
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -5,24 +5,20 @@
|
|||
*******************************************************************************
|
||||
*
|
||||
* $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/icu/text/DecimalFormat.java,v $
|
||||
* $Date: 2002/12/05 01:21:38 $
|
||||
* $Revision: 1.20 $
|
||||
* $Date: 2003/02/21 01:49:21 $
|
||||
* $Revision: 1.21 $
|
||||
*
|
||||
*****************************************************************************************
|
||||
*/
|
||||
package com.ibm.icu.text;
|
||||
|
||||
import com.ibm.icu.util.Currency;
|
||||
import java.text.Format;
|
||||
import java.text.ParsePosition;
|
||||
import java.text.FieldPosition;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.Locale;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.util.Hashtable;
|
||||
import java.io.InvalidObjectException; //Bug 4185761 [Richard/GCL]
|
||||
|
||||
/**
|
||||
* <code>DecimalFormat</code> is a concrete subclass of
|
||||
|
@ -496,8 +492,9 @@ public class DecimalFormat extends NumberFormat {
|
|||
|
||||
// Apply rounding after multiplier
|
||||
if (roundingDouble > 0.0) {
|
||||
number = roundingDouble
|
||||
* round(number / roundingDouble, roundingMode, isNegative);
|
||||
// number = roundingDouble
|
||||
// * round(number / roundingDouble, roundingMode, isNegative);
|
||||
number = round(number, roundingDouble, roundingMode, isNegative);
|
||||
}
|
||||
|
||||
if (Double.isInfinite(number))
|
||||
|
@ -529,46 +526,74 @@ public class DecimalFormat extends NumberFormat {
|
|||
return subformat(result, fieldPosition, isNegative, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <strong><font face=helvetica color=red>NEW</font></strong>
|
||||
* Round a double value to the nearest integer according to the
|
||||
* given mode.
|
||||
* @param a the absolute value of the number to be rounded
|
||||
* Note this is changed from the version in 2.4, since division of doubles
|
||||
* have inaccuracies. jitterbug 1871.
|
||||
* @param number the absolute value of the number to be rounded
|
||||
* @param roundingInc the rounding increment
|
||||
* @param mode a BigDecimal rounding mode
|
||||
* @param isNegative true if the number to be rounded is negative
|
||||
* @return the absolute value of the rounded result
|
||||
*/
|
||||
private static double round(double a, int mode, boolean isNegative) {
|
||||
private static double round(double number, double roundingInc,
|
||||
int mode, boolean isNegative) {
|
||||
double div = number / roundingInc;
|
||||
switch (mode) {
|
||||
case java.math.BigDecimal.ROUND_CEILING:
|
||||
return isNegative ? Math.floor(a) : Math.ceil(a);
|
||||
return (isNegative ? Math.floor(div) : Math.ceil(div))
|
||||
* roundingInc;
|
||||
case java.math.BigDecimal.ROUND_FLOOR:
|
||||
return isNegative ? Math.ceil(a) : Math.floor(a);
|
||||
return (isNegative ? Math.ceil(div) : Math.floor(div))
|
||||
* roundingInc;
|
||||
case java.math.BigDecimal.ROUND_DOWN:
|
||||
return Math.floor(a);
|
||||
return (Math.floor(div)) * roundingInc;
|
||||
case java.math.BigDecimal.ROUND_UP:
|
||||
return Math.ceil(a);
|
||||
return (Math.ceil(div)) * roundingInc;
|
||||
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.
|
||||
{
|
||||
double f = Math.floor(a);
|
||||
if ((a - f) != 0.5) {
|
||||
return Math.rint(a);
|
||||
{ double ceildiff = (Math.ceil(div) * roundingInc) - number;
|
||||
double floor = Math.floor(div);
|
||||
double floordiff = number - (floor * roundingInc);
|
||||
if (ceildiff != floordiff) {
|
||||
return (Math.rint(div)) * roundingInc;
|
||||
}
|
||||
f /= 2.0;
|
||||
return f == Math.floor(f) ? Math.floor(a) : (Math.floor(a) + 1.0);
|
||||
floor /= 2.0;
|
||||
return (floor == Math.floor(floor) ? Math.floor(div)
|
||||
: (Math.floor(div) + 1.0))
|
||||
* roundingInc;
|
||||
}
|
||||
case java.math.BigDecimal.ROUND_HALF_DOWN:
|
||||
return ((a - Math.floor(a)) <= 0.5) ? Math.floor(a) : Math.ceil(a);
|
||||
{
|
||||
double ceil = Math.ceil(div);
|
||||
double ceildiff = (ceil * roundingInc) - number;
|
||||
double floor = Math.floor(div);
|
||||
double floordiff = number - (floor * roundingInc);
|
||||
if (ceildiff < floordiff) {
|
||||
return ceil * roundingInc;
|
||||
}
|
||||
return floor * roundingInc;
|
||||
}
|
||||
case java.math.BigDecimal.ROUND_HALF_UP:
|
||||
return ((a - Math.floor(a)) < 0.5) ? Math.floor(a) : Math.ceil(a);
|
||||
{
|
||||
double ceil = Math.ceil(div);
|
||||
double ceildiff = (ceil * roundingInc) - number;
|
||||
double floor = Math.floor(div);
|
||||
double floordiff = number - (floor * roundingInc);
|
||||
if (ceildiff <= floordiff) {
|
||||
return ceil * roundingInc;
|
||||
}
|
||||
return floor * roundingInc;
|
||||
}
|
||||
case java.math.BigDecimal.ROUND_UNNECESSARY:
|
||||
if (a != Math.floor(a)) {
|
||||
if (div != Math.floor(div)) {
|
||||
throw new ArithmeticException("Rounding necessary");
|
||||
}
|
||||
return a;
|
||||
return number;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid rounding mode: " + mode);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue