Skip to content

Commit

Permalink
Merge pull request #1231 from Safety0ff/master
Browse files Browse the repository at this point in the history
Fix std.BigInt bug #9548 plus other new discovered bugs.
  • Loading branch information
andralex committed May 26, 2013
2 parents ebb1457 + 202d182 commit e54288b
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 17 deletions.
57 changes: 48 additions & 9 deletions std/bigint.d
Expand Up @@ -115,7 +115,7 @@ public:
///
BigInt opAssign(T)(T x) pure if (isIntegral!T)
{
data = cast(ulong)((x < 0) ? -x : x);
data = cast(ulong)absUnsign(x);
sign = (x < 0);
return this;
}
Expand All @@ -133,7 +133,7 @@ public:
if ((op=="+" || op=="-" || op=="*" || op=="/" || op=="%"
|| op==">>" || op=="<<" || op=="^^") && isIntegral!T)
{
ulong u = cast(ulong)(y < 0 ? -y : y);
ulong u = absUnsign(y);

static if (op=="+")
{
Expand Down Expand Up @@ -265,7 +265,7 @@ public:
if (op == "%" && isIntegral!T)
{
assert(y!=0);
uint u = y < 0 ? -y : y;
uint u = absUnsign(y);
int rem = BigUint.modInt(data, u);
// x%y always has the same sign as x.
// This is not the same as mathematical mod.
Expand All @@ -283,7 +283,7 @@ public:
BigInt opBinaryRight(string op, T)(T y) pure
if (op == "-" && isIntegral!T)
{
ulong u = cast(ulong)(y < 0 ? -y : y);
ulong u = absUnsign(y);
BigInt r;
static if (op == "-")
{
Expand All @@ -304,7 +304,7 @@ public:
// x%y always has the same sign as x.
if (data.ulongLength() > 1)
return x;
ulong u = x < 0 ? -x : x;
ulong u = absUnsign(x);
ulong rem = u % data.peekUlong(0);
// x%y always has the same sign as x.
return cast(T)((x<0) ? -rem : rem);
Expand Down Expand Up @@ -356,7 +356,7 @@ public:
{
if (sign != (y<0))
return 0;
return data.opEquals(cast(ulong)( y>=0 ? y : -y));
return data.opEquals(cast(ulong)absUnsign(y));
}

///
Expand All @@ -370,7 +370,7 @@ public:
{
if (sign != (y<0) )
return sign ? -1 : 1;
int cmp = data.opCmp(cast(ulong)(y >= 0 ? y : -y));
int cmp = data.opCmp(cast(ulong)absUnsign(y));
return sign? -cmp: cmp;
}
///
Expand All @@ -386,7 +386,7 @@ public:
long toLong() pure const
{
return (sign ? -1 : 1) *
(data.ulongLength() == 1 && (data.peekUlong(0) <= cast(ulong)(long.max))
(data.ulongLength() == 1 && (data.peekUlong(0) <= sign+cast(ulong)(long.max)) // 1+long.max = |long.min|
? cast(long)(data.peekUlong(0))
: long.max);
}
Expand All @@ -395,7 +395,7 @@ public:
int toInt() pure const
{
return (sign ? -1 : 1) *
(data.uintLength() == 1 && (data.peekUint(0) <= cast(uint)(int.max))
(data.uintLength() == 1 && (data.peekUint(0) <= sign+cast(uint)(int.max)) // 1+int.max = |int.min|
? cast(int)(data.peekUint(0))
: int.max);
}
Expand Down Expand Up @@ -522,6 +522,23 @@ string toHex(BigInt x)
return outbuff;
}

// Returns the absolute value of x converted to the corresponding unsigned type
Unsigned!T absUnsign(T)(T x) if (isIntegral!T)
{
static if (isSigned!T)
{
/* This returns the correct result even when x = T.min
* on two's complement machines because unsigned(T.min) = |T.min|
* even though -T.min = T.min.
*/
return unsigned((x < 0) ? -x : x);
}
else
{
return x;
}
}

unittest {
// Radix conversion
assert( toDecimalString(BigInt("-1_234_567_890_123_456_789"))
Expand Down Expand Up @@ -553,6 +570,28 @@ unittest {
assert((-4) % BigInt(5) == -4); // bug 5928
assert(BigInt(-4) % BigInt(5) == -4);
assert(BigInt(2)/BigInt(-3) == BigInt(0)); // bug 8022
assert(BigInt("-1") > long.min); // bug 9548
}

unittest // Minimum signed value bug tests.
{
assert(BigInt("-0x8000000000000000") == BigInt(long.min));
assert(BigInt("-0x8000000000000000")+1 > BigInt(long.min));
assert(BigInt("-0x80000000") == BigInt(int.min));
assert(BigInt("-0x80000000")+1 > BigInt(int.min));
assert(BigInt(long.min).toLong() == long.min); // lossy toLong bug for long.min
assert(BigInt(int.min).toInt() == int.min); // lossy toInt bug for int.min
assert(BigInt(long.min).ulongLength == 1);
assert(BigInt(int.min).uintLength == 1); // cast/sign extend bug in opAssign
BigInt a;
a += int.min;
assert(a == BigInt(int.min));
a = int.min - BigInt(int.min);
assert(a == 0);
a = int.min;
assert(a == BigInt(int.min));
assert(int.min % (BigInt(int.min)-1) == int.min);
assert((BigInt(int.min)-1)%int.min == -1);
}

unittest // Recursive division, bug 5568
Expand Down
39 changes: 31 additions & 8 deletions std/internal/math/biguintcore.d
Expand Up @@ -73,6 +73,13 @@ else static if (BigDigit.sizeof == long.sizeof)
}
else static assert(0, "Unsupported BigDigit size");

private import std.traits:isIntegral;
enum BigDigitBits = BigDigit.sizeof*8;
template maxBigDigits(T) if (isIntegral!T)
{
enum maxBigDigits = (T.sizeof+BigDigit.sizeof-1)/BigDigit.sizeof;
}

enum BigDigit [] ZERO = [0];
enum BigDigit [] ONE = [1];
enum BigDigit [] TWO = [2];
Expand Down Expand Up @@ -197,15 +204,24 @@ public:
///
int opCmp(Tulong)(Tulong y) pure if(is (Tulong == ulong))
{
if (data.length > 2)
if (data.length > maxBigDigits!Tulong)
return 1;
uint ylo = cast(uint)(y & 0xFFFF_FFFF);
uint yhi = cast(uint)(y >> 32);
if (data.length == 2 && data[1] != yhi)
return data[1] > yhi ? 1: -1;
if (data[0] == ylo)
return 0;
return data[0] > ylo ? 1: -1;

foreach_reverse (i; 0 .. maxBigDigits!Tulong)
{
BigDigit tmp = cast(BigDigit)(y>>(i*BigDigitBits));
if (tmp == 0)
if (data.length >= i+1)
return 1;
else
continue;
else
if (i+1 > data.length)
return -1;
else if (tmp != data[i])
return data[i] > tmp ? 1 : -1;
}
return 0;
}

bool opEquals(Tdummy = void)(ref const BigUint y) pure const
Expand Down Expand Up @@ -830,6 +846,13 @@ public:

} // end BigUint

unittest
{
// ulong comparison test
BigUint a = [1];
assert(a == 1);
assert(a < 0x8000_0000_0000_0000UL); // bug 9548
}

// Remove leading zeros from x, to restore the BigUint invariant
BigDigit[] removeLeadingZeros(BigDigit [] x) pure
Expand Down

0 comments on commit e54288b

Please sign in to comment.