From 202d182152575db1d744ed5ee89b05a39e5a807b Mon Sep 17 00:00:00 2001 From: safety0ff Date: Sun, 7 Apr 2013 17:33:39 -0400 Subject: [PATCH] Fix std.BigInt bug #9548 plus other new discovered bugs. --- std/bigint.d | 57 +++++++++++++++++++++++++++------ std/internal/math/biguintcore.d | 39 +++++++++++++++++----- 2 files changed, 79 insertions(+), 17 deletions(-) diff --git a/std/bigint.d b/std/bigint.d index a0bfc638cae..45e9115f6b1 100644 --- a/std/bigint.d +++ b/std/bigint.d @@ -115,7 +115,7 @@ public: /// BigInt opAssign(T)(T x) if (isIntegral!T) { - data = cast(ulong)((x < 0) ? -x : x); + data = cast(ulong)absUnsign(x); sign = (x < 0); return this; } @@ -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=="+") { @@ -264,7 +264,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. @@ -282,7 +282,7 @@ public: BigInt opBinaryRight(string op, T)(T y) if (op == "-" && isIntegral!T) { - ulong u = cast(ulong)(y < 0 ? -y : y); + ulong u = absUnsign(y); BigInt r; static if (op == "-") { @@ -303,7 +303,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); @@ -355,7 +355,7 @@ public: { if (sign != (y<0)) return 0; - return data.opEquals(cast(ulong)( y>=0 ? y : -y)); + return data.opEquals(cast(ulong)absUnsign(y)); } /// @@ -363,7 +363,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; } /// @@ -379,7 +379,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); } @@ -388,7 +388,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); } @@ -515,6 +515,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")) @@ -546,6 +563,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 diff --git a/std/internal/math/biguintcore.d b/std/internal/math/biguintcore.d index f1dac8de0cd..372d97b4003 100644 --- a/std/internal/math/biguintcore.d +++ b/std/internal/math/biguintcore.d @@ -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]; @@ -204,15 +211,24 @@ public: /// int opCmp(Tulong)(Tulong y) 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 @@ -835,6 +851,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)