ICU-5350 Fix decimal rounding

X-SVN-Rev: 21647
This commit is contained in:
Deborah Goldsmith 2007-06-06 01:17:15 +00:00
parent 44e1e346ca
commit 5369d809be
5 changed files with 114 additions and 14 deletions

View file

@ -416,6 +416,7 @@ DecimalFormat::operator=(const DecimalFormat& rhs)
*fRoundingIncrement = *rhs.fRoundingIncrement;
}
fRoundingDouble = rhs.fRoundingDouble;
fRoundingMode = rhs.fRoundingMode;
fMultiplier = rhs.fMultiplier;
fGroupingSize = rhs.fGroupingSize;
fGroupingSize2 = rhs.fGroupingSize2;
@ -732,6 +733,10 @@ DecimalFormat::format( double number,
DigitList digits;
// This detects negativity too.
if (fRoundingIncrement == NULL) {
// If we did not round in binary space, round in decimal space
digits.fRoundingMode = fRoundingMode;
}
digits.set(number, precision(FALSE),
!fUseExponentialNotation && !areSignificantDigitsUsed());

View file

@ -1,6 +1,6 @@
/*
**********************************************************************
* Copyright (C) 1997-2005, International Business Machines
* Copyright (C) 1997-2007, International Business Machines
* Corporation and others. All Rights Reserved.
**********************************************************************
*
@ -94,6 +94,7 @@ DigitList::operator=(const DigitList& other)
fDecimalAt = other.fDecimalAt;
fCount = other.fCount;
fIsPositive = other.fIsPositive;
fRoundingMode = other.fRoundingMode;
uprv_strncpy(fDigits, other.fDigits, fCount);
}
return *this;
@ -108,6 +109,7 @@ DigitList::operator==(const DigitList& that) const
(fDecimalAt == that.fDecimalAt &&
fCount == that.fCount &&
fIsPositive == that.fIsPositive &&
fRoundingMode == that.fRoundingMode &&
uprv_strncmp(fDigits, that.fDigits, fCount) == 0));
}
@ -120,6 +122,7 @@ DigitList::clear()
fDecimalAt = 0;
fCount = 0;
fIsPositive = TRUE;
fRoundingMode = DecimalFormat::kRoundHalfEven;
// Don't bother initializing fDigits because fCount is 0.
}
@ -548,7 +551,7 @@ DigitList::round(int32_t maximumDigits)
/**
* Return true if truncating the representation to the given number
* of digits will result in an increment to the last digit. This
* method implements half-even rounding, the default rounding mode.
* method implements the requested rounding mode.
* [bnf]
* @param maximumDigits the number of digits to keep, from 0 to
* <code>count-1</code>. If 0, then all digits are rounded away, and
@ -558,16 +561,38 @@ DigitList::round(int32_t maximumDigits)
* incremented
*/
UBool DigitList::shouldRoundUp(int32_t maximumDigits) const {
// Implement IEEE half-even rounding
if (fDigits[maximumDigits] == '5' ) {
for (int i=maximumDigits+1; i<fCount; ++i) {
if (fDigits[i] != kZero) {
switch (fRoundingMode) {
case DecimalFormat::kRoundCeiling:
return fIsPositive;
case DecimalFormat::kRoundFloor:
return !fIsPositive;
case DecimalFormat::kRoundDown:
return FALSE;
case DecimalFormat::kRoundUp:
return TRUE;
case DecimalFormat::kRoundHalfEven:
case DecimalFormat::kRoundHalfDown:
case DecimalFormat::kRoundHalfUp:
default:
if (fDigits[maximumDigits] == '5' ) {
for (int i=maximumDigits+1; i<fCount; ++i) {
if (fDigits[i] != kZero) {
return TRUE;
}
}
switch (fRoundingMode) {
case DecimalFormat::kRoundHalfEven:
default:
// Implement IEEE half-even rounding
return maximumDigits > 0 && (fDigits[maximumDigits-1] % 2 != 0);
case DecimalFormat::kRoundHalfDown:
return FALSE;
case DecimalFormat::kRoundHalfUp:
return TRUE;
}
}
return maximumDigits > 0 && (fDigits[maximumDigits-1] % 2 != 0);
return (fDigits[maximumDigits] > '5');
}
return (fDigits[maximumDigits] > '5');
}
// -------------------------------------

View file

@ -1,7 +1,7 @@
/*
******************************************************************************
*
* Copyright (C) 1997-2004, International Business Machines
* Copyright (C) 1997-2007, International Business Machines
* Corporation and others. All Rights Reserved.
*
******************************************************************************
@ -26,6 +26,7 @@
#include "unicode/utypes.h"
#include "unicode/uobject.h"
#include "unicode/decimfmt.h"
#include <float.h>
// Decimal digits in a 64-bit int
@ -218,10 +219,11 @@ public:
* Zero is represented by any DigitList with fCount == 0 or with each fDigits[i]
* for all i <= fCount == '0'.
*/
int32_t fDecimalAt;
int32_t fCount;
UBool fIsPositive;
char *fDigits;
int32_t fDecimalAt;
int32_t fCount;
UBool fIsPositive;
char *fDigits;
DecimalFormat::ERoundingMode fRoundingMode;
private:

View file

@ -49,6 +49,7 @@ void addNumFrDepTest(TestNode** root)
addTest(root, &TestDoubleAttribute, "tsformat/cnmdptst/TestDoubleAttribute");
addTest(root, &TestSecondaryGrouping, "tsformat/cnmdptst/TestSecondaryGrouping");
addTest(root, &TestCurrencyKeywords, "tsformat/cnmdptst/TestCurrencyKeywords");
addTest(root, &TestRounding5350, "tsformat/cnmdptst/TestRounding5350");
}
/*Test Various format patterns*/
@ -861,4 +862,67 @@ static void TestCurrencyKeywords(void)
}
}
/**
* Test proper handling of rounding modes.
*/
static void TestRounding5350(void)
{
UNumberFormat *nnf;
UErrorCode status = U_ZERO_ERROR;
/* this is supposed to open default date format, but later on it treats it like it is "en_US"
- very bad if you try to run the tests on machine where default locale is NOT "en_US" */
/* nnf = unum_open(UNUM_DEFAULT, NULL, &status); */
nnf = unum_open(UNUM_DEFAULT, NULL,0,"en_US",NULL, &status);
if(U_FAILURE(status)){
log_err("FAIL: failure in the construction of number format: %s\n", myErrorName(status));
} else {
unum_setAttribute(nnf, UNUM_MAX_FRACTION_DIGITS, 2);
roundingTest2(nnf, -0.125, UNUM_ROUND_CEILING, "-0.12");
roundingTest2(nnf, -0.125, UNUM_ROUND_FLOOR, "-0.13");
roundingTest2(nnf, -0.125, UNUM_ROUND_DOWN, "-0.12");
roundingTest2(nnf, -0.125, UNUM_ROUND_UP, "-0.13");
roundingTest2(nnf, 0.125, UNUM_FOUND_HALFEVEN, "0.12");
roundingTest2(nnf, 0.135, UNUM_ROUND_HALFDOWN, "0.13");
roundingTest2(nnf, 0.125, UNUM_ROUND_HALFUP, "0.13");
roundingTest2(nnf, 0.135, UNUM_FOUND_HALFEVEN, "0.14");
}
unum_close(nnf);
}
/*-------------------------------------*/
static void roundingTest2(UNumberFormat* nf, double x, int32_t roundingMode, const char* expected)
{
UChar *out = NULL;
UChar *res;
UFieldPosition pos;
UErrorCode status;
int32_t lneed;
status=U_ZERO_ERROR;
unum_setAttribute(nf, UNUM_ROUNDING_MODE, roundingMode);
lneed=0;
lneed=unum_formatDouble(nf, x, NULL, lneed, NULL, &status);
if(status==U_BUFFER_OVERFLOW_ERROR){
status=U_ZERO_ERROR;
out=(UChar*)malloc(sizeof(UChar) * (lneed+1) );
pos.field=0;
unum_formatDouble(nf, x, out, lneed+1, &pos, &status);
}
if(U_FAILURE(status)) {
log_err("Error in formatting using unum_formatDouble(.....): %s\n", myErrorName(status) );
}
/*Need to use log_verbose here. Problem with the float*/
/*printf("%f format with %d fraction digits to %s\n", x, maxFractionDigits, austrdup(out) );*/
res=(UChar*)malloc(sizeof(UChar) * (strlen(expected)+1) );
u_uastrcpy(res, expected);
if (u_strcmp(out, res) != 0)
log_err("FAIL: Expected: %s or %s\n", expected, austrdup(res) );
free(res);
if(out != NULL) {
free(out);
}
}
#endif /* #if !UCONFIG_NO_FORMATTING */

View file

@ -1,6 +1,6 @@
/********************************************************************
* COPYRIGHT:
* Copyright (c) 1997-2003, International Business Machines Corporation and
* Copyright (c) 1997-2003, 2007 International Business Machines Corporation and
* others. All Rights Reserved.
********************************************************************/
/********************************************************************************
@ -38,6 +38,9 @@ static void TestCurrencySign(void);
/* Test proper rounding by the format method.*/
static void TestRounding487(void);
/* Test proper handling of rounding modes. */
static void TestRounding5350(void);
/* Test localized currency patterns. */
static void TestCurrency(void);
@ -48,6 +51,7 @@ static void TestSecondaryGrouping(void);
/*Internal functions used*/
static void roundingTest(UNumberFormat*, double, int32_t, const char*);
static void roundingTest2(UNumberFormat*, double, int32_t, const char*);
static void TestCurrencyKeywords(void);