mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-10 07:39:16 +00:00
ICU-11439 Improve portability of DecimalFormat::getFixedDecimal().
X-SVN-Rev: 36893
This commit is contained in:
parent
9d67693f3f
commit
ff85125d2f
5 changed files with 58 additions and 23 deletions
|
@ -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.
|
||||
|
|
|
@ -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"
|
||||
|
||||
//-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -183,6 +183,7 @@ class NumberFormatTest: public CalendarTimeZoneTest {
|
|||
void TestEquality();
|
||||
|
||||
void TestCurrencyUsage();
|
||||
void TestDoubleLimit11439();
|
||||
|
||||
private:
|
||||
UBool testFormattableAsUFormattable(const char *file, int line, Formattable &f);
|
||||
|
|
Loading…
Add table
Reference in a new issue