ICU-11439 Improve portability of DecimalFormat::getFixedDecimal().

X-SVN-Rev: 36893
This commit is contained in:
Andy Heninger 2014-12-18 22:46:45 +00:00
parent 9d67693f3f
commit ff85125d2f
5 changed files with 58 additions and 23 deletions

View file

@ -72,6 +72,7 @@
#include "dcfmtimp.h"
#include "plurrule_impl.h"
#include "decimalformatpattern.h"
#include "fmtableimp.h"
/*
* On certain platforms, round is a macro defined in math.h
@ -1138,11 +1139,6 @@ DecimalFormat::getFixedDecimal(double number, UErrorCode &status) const {
return result;
}
// MSVC optimizer bug?
// turn off optimization as it causes different behavior in the int64->double->int64 conversion
#if defined (_MSC_VER)
#pragma optimize ( "", off )
#endif
FixedDecimal
DecimalFormat::getFixedDecimal(const Formattable &number, UErrorCode &status) const {
if (U_FAILURE(status)) {
@ -1164,17 +1160,9 @@ DecimalFormat::getFixedDecimal(const Formattable &number, UErrorCode &status) co
return getFixedDecimal(number.getDouble(status), status);
}
if (type == Formattable::kInt64) {
// "volatile" here is a workaround to avoid optimization issues.
volatile double fdv = number.getDouble(status);
// Note: conversion of int64_t -> double rounds with some compilers to
// values beyond what can be represented as a 64 bit int. Subsequent
// testing or conversion with int64_t produces bad results.
// So filter the problematic values, route them to DigitList.
if (fdv != (double)U_INT64_MAX && fdv != (double)U_INT64_MIN &&
number.getInt64() == (int64_t)fdv) {
return getFixedDecimal(number.getDouble(status), status);
}
if (type == Formattable::kInt64 && number.getInt64() <= MAX_INT64_IN_DOUBLE &&
number.getInt64() >= -MAX_INT64_IN_DOUBLE) {
return getFixedDecimal(number.getDouble(status), status);
}
// The only case left is type==int64_t, with a value with more digits than a double can represent.
@ -1186,10 +1174,6 @@ DecimalFormat::getFixedDecimal(const Formattable &number, UErrorCode &status) co
digits.set(number.getInt64());
return getFixedDecimal(digits, status);
}
// end workaround MSVC optimizer bug
#if defined (_MSC_VER)
#pragma optimize ( "", on )
#endif
// Create a fixed decimal from a DigitList.

View file

@ -1,6 +1,6 @@
/*
*******************************************************************************
* Copyright (C) 1997-2013, International Business Machines Corporation and *
* Copyright (C) 1997-2014, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*
@ -28,6 +28,7 @@
#include "cstring.h"
#include "decNumber.h"
#include "digitlst.h"
#include "fmtableimp.h"
// *****************************************************************************
// class Formattable
@ -37,7 +38,6 @@ U_NAMESPACE_BEGIN
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Formattable)
#include "fmtableimp.h"
//-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.

View file

@ -1,6 +1,6 @@
/*
*******************************************************************************
* Copyright (C) 2010-2012, International Business Machines Corporation and *
* Copyright (C) 2010-2014, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
@ -8,6 +8,9 @@
#ifndef FMTABLEIMP_H
#define FMTABLEIMP_H
#include "digitlst.h"
U_NAMESPACE_BEGIN
/**
* @internal
@ -19,6 +22,15 @@ struct FmtStackData {
// 192 total
};
/**
* Maximum int64_t value that can be stored in a double without chancing losing precision.
* IEEE doubles have 53 bits of mantissa, 10 bits exponent, 1 bit sign.
* IBM Mainframes have 56 bits of mantissa, 7 bits of base 16 exponent, 1 bit sign.
* Define this constant to the smallest value from those for supported platforms.
* @internal
*/
static const int64_t MAX_INT64_IN_DOUBLE = 0x001FFFFFFFFFFFFFLL;
U_NAMESPACE_END
#endif

View file

@ -133,6 +133,7 @@ void NumberFormatTest::runIndexedTest( int32_t index, UBool exec, const char* &n
TESTCASE_AUTO(TestAccountingCurrency);
TESTCASE_AUTO(TestEquality);
TESTCASE_AUTO(TestCurrencyUsage);
TESTCASE_AUTO(TestDoubleLimit11439);
TESTCASE_AUTO_END;
}
@ -7736,4 +7737,41 @@ void NumberFormatTest::TestCurrencyUsage() {
delete fmt;
}
}
// Check the constant MAX_INT64_IN_DOUBLE.
// The value should convert to a double with no loss of precision.
// A failure may indicate a platform with a different double format, requiring
// a revision to the constant.
//
// Note that this is actually hard to test, because the language standard gives
// compilers considerable flexibility to do unexpected things with rounding and
// with overflow in simple int to/from float conversions. Some compilers will completely optimize
// away a simple round-trip conversion from int64_t -> double -> int64_t.
void NumberFormatTest::TestDoubleLimit11439() {
char buf[50];
for (int64_t num = MAX_INT64_IN_DOUBLE-10; num<=MAX_INT64_IN_DOUBLE; num++) {
sprintf(buf, "%ld", num);
double fNum = 0.0;
sscanf(buf, "%lf", &fNum);
int64_t rtNum = fNum;
if (num != rtNum) {
errln("%s:%d MAX_INT64_IN_DOUBLE test, %ld did not round trip. Got %ld", __FILE__, __LINE__, num, rtNum);
return;
}
}
for (int64_t num = -MAX_INT64_IN_DOUBLE+10; num>=-MAX_INT64_IN_DOUBLE; num--) {
sprintf(buf, "%ld", num);
double fNum = 0.0;
sscanf(buf, "%lf", &fNum);
int64_t rtNum = fNum;
if (num != rtNum) {
errln("%s:%d MAX_INT64_IN_DOUBLE test, %ld did not round trip. Got %ld", __FILE__, __LINE__, num, rtNum);
return;
}
}
}
#endif /* #if !UCONFIG_NO_FORMATTING */

View file

@ -183,6 +183,7 @@ class NumberFormatTest: public CalendarTimeZoneTest {
void TestEquality();
void TestCurrencyUsage();
void TestDoubleLimit11439();
private:
UBool testFormattableAsUFormattable(const char *file, int line, Formattable &f);