Skip to content

Commit

Permalink
Fix Issue 20147 - Enable comparison (==, >, >=, <=, <) between std.bi…
Browse files Browse the repository at this point in the history
…gint.BigInt and floating point numbers

Quadruple precision floating point not supported.
  • Loading branch information
n8sh committed Aug 20, 2019
1 parent 76c2198 commit d99bde8
Showing 1 changed file with 75 additions and 5 deletions.
80 changes: 75 additions & 5 deletions std/bigint.d
Original file line number Diff line number Diff line change
Expand Up @@ -643,22 +643,30 @@ public:

/**
Implements `BigInt` equality test with other `BigInt`'s and built-in
integer types.
numeric types.
*/
bool opEquals()(auto ref const BigInt y) const pure @nogc
{
return sign == y.sign && y.data == data;
}

/// ditto
bool opEquals(T)(T y) const pure nothrow @nogc if (isIntegral!T)
bool opEquals(T)(const T y) const pure nothrow @nogc if (isIntegral!T)
{
if (sign != (y<0))
return 0;
return data.opEquals(cast(ulong) absUnsign(y));
}

///
/// ditto
bool opEquals(T)(const T y) const nothrow @nogc if (isFloatingPoint!T)
{
// This is a separate function from the isIntegral!T case
// due to the impurity of std.math.scal1b which is used
// for 80 bit floats.
return 0 == opCmp(y);
}

@system unittest
{
auto x = BigInt("12345");
Expand All @@ -673,6 +681,27 @@ public:
assert(x != w);
}

@system unittest
{
import std.math : nextDown, nextUp;

const x = BigInt("0x1abc_de80_0000_0000_0000_0000_0000_0000");
const d = 0x1.abcde8p124;
assert(x == d);

BigInt x1 = x + 1;
BigInt x2 = x - 1;
assert(x1 != d);
assert(x2 != d);

auto d1 = nextUp(d);
auto d2 = nextDown(d);
assert(x != d1);
assert(x != d2);

assert(x != double.nan);
}

/**
Implements casting to `bool`.
*/
Expand Down Expand Up @@ -781,7 +810,7 @@ public:
*/
T opCast(T)() const nothrow @nogc if (isFloatingPoint!T)
{
import core.bitop: bsr;
import core.bitop : bsr;
static assert(T.mant_dig <= 65, "Casting to quadruple precision floating point not yet supported.");

const ulongLength = data.ulongLength;
Expand Down Expand Up @@ -867,14 +896,42 @@ public:
}

/// ditto
int opCmp(T)(T y) pure nothrow @nogc const if (isIntegral!T)
int opCmp(T)(const T y) pure nothrow @nogc const if (isIntegral!T)
{
if (sign != (y<0) )
return sign ? -1 : 1;
int cmp = data.opCmp(cast(ulong) absUnsign(y));
return sign? -cmp: cmp;
}
/// ditto
int opCmp(T)(const T y) nothrow @nogc const if (isFloatingPoint!T)
{
import core.bitop : bsr;
import std.math : cmp, isFinite;

if (!isFinite(y))
return cmp(T(0), y); // Handles +/- infinity and NaN.
const asFloat = cast(T) this;
if (asFloat != y)
return asFloat > y ? 1 : -1;
const ulongLength = data.ulongLength;
const w1 = data.peekUlong(ulongLength - 1);
const numSignificantBits = (ulongLength - 1) * 64 + bsr(w1) + 1;
for (ptrdiff_t bitsRemainingToCheck = numSignificantBits - T.mant_dig, i = 0;
bitsRemainingToCheck > 0; i++, bitsRemainingToCheck -= 64)
{
auto word = data.peekUlong(i);
if (word == 0)
continue;
// Make sure we're only checking digits that are beyond
// the precision of `y`.
if (bitsRemainingToCheck < 64 && (word << (64 - bitsRemainingToCheck)) == 0)
break; // This can only happen on the last loop iteration.
return isNegative ? -1 : 1;
}
return 0;
}
/// ditto
int opCmp(T:BigInt)(const T y) pure nothrow @nogc const
{
if (sign != y.sign)
Expand All @@ -897,6 +954,19 @@ public:
assert(x < w);
}

///
@system unittest
{
auto x = BigInt("0x1abc_de80_0000_0000_0000_0000_0000_0000");
BigInt y = x - 1;
BigInt z = x + 1;

double d = 0x1.abcde8p124;
assert(y < d);
assert(z > d);
assert(x >= d && x <= d);
}

/**
Returns: The value of this `BigInt` as a `long`, or `long.max`/`long.min`
if outside the representable range.
Expand Down

0 comments on commit d99bde8

Please sign in to comment.