On Wednesday, 18 July 2018 at 13:12:05 UTC, Ivan Kazmenko wrote:
On Tuesday, 17 July 2018 at 21:18:12 UTC, John Colvin wrote:
Just do what std.typecons.Proxy does and return float.nan for
the incomparable case.
Isn't it slow though on current processors? I just threw
together a test program.
Leaving x uninitialized, or using floats, work about the same.
No, floats are a whole lot less slow.
But I agree, I would still prefer to have a signed bool which
uses only 1 byte
and provide _much_ faster comparison to NaN. And I have created
one,
but as it is not supported by the language natively, it
internally has to use
float, so is not faster (or even slower):
/// signed boolean type (2bit), has the values neg (0b11), zero
(0b00), pos (0b01) and nan (0b10)
/// this is an extended boolean that split "true" into
positive/negative and "false" into zero/NaN
struct sbool
{
pure:
@safe:
@nogc:
nothrow:
enum { neg = 0b11, zero = 0b00, pos = 0b01, nan = 0b10 };
this(T)(const(T) x) if(isNumeric!T)
{
static if(is(Unqual!T == sbool))
val = x.val; /// pos -> 1, neg -> 3, zero -> 0, nan -> 2
else static if(is(Unqual!T == bool))
val = x ? pos : zero;
else static if(isUnsigned!T)
val = x.isInvalid ? nan : (x>0) ? pos : zero;
else // signed or safe signed
val = x.isInvalid ? nan : (x<0) ? neg : (x>0) ? pos :
zero;
}
T opCast(T)() const if(isNumeric!T)
{
static if(is(Unqual!T == sbool))
return this;
else static if(is(Unqual!T == bool))
return val&1; // pos and neg are mapped to true, zero
and NaN are mapped to false
else static if(isFloatingPoint!T)
return tsgn[val];
else
return val == nan ? invalid!T : cast(T)tsgn[val];
}
sbool opAssign(T)(const(T) x) if(isNumeric!T)
{
return this(x);
}
sbool opOpAssign(string op)(const sbool x) if(op == "+" || op
== "-" || op == "*" || op == "/" || op == "%")
{
static if(op == "+") // attention! pos+neg = neg+pos = nan
!!
val = tadd[val];
else static if(op == "-") // attention! pos-pos = neg-neg =
nan !!
val = tsub[val];
else static if(op == "*")
val = tmul[val];
else static if(op == "/")
val = tdiv[val];
else static if(op == "%")
val = trem[val];
val >>= x.val<<1;
val &= 3;
return this;
}
sbool opUnary(string op)() if(op == "++" || op == "--")
{
mixin(op~"val");
val &= 3;
return this;
}
sbool opUnary(string op)() const if(op == "+" || op == "-" ||
op == "~")
{
static if(op == "+") return this;
sbool r = this;
mixin("r.val = "~op~"r.val");
r.val &= 3;
return r;
}
sbool opBinary(string op)(const(sbool) x) const if(op == "+"
|| op == "-" || op == "*" || op == "/" || op == "%")
{
sbool r = this;
return mixin("r "~op~"= x");
}
Signed!T opBinary(string op, T)(const(T) x) const
if(isNumeric!T && op == "*")
{
static if(isUnsigned!T)
{
alias R = Signed!T;
if(val == nan || x.isInvalid) return invalid!R;
if(val == zero) return 0;
if(x > R.max) return invalid!R;
return (val == pos) ? R(x) : -R(x);
}
else // signed or float: return type is same as T
{
if(x.isInvalid) return x;
final switch(val)
{
case pos: return x;
case neg: return -x;
case zero: return 0;
case nan: return invalid!T;
}
}
}
Signed!T opBinaryRight(string op, T)(const(T) x) const
if(isNumeric!T && op == "*")
{
return opBinary!"*"(x);
}
private:
ubyte val = nan;
static immutable float[4] tsgn = [ 0.0f, 1.0f, float.init,
-1.0f ];
// composition tables 0: -1 N 1 0 1: -1 N 1
0 N: -1 N 1 0 -1: -1 N 1 0
static immutable ubyte[4] tadd = [ 0b_11_10_01_00,
0b_10_10_01_01, 0b_10_10_10_10, 0b_11_10_10_11 ];
static immutable ubyte[4] tsub = [ 0b_01_10_11_00,
0b_01_10_10_01, 0b_10_10_10_10, 0b_10_10_11_11 ];
static immutable ubyte[4] tmul = [ 0b_00_10_00_00,
0b_11_10_01_00, 0b_10_10_10_10, 0b_01_10_11_00 ];
static immutable ubyte[4] tdiv = [ 0b_00_10_00_10,
0b_11_10_01_10, 0b_10_10_10_10, 0b_01_10_11_10 ];
static immutable ubyte[4] trem = [ 0b_00_10_00_10,
0b_01_10_01_10, 0b_10_10_10_10, 0b_11_10_11_10 ];
// remainder table is designed so, that if you define
// quot = (abs(a)/abs(b)) * (sbool(a)/sbool(b));
// rem = (abs(a)%abs(b)) * (sbool(a)%sbool(b));
// then assert(a == quot*b + rem) holds for all (a,b) with
b != 0
}
unittest
{
byte quot, rem;
for(byte a = 127; a >= -127; --a)
{
for(byte b = 127; b >= -127; --b)
{
quot = cast(byte)( (abs(a)/abs(b)) * (sbool(a)/sbool(b))
);
rem = cast(byte)( (abs(a)%abs(b)) * (sbool(a)%sbool(b))
);
assert((b == 0 && isInvalid(quot) && isInvalid(rem)) ||
a == quot*b + rem);
}
}
}