mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-06 22:15:31 +00:00
ICU-13634 Adding integer overflow logic to ICU4C number pipeline in places where it is in ICU4J.
X-SVN-Rev: 41136
This commit is contained in:
parent
e3180662e2
commit
23d76d8863
6 changed files with 78 additions and 9 deletions
|
@ -533,6 +533,30 @@ uprv_fmin(double x, double y)
|
|||
return (x > y ? y : x);
|
||||
}
|
||||
|
||||
#include <iostream>
|
||||
|
||||
U_CAPI UBool U_EXPORT2
|
||||
uprv_add32_overflow(int32_t a, int32_t b, int32_t* res) {
|
||||
// NOTE: Some compilers (GCC, Clang) have primitives available, like __builtin_add_overflow.
|
||||
// This function could be optimized by calling one of those primitives.
|
||||
auto a64 = static_cast<int64_t>(a);
|
||||
auto b64 = static_cast<int64_t>(b);
|
||||
int64_t res64 = a64 + b64;
|
||||
*res = static_cast<int32_t>(res64);
|
||||
return res64 != *res;
|
||||
}
|
||||
|
||||
U_CAPI UBool U_EXPORT2
|
||||
uprv_mul32_overflow(int32_t a, int32_t b, int32_t* res) {
|
||||
// NOTE: Some compilers (GCC, Clang) have primitives available, like __builtin_mul_overflow.
|
||||
// This function could be optimized by calling one of those primitives.
|
||||
auto a64 = static_cast<int64_t>(a);
|
||||
auto b64 = static_cast<int64_t>(b);
|
||||
int64_t res64 = a64 * b64;
|
||||
*res = static_cast<int32_t>(res64);
|
||||
return res64 != *res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncates the given double.
|
||||
* trunc(3.3) = 3.0, trunc (-3.3) = -3.0
|
||||
|
|
|
@ -391,6 +391,32 @@ U_INTERNAL double U_EXPORT2 uprv_log(double d);
|
|||
*/
|
||||
U_INTERNAL double U_EXPORT2 uprv_round(double x);
|
||||
|
||||
/**
|
||||
* Adds the signed integers a and b, storing the result in res.
|
||||
* Checks for signed integer overflow.
|
||||
* Similar to the GCC/Clang extension __builtin_add_overflow
|
||||
*
|
||||
* @param a The first operand.
|
||||
* @param b The second operand.
|
||||
* @param res a + b
|
||||
* @return true if overflow occurred; false if no overflow occurred.
|
||||
* @internal
|
||||
*/
|
||||
U_INTERNAL UBool U_EXPORT2 uprv_add32_overflow(int32_t a, int32_t b, int32_t* res);
|
||||
|
||||
/**
|
||||
* Multiplies the signed integers a and b, storing the result in res.
|
||||
* Checks for signed integer overflow.
|
||||
* Similar to the GCC/Clang extension __builtin_mul_overflow
|
||||
*
|
||||
* @param a The first multiplicand.
|
||||
* @param b The second multiplicand.
|
||||
* @param res a * b
|
||||
* @return true if overflow occurred; false if no overflow occurred.
|
||||
* @internal
|
||||
*/
|
||||
U_INTERNAL UBool U_EXPORT2 uprv_mul32_overflow(int32_t a, int32_t b, int32_t* res);
|
||||
|
||||
#if 0
|
||||
/**
|
||||
* Returns the number of digits after the decimal point in a double number x.
|
||||
|
|
|
@ -192,12 +192,14 @@ int32_t DecimalQuantity::getMagnitude() const {
|
|||
return scale + precision - 1;
|
||||
}
|
||||
|
||||
void DecimalQuantity::adjustMagnitude(int32_t delta) {
|
||||
bool DecimalQuantity::adjustMagnitude(int32_t delta) {
|
||||
if (precision != 0) {
|
||||
// TODO: How to handle overflow cases?
|
||||
scale += delta;
|
||||
origDelta += delta;
|
||||
// i.e., scale += delta; origDelta += delta
|
||||
bool overflow = uprv_add32_overflow(scale, delta, &scale);
|
||||
overflow = uprv_add32_overflow(origDelta, delta, &origDelta) || overflow;
|
||||
return overflow;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
StandardPlural::Form DecimalQuantity::getStandardPlural(const PluralRules *rules) const {
|
||||
|
|
|
@ -100,8 +100,9 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory {
|
|||
* this method with delta=-3 will change the value to "1.23456".
|
||||
*
|
||||
* @param delta The number of magnitudes of ten to change by.
|
||||
* @return true if integer overflow occured; false otherwise.
|
||||
*/
|
||||
void adjustMagnitude(int32_t delta);
|
||||
bool adjustMagnitude(int32_t delta);
|
||||
|
||||
/**
|
||||
* @return The power of ten corresponding to the most significant nonzero digit.
|
||||
|
|
|
@ -164,9 +164,11 @@ bool DecimalMatcher::match(StringSegment& segment, ParsedNumber& result, int8_t
|
|||
|
||||
// Save the digit in the DecimalQuantity or scientific adjustment.
|
||||
if (exponentSign != 0) {
|
||||
int nextExponent = digit + exponent * 10;
|
||||
if (nextExponent < exponent) {
|
||||
// Overflow
|
||||
int32_t nextExponent;
|
||||
// i.e., nextExponent = exponent * 10 + digit
|
||||
UBool overflow = uprv_mul32_overflow(exponent, 10, &nextExponent) ||
|
||||
uprv_add32_overflow(nextExponent, digit, &nextExponent);
|
||||
if (overflow) {
|
||||
exponent = INT32_MAX;
|
||||
} else {
|
||||
exponent = nextExponent;
|
||||
|
@ -278,7 +280,7 @@ bool DecimalMatcher::match(StringSegment& segment, ParsedNumber& result, int8_t
|
|||
U_ASSERT(!result.quantity.bogus);
|
||||
bool overflow = (exponent == INT32_MAX);
|
||||
if (!overflow) {
|
||||
result.quantity.adjustMagnitude(exponentSign * exponent);
|
||||
overflow = result.quantity.adjustMagnitude(exponentSign * exponent);
|
||||
}
|
||||
if (overflow) {
|
||||
if (exponentSign == -1) {
|
||||
|
|
|
@ -128,6 +128,20 @@ static void TestPUtilAPI(void){
|
|||
log_err("ERROR: uprv_isInfinite failed.\n");
|
||||
}
|
||||
|
||||
log_verbose("Testing the APIs uprv_add32_overflow and uprv_mul32_overflow\n");
|
||||
int32_t overflow_result;
|
||||
doAssert(FALSE, uprv_add32_overflow(INT32_MAX - 2, 1, &overflow_result), "should not overflow");
|
||||
doAssert(INT32_MAX - 1, overflow_result, "should equal INT32_MAX - 1");
|
||||
doAssert(FALSE, uprv_add32_overflow(INT32_MAX - 2, 2, &overflow_result), "should not overflow");
|
||||
doAssert(INT32_MAX, overflow_result, "should equal exactly INT32_MAX");
|
||||
doAssert(TRUE, uprv_add32_overflow(INT32_MAX - 2, 3, &overflow_result), "should overflow");
|
||||
doAssert(FALSE, uprv_mul32_overflow(INT32_MAX / 5, 4, &overflow_result), "should not overflow");
|
||||
doAssert(INT32_MAX / 5 * 4, overflow_result, "should equal INT32_MAX / 5 * 4");
|
||||
doAssert(TRUE, uprv_mul32_overflow(INT32_MAX / 5, 6, &overflow_result), "should overflow");
|
||||
// Test on negative numbers:
|
||||
doAssert(FALSE, uprv_add32_overflow(-3, -2, &overflow_result), "should not overflow");
|
||||
doAssert(-5, overflow_result, "should equal -5");
|
||||
|
||||
#if 0
|
||||
log_verbose("Testing the API uprv_digitsAfterDecimal()....\n");
|
||||
doAssert(uprv_digitsAfterDecimal(value1), 3, "uprv_digitsAfterDecimal() failed.");
|
||||
|
|
Loading…
Add table
Reference in a new issue