diff --git a/sources/BaseRealm/API/Math/BigInt.uc b/sources/BaseRealm/API/Math/BigInt.uc index e9cf51e..678f936 100644 --- a/sources/BaseRealm/API/Math/BigInt.uc +++ b/sources/BaseRealm/API/Math/BigInt.uc @@ -41,8 +41,9 @@ var private bool negative; // Valid `BigInt` should not have this array empty. var private array digits; -private const ALMOST_MAX_INT = 147483647; -private const DIGITS_IN_MAX_INT = 10; +const ALMOST_MAX_INT = 147483647; +const DIGITS_IN_MAX_INT = 10; +const LAST_DIGIT_ORDER = 1000000000; protected function Constructor() { @@ -365,12 +366,12 @@ public function int ToInt() { local int i; local int accumulator; - local int mostSignificantDigit; + local int safeDigitsAmount; - if (digits.lenght <= 0) { + if (digits.length <= 0) { return 0; } - if (digits.lenght > DIGITS_IN_MAX_INT) + if (digits.length > DIGITS_IN_MAX_INT) { if (negative) { return (-MaxInt - 1); @@ -379,21 +380,57 @@ public function int ToInt() return MaxInt; } } - mostSignificantDigit = -1; - if (digits.lenght == DIGITS_IN_MAX_INT) - { - mostSignificantDigit = digits[digits.length - 1]; - digits[i] = DIGITS_IN_MAX_INT - 1; - } // At most `DIGITS_IN_MAX_INT - 1` iterations - for (i = 0; i < digits.length; i += 1) - {//ALMOST_MAX_INT + safeDigitsAmount = Min(DIGITS_IN_MAX_INT - 1, digits.length); + for (i = safeDigitsAmount - 1; i >= 0; i -= 1) + { accumulator *= 10; accumulator += digits[i]; } - if (mostSignificantDigit < 0) { + if (negative) { + accumulator *= -1; + } + accumulator = AddUnsafeDigitToInt(accumulator); + return accumulator; +} + +// Assumes `digits.length <= DIGITS_IN_MAX_INT` +private function int AddUnsafeDigitToInt(int accumulator) +{ + local int unsafeDigit; + local bool noOverflow; + + if (digits.length < DIGITS_IN_MAX_INT) { + return accumulator; + } + unsafeDigit = digits[DIGITS_IN_MAX_INT - 1]; + // `MaxInt` stats with `2`, so if last/unsafe digit is either `0` or `1`, + // there is no overflow, otherwise - check rest of the digits + noOverflow = (unsafeDigit < 2); + if (unsafeDigit == 2) + { + // Include `MaxInt` and `-MaxInt-1` (minimal possible value) into + // an overflow too - this way we still give a correct result, but do + // not have to worry about `int`-arithmetic error + noOverflow = noOverflow + || (negative && (accumulator > -ALMOST_MAX_INT - 1)) + || (!negative && (accumulator < ALMOST_MAX_INT)); + } + if (noOverflow) + { + if (negative) { + accumulator -= unsafeDigit * LAST_DIGIT_ORDER; + } + else { + accumulator += unsafeDigit * LAST_DIGIT_ORDER; + } return accumulator; } + // Handle overflow + if (negative) { + return (-MaxInt - 1); + } + return MaxInt; } public function Text ToText() diff --git a/sources/BaseRealm/API/Math/Tests/TEST_BigInt.uc b/sources/BaseRealm/API/Math/Tests/TEST_BigInt.uc index 84b03f9..c90785c 100644 --- a/sources/BaseRealm/API/Math/Tests/TEST_BigInt.uc +++ b/sources/BaseRealm/API/Math/Tests/TEST_BigInt.uc @@ -24,9 +24,13 @@ protected static function TESTS() { // Here we use `ToString()` method to check `BigInt` creation, // therefore also testing it + Context("Testing creation of `BigInt`s."); Test_Creating(); // So here we nee to test `ToText()` methods separately + Context("Testing `ToText()` method of `BigInt`s."); Test_ToText(); + Context("Testing `ToInt()` method of `BigInt`s."); + Test_ToInt(); Context("Testing basic arithmetic operations on `BigInt`s."); Test_AddingValues(); Test_SubtractingValues(); @@ -34,7 +38,6 @@ protected static function TESTS() protected static function Test_Creating() { - Context("Testing creation of `BigInt`s."); Issue("`ToString()` doesn't return value `BigInt` was initialized with" @ "a positive `int`."); TEST_ExpectTrue(__().math.ToBigInt(13524).ToString() == "13524"); @@ -70,7 +73,6 @@ protected static function Test_Creating() protected static function Test_ToText() { - Context("Testing `ToText()` method of `BigInt`s."); Issue("`ToText()` doesn't return value `BigInt` was initialized with" @ "a positive integer inside `string`."); TEST_ExpectTrue(__().math @@ -204,7 +206,7 @@ protected static function SubTest_SubtractingSameSignValues() main.Subtract(sub); TEST_ExpectTrue(main.ToString() == "0"); } -//Negative `BigInt`s is incorrectly subtracted from positive one. [2] + protected static function SubTest_SubtractingDifferentSignValues() { local BigInt main, sub; @@ -236,7 +238,30 @@ protected static function SubTest_SubtractingDifferentSignValues() sub = __().math.MakeBigInt_S("728965872936589276"); main.Subtract(sub); TEST_ExpectTrue(main.ToString() == "-1457931745873178552"); - Log("UMBRA TEST"); +} + +protected static function Test_ToInt() +{ + Issue("Testing conversion for non-overflowing values."); + TEST_ExpectTrue(__().math.MakeBigInt_S("0").ToInt() == 0); + TEST_ExpectTrue(__().math.MakeBigInt_S("-0").ToInt() == 0); + TEST_ExpectTrue(__().math.MakeBigInt_S("13524").ToInt() == 13524); + TEST_ExpectTrue(__().math.MakeBigInt_S("-666").ToInt() == -666); + TEST_ExpectTrue(__().math.MakeBigInt_S("2147483647").ToInt() == 2147483647); + TEST_ExpectTrue(__().math.MakeBigInt_S("2147483646").ToInt() == 2147483646); + TEST_ExpectTrue( + __().math.MakeBigInt_S("-2147483648").ToInt() == -2147483648); + TEST_ExpectTrue( + __().math.MakeBigInt_S("-2147483647").ToInt() == -2147483647); + + Issue("Testing conversion for overflowing values."); + TEST_ExpectTrue(__().math.MakeBigInt_S("2147483648").ToInt() == 2147483647); + TEST_ExpectTrue( + __().math.MakeBigInt_S("8342748293074932473246").ToInt() == 2147483647); + TEST_ExpectTrue( + __().math.MakeBigInt_S("-2147483649").ToInt() == -2147483648); + TEST_ExpectTrue( + __().math.MakeBigInt_S("-32545657348437563873").ToInt() == -2147483648); } defaultproperties