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
  • Loading branch information
n8sh committed Aug 26, 2019
1 parent 9873b65 commit 98f5d9b
Showing 1 changed file with 110 additions and 4 deletions.
114 changes: 110 additions & 4 deletions std/bigint.d
Expand Up @@ -31,6 +31,7 @@ import std.format : FormatSpec, FormatException;
import std.internal.math.biguintcore;
import std.range.primitives;
import std.traits;
import std.math : FloatingPointControl;

/** A struct representing an arbitrary precision integer.
*
Expand Down Expand Up @@ -643,22 +644,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.scalbn which is used
// for 80 bit floats.
return 0 == opCmp(y);
}

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

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

const x = BigInt("0x1abc_de80_0000_0000_0000_0000_0000_0000");
BigInt x1 = x + 1;
BigInt x2 = x - 1;

const d = 0x1.abcde8p124;
assert(x == d);
assert(x1 != d);
assert(x2 != d);
assert(x != nextUp(d));
assert(x != nextDown(d));
assert(x != double.nan);

const dL = 0x1.abcde8p124L;
assert(x == dL);
assert(x1 != dL);
assert(x2 != dL);
assert(x != nextUp(dL));
assert(x != nextDown(dL));
assert(x != real.nan);
}

/**
Implements casting to `bool`.
*/
Expand Down Expand Up @@ -953,14 +987,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 = opCast!(T, FloatingPointControl.roundToZero);
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 @@ -983,6 +1045,50 @@ 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);
}

@system unittest
{
// Test that rounding of opCast!real is as opCmp!real expects.
auto x = BigInt("0x1abc_de80_0000_0000_0000_0000_0000_0000");
BigInt y = x - 1;
BigInt z = x + 1;

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

// Test comparison for numbers of 64 bits or fewer.
auto w1 = BigInt(0x1abc_de80_0000_0000);
auto w2 = w1 - 1;
auto w3 = w1 + 1;
assert(w1.ulongLength == 1);
assert(w2.ulongLength == 1);
assert(w3.ulongLength == 1);

double e = 0x1.abcde8p+60;
assert(w1 >= e && w1 <= e);
assert(w2 < e);
assert(w3 > e);

real eL = 0x1.abcde8p+60;
assert(w1 >= eL && w1 <= eL);
assert(w2 < eL);
assert(w3 > eL);
}

/**
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 98f5d9b

Please sign in to comment.