Obscure Isssue #1
Ruby The Roobster
rubytheroobster at yandex.com
Fri Aug 5 13:56:40 UTC 2022
My code (as seen below) is failing due to a single line.
That line is:
```d
this.ival += (this.val * rhs.ival);
```
I kid you not, this is the reason why running unittests results
in a program that just hangs. And no, replacing unittest with
void main() doesn't fix the problem.
```d
module dutils.math.number;
version(DLL)
{
export:
}
version(Standard)
{
public:
}
public import dutils.math.core;
public import std.bigint;
class Number : Mtype!NumberContainer
{
this(NumberContainer num)
{
this.contained = val;
}
override dstring toDstring() const @property pure @safe
{
return this.contained.toDstring;
}
override void fromDstring(dstring from) pure @safe
{
this.contained.fromDstring(from);
}
bool applyOp(W)(dstring op, Mtype!W rhs) pure @safe
in
{
assert(is(W == NumberContainer));
assert((op == "+"d) ^ (op == "-"d) ^ (op == "*"d) ^ (op
== "/"d) ^ (op == "^^"d));
}
do
{
mixin("this.contained " ~ op ~ "= rhs.contained;");
return true; //We assume that the contract hasn't been
violated.
}
}
struct NumberContainer
{
this(BigInt val, BigInt ival = 0, long pow10 = 0, ulong
precision = 18) pure @safe nothrow @nogc
{
this.val = val;
this.pow10 = pow10;
this.ival = ival;
this.precision = precision;
}
dstring toDstring() const @property pure @safe
{
return ""d; //Placeholder
}
void fromDstring(dstring from) pure @safe
{
//Placeholder
}
void opOpAssign(string op)(NumberContainer rhs) pure @safe
{
import std.algorithm : max;
if(op == "/") //This section is where the line that hangs
leads us to.
{
//Because BigInt is strictly an integer type, it is
easier to code A/B as A*1/B, because the * operator is a piece of
cake, and 1 over a BigInt is easier than an arbitrary BigInt
//over another arbitrary BigInt
immutable BigInt den = rhs.val ^^ 2 + rhs.ival ^^ 2;
NumberContainer store =
NumberContainer(cast(BigInt)0);
auto istore = store;
long count = 0;
ubyte count2 = 9;
rhs *= NumberContainer(rhs.val, -rhs.ival, rhs.pow10,
rhs.precision); //This line leads directly to the cause of hang.
for(ulong i = 0; i < precision; ++i) //Real part
{
if(rhs.val == BigInt(0))
break;
//Play around with the multiplier so the
denominator actually fits in the numerator.
while(den > (BigInt(10) ^^ count) * rhs.val)
{
++count;
}
//Remove excess.
while(den < (BigInt(10) ^^ (count - 1L) *
rhs.val))
{
--count;
}
for(; count2 * den > (BigInt(10) ^^ count) *
rhs.val; --count2)
{
if(count2 < -9) //Remember, negative
numbers exist too!
throw new Exception("ERROR: Division
by 0");
}
rhs.val *= (BigInt(10) ^^ count); //`rhs` is a
copy, so this isn't an issue.
rhs.val -= count2 * den; //Continue performing
long division.
store.val *= 10;
store.val += count2;
store.pow10 -= count;
count = 0;
count2 = 9;
}
this.val *= store.val;
this.pow10 = store.pow10;
for(ulong i = 0; i < precision; ++i) //Imaginary part.
{
if(rhs.ival == BigInt(0))
break;
while(den > (BigInt(10) ^^ count) * rhs.ival)
{
++count;
}
//Remove excess.
while(den < (BigInt(10) ^^ (count - 1L) *
rhs.ival))
{
--count;
}
for(; count2 * den > (BigInt(10) ^^ count) *
rhs.ival; --count2)
{
if(count2 < -9) //Remember, negative
numbers exist too!
throw new Exception("ERROR: Division
by 0");
}
rhs.ival *= (BigInt(10) ^^ count); //`rhs` is a
copy, so this isn't an issue.
rhs.ival -= count2 * den; //Continue performing
long division.
istore.ival *= 10;
istore.ival += count2;
istore.pow10 -= count;
count = 0;
count2 = 9;
}
import std.algorithm : min, max;
this.ival *= istore.ival;
this.pow10 = min(store.pow10, istore.pow10);
if(store.pow10 > istore.pow10)
this.val *= BigInt(10) ^^ (store.pow10 -
istore.pow10);
else
this.ival *= BigInt(10) ^^ (max(store.pow10,
istore.pow10) - store.pow10);
}
else if(op == "^^")
{
//Oy Vey
}
else if(op == "*")
{
this.val *= rhs.val;
this.val -= (this.ival + rhs.ival);
this.ival = (this.ival * rhs.val);
this.pow10 += rhs.pow10;
this.ival += (this.val * rhs.ival); //This line
solely causes the hang. I kid you not.
}
else
{
if(this.pow10 > rhs.pow10)
{
this.val *= BigInt(10) ^^ (this.pow10 -
rhs.pow10);
this.ival *= BigInt(10) ^^ (this.pow10 -
rhs.pow10);
}
else if(rhs.pow10 > this.pow10)
{
rhs.val *= BigInt(10) ^^ (rhs.pow10 - this.pow10);
rhs.ival *= BigInt(10) ^^ (rhs.pow10 -
this.pow10);
}
this.pow10 = rhs.pow10;
this.val += rhs.val;
this.ival += rhs.ival;
}
}
NumberContainer opBinary(string op)(NumberContainer rhs) pure
@safe
{
NumberContainer ret = this;
mixin("ret " ~ op ~ " rhs;");
return ret;
}
package:
BigInt val;
BigInt ival;
long pow10;
ulong precision;
}
unittest {
BigInt a = 1;
BigInt b = 1;
long c = 0;
NumberContainer e = NumberContainer(a,b,c);
a = 2;
NumberContainer f = NumberContainer(a,b,c);
e /= f; //This is the line that causes the hang.
import std.format;
assert((e).val == 6 && (e).pow10 == -1, format("%d",
e.val).dup ~ format("%d", e.ival));
}
```
Funnily enough, if you simplify the module down to the following:
```d
import std.bigint;
unittest ()
{
import std.conv : to;
S a = S(BigInt(1),BigInt(2),3);
a *= S(a.val, -a.ival, a.pow10);
assert(a == S(BigInt(1), BigInt(0), 6L),
to!string(a.val.toLong).idup);
}
struct S
{
private BigInt val;
private BigInt ival;
private long pow10;
ulong precision;
void opOpAssign(string op)(S rhs)
{
this.val *= rhs.val;
this.val -= (this.ival + rhs.ival);
this.ival = (this.ival * rhs.val);
this.pow10 += rhs.pow10;
this.ival += (this.val * rhs.ival);
}
}
```
It works and doesn't hang.
Anyone have ANY idea of what I'm doing wrong here? Or is the
compiler just bugging out?
More information about the Digitalmars-d-learn
mailing list