opCmp / opEquals do not actually support partial orders

Dominikus Dittes Scherkl dominikus.scherkl at continental-corporation.com
Wed Jul 18 14:02:28 UTC 2018


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);
       }
    }
}





More information about the Digitalmars-d mailing list