www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - GDC adds intrinsic support for core.checkedint

reply "Iain Buclaw" <ibuclaw gdcproject.org> writes:
Hi,

GDC now recognizes all functions in core.checkedint as 
intrinsics.  May you all enjoy using inlined, hardware leveraged, 
overflow-checking integer operations.

https://github.com/D-Programming-GDC/GDC/pull/110

I say 'you all', but what I really mean is 'bearophile'. :-)

Regards
Iain.
Jun 30 2015
next sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 6/30/2015 11:25 AM, Iain Buclaw wrote:
 GDC now recognizes all functions in core.checkedint as intrinsics.  May you all
 enjoy using inlined, hardware leveraged, overflow-checking integer operations.

 https://github.com/D-Programming-GDC/GDC/pull/110
Pretty dazz, Iain!
Jun 30 2015
prev sibling next sibling parent reply "tsbockman" <thomas.bockman gmail.com> writes:
On Tuesday, 30 June 2015 at 18:25:14 UTC, Iain Buclaw wrote:
 Hi,

 GDC now recognizes all functions in core.checkedint as 
 intrinsics.  May you all enjoy using inlined, hardware 
 leveraged, overflow-checking integer operations.

 https://github.com/D-Programming-GDC/GDC/pull/110

 I say 'you all', but what I really mean is 'bearophile'. :-)

 Regards
 Iain.
core.checkedint is not very nice to work with directly, so Robert burner Schadek and I have been working on a wrapper struct for Phobos. Robert's version: https://github.com/D-Programming-Language/phobos/pull/3389 My version: https://github.com/tsbockman/CheckedInt Some comparative benchmarks (note: these predate Iain's update to GDC): https://github.com/D-Programming-Language/phobos/pull/3389#issuecomment-115997507 I don't think either of our versions is ready to be pulled - for one thing, mine has no documentation yet. However, we could use some feedback from anyone interested in making use of checked integer arithmetic: 1. What name do you prefer? SafeInt!T? CheckedInt!T? Something else? 2. Regardless of which name is selected, it will still be rather verbose. I myself intend to use aliases like these to streamline things: alias cint = CheckedInt!int; alias cuint = CheckedInt!uint; // etc... static if(debug) { alias dint = CheckedInt!int; alias duint = CheckedInt!uint; // etc... } else { alias dint = int; alias duint = uint; // etc... } (These aliases could easily be placed in a separate module, or in a mixin template, to prevent namespace pollution when they aren't wanted.) Would anyone else like to see aliases like these included in Phobos? 3. I know D is on a big nogc nothrow kick right now, but this seems like something where exceptions could be really useful. It would be easy to forget to manually check .isNaN. I think a template option should be included to throw an exception when attempting to unwrap a NaN value. The API I have in mind would still allow unchecked unwrapping when explicitly requested, and the performance hit from the extra branches should be minimal. The disadvantage of throwing when unwrapping is that the exception might be thrown much later than the NaN value was actually generated. The alternative would be to throw immediately when a NaN is generated. However, this would be substantially slower in at least some cases. Thoughts? 4. Robert has suggested that a SafeInt type should disable bitwise operations like ~, &, |, ^, but I don't understand why. It seems highly desirable to me to have NaN values propagate sensibly through all standard integer "math" - not only the operators, but also things like core.bitop and the integer operations in std.math. Ideally, I would like to have a sufficiently complete set of CheckedInt!T-aware math operations, that the implicit conversion to T could be removed so that the compiler would remind people to check isNaN before passing the wrapped value to non-CheckedInt!T-aware functions. (I'm not sure it will really make sense to go that far, though, since this scheme would still add syntactic friction to things like indexing an array with a CheckedInt.) What features do you want? 5. For anyone who cares to take a look at our work-in-progress code, which version looks more promising to you at this point? Robert's, or mine?
Jun 30 2015
next sibling parent "tsbockman" <thomas.bockman gmail.com> writes:
After thinking about the both the exception issue, and the 
implicit conversion issue, I believe the best solution would be 
something like this:

Have an additional template parameter to CheckedInt!T which 
controls whether an implicit conversion to type T is available or 
not. If there is an implicit conversion, attempting to use it on 
a NaN value will throw an exception.

This approach will keep the user reasonably safe from 
accidentally ignoring a NaN condition for both the throwing and 
the nothrow versions. The throwing version will just be a little 
more syntactically convenient.

This behaviour should probably extend to explicit opCast!V() 
calls as well, where V is an integral type.

Unchecked unwrapping will always be available either way, by 
using CheckedInt.value.

To ease the writing of generic code which might accept a built-in 
integer or a CheckedInt, a template function like this can be 
included:

auto numValue(T)(in T t) pure
	if(isNumeric!T || isCheckedInt!T || isCIStore!T)
{
	static if(isCheckedInt!T || isCIStore!T)
		return t.value;
	else
		return t;
}

(Something like this actually already exists in both my version 
and Robert's version, for internal use.)
Jun 30 2015
prev sibling next sibling parent reply "Robert burner Schadek" <rburners gmail.com> writes:
On Tuesday, 30 June 2015 at 20:24:38 UTC, tsbockman wrote:
     4. Robert has suggested that a SafeInt type should disable 
 bitwise operations like ~, &, |, ^, but I don't understand why.
The name SafeInt and the missing bitwise operations go in tandem. SafeInt is a math type, +-/*% nothing more. Whenever I saw people beginning cleaver and use bitwise operations for math, they failed. Me included. Thats why
Jul 01 2015
next sibling parent "tsbockman" <thomas.bockman gmail.com> writes:
On Wednesday, 1 July 2015 at 21:15:33 UTC, Robert burner Schadek 
wrote:
 On Tuesday, 30 June 2015 at 20:24:38 UTC, tsbockman wrote:
     4. Robert has suggested that a SafeInt type should disable 
 bitwise operations like ~, &, |, ^, but I don't understand why.
The name SafeInt and the missing bitwise operations go in tandem. SafeInt is a math type, +-/*% nothing more. Whenever I saw people beginning cleaver and use bitwise operations for math, they failed. Me included. Thats why
That makes sense. However, I am still interested in knowing whether others generally share your view, or mine. I, personally, find bitwise operations useful. Bitwise operators are not just for "clever" code; the use of `x << y` rather than `x * 2^^y` both ensures good code gen, and is clear and concise. API-wise, there are a few different options: 1. Disable bitwise operations on `SafeInt`. Pros: Discourages the use of bug-prone optimization techniques. Cons: Code that actually has a good reason to use bitwise operators either ends up being rewritten in a slow/unnatural/bug-prone fashion using the approved operators, or it is written against `SafeInt.value` and is not only unchecked itself, but also disrupts the propagation of NaN states from earlier checked operations. 2. Include checked (NaN propagating) bitwise operations in `SafeInt`. Pros: Allows safe, efficient use of bitwise operations where appropriate. Cons: Allows abuse of bitwise operations to write inscrutible "clever" code. 3. Make checked bitwise operations optional according to an additional template parameter to `SafeInt`. Pros: Allows safe, efficient use of bitwise operations where appropriate. Where they are not needed, the programmer may request that the compiler block their abuse. Cons: Complicates the design and usage of `SafeInt`. I now understand your motivation for banning the use of bitwise operations with `SafeInt`. However, I really don't think removing them entirely from `SafeInt` is a good idea; it takes so little code to work around this restriction that people (me, for one) will just use them anyway. Without checked versions in `SafeInt`, more code that converts between `SafeInt` and built-in integral types will be written. Unless we always throw an exception when unwrapping a NaN value, such code will tend to silently eat NaN states and hide bugs even in the preceding checked arithmetic. More generally, all the bitwise operators except the shifts (`<<`, `>>`, and `>>>`) have very well-defined and predictable behaviour. Even the shifts are no more confusing to work with than the equivalent composites of `*` or `/` with `2^^`. It doesn't really seem to fit the spirit of D to ban such well-behaved basic tools just because people sometimes try to use them for things they shouldn't. People write incorrect algorithms or forget to handle corner cases when working with the normal arithmetic operators all the time; should we ban them?
Jul 01 2015
prev sibling parent reply "rsw0x" <anonymous anonymous.com> writes:
On Wednesday, 1 July 2015 at 21:15:33 UTC, Robert burner Schadek 
wrote:
 On Tuesday, 30 June 2015 at 20:24:38 UTC, tsbockman wrote:
     4. Robert has suggested that a SafeInt type should disable 
 bitwise operations like ~, &, |, ^, but I don't understand why.
The name SafeInt and the missing bitwise operations go in tandem. SafeInt is a math type, +-/*% nothing more. Whenever I saw people beginning cleaver and use bitwise operations for math, they failed. Me included. Thats why
all bitwise ops except for shifting are fundamental math operators just like addition and subtraction if you view an integer as a bitstring(which is what it is...)
Jul 01 2015
parent reply "Robert burner Schadek" <rburners gmail.com> writes:
On Thursday, 2 July 2015 at 00:29:46 UTC, rsw0x wrote:
 all bitwise ops except for shifting are fundamental math 
 operators just like addition and subtraction if you view an 
 integer as a bitstring(which is what it is...)
I must have missed something in my math classes. "Like the natural numbers, Z is closed under the operations of addition and multiplication" [1] build in Integer are not really closed, but close enough. No mentioning of shift operations though. [1] https://en.wikipedia.org/wiki/Integer
Jul 02 2015
parent reply "tsbockman" <thomas.bockman gmail.com> writes:
On Thursday, 2 July 2015 at 11:26:24 UTC, Robert burner Schadek 
wrote:
 On Thursday, 2 July 2015 at 00:29:46 UTC, rsw0x wrote:
 all bitwise ops except for shifting are fundamental math 
 operators just like addition and subtraction if you view an 
 integer as a bitstring(which is what it is...)
I must have missed something in my math classes. "Like the natural numbers, Z is closed under the operations of addition and multiplication" [1] build in Integer are not really closed, but close enough. No mentioning of shift operations though. [1] https://en.wikipedia.org/wiki/Integer
I don't see why the closed-ness of the integers under an operation should determine whether it makes the cut or not, but if that's the standard: The built-in integer types are truly closed under the bitwise operators `~`, `&`, `|`, and `^`, with no overflow, underflow, or undefined combinations. In contrast, unary `-` can overflow, while binary `+`, `-`, and `*` can all overflow or underflow. `<<` and `>>` are not closed, but they are conceptually identical to `* 2^^` and `/ 2^^`, respectively. Their invalid input combinations can be handled the same way.
Jul 02 2015
parent reply "Robert burner Schadek" <rburners gmail.com> writes:
On Thursday, 2 July 2015 at 20:24:55 UTC, tsbockman wrote:
 I don't see why the closed-ness of the integers under an 
 operation should determine whether it makes the cut or not, but 
 if that's the standard:
I wanted to have an Integral type. And Math Integrals only have the base operations add and mul. Shift just feels wrong to me.
 The built-in integer types are truly closed under the bitwise 
 operators `~`, `&`, `|`, and `^`, with no overflow, underflow, 
 or undefined combinations.
Bitwise are no math operations, these are CS operations In the end it is a design decision. And that's how I chose.
Jul 02 2015
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 07/02/2015 11:22 PM, Robert burner Schadek wrote:
 On Thursday, 2 July 2015 at 20:24:55 UTC, tsbockman wrote:
 I don't see why the closed-ness of the integers under an operation
 should determine whether it makes the cut or not, but if that's the
 standard:
I wanted to have an Integral type. And Math Integrals only have the base operations add and mul. Shift just feels wrong to me. ...
You're going to need more justification than that.
 The built-in integer types are truly closed under the bitwise
 operators `~`, `&`, `|`, and `^`, with no overflow, underflow, or
 undefined combinations.
Bitwise are no math operations, these are CS operations In the end it is a design decision. And that's how I chose.
Some designs are better than others.
Jul 02 2015
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 07/03/2015 04:17 AM, Timon Gehr wrote:
 Bitwise are no math operations, these are CS operations
What does that even mean?
Jul 02 2015
parent reply "Robert burner Schadek" <rburners gmail.com> writes:
On Friday, 3 July 2015 at 02:17:59 UTC, Timon Gehr wrote:
 On 07/03/2015 04:17 AM, Timon Gehr wrote:
 Bitwise are no math operations, these are CS operations
What does that even mean?
That there are no bitwise operations in math.
Jul 03 2015
next sibling parent "Tofu Ninja" <emmons0 purdue.edu> writes:
On Friday, 3 July 2015 at 08:09:11 UTC, Robert burner Schadek 
wrote:
 On Friday, 3 July 2015 at 02:17:59 UTC, Timon Gehr wrote:
 On 07/03/2015 04:17 AM, Timon Gehr wrote:
 Bitwise are no math operations, these are CS operations
What does that even mean?
That there are no bitwise operations in math.
https://en.wikipedia.org/wiki/Bitwise_operation#Mathematical_equivalents
Jul 03 2015
prev sibling next sibling parent "rsw0x" <anonymous anonymous.com> writes:
On Friday, 3 July 2015 at 08:09:11 UTC, Robert burner Schadek 
wrote:
 On Friday, 3 July 2015 at 02:17:59 UTC, Timon Gehr wrote:
 On 07/03/2015 04:17 AM, Timon Gehr wrote:
 Bitwise are no math operations, these are CS operations
What does that even mean?
That there are no bitwise operations in math.
boolean algebra the math came first.
Jul 03 2015
prev sibling next sibling parent "Matthias Bentrup" <matthias.bentrup googlemail.com> writes:
On Friday, 3 July 2015 at 08:09:11 UTC, Robert burner Schadek 
wrote:
 On Friday, 3 July 2015 at 02:17:59 UTC, Timon Gehr wrote:
 On 07/03/2015 04:17 AM, Timon Gehr wrote:
 Bitwise are no math operations, these are CS operations
What does that even mean?
That there are no bitwise operations in math.
You imply math == arithmetic, which is false. If you take a look at e.g. Algebraic Coding Theory, it basically uses bitwise operations to model polynominal arithmetic. If you want a mathematically correct representation of the integers modulo 2^n (which is what most closely resembles an int in math), you'll also have to disallow the less than and greater than operators, as they cannot be defined sensibly on a cyclic group.
Jul 03 2015
prev sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 07/03/2015 10:09 AM, Robert burner Schadek wrote:
 On Friday, 3 July 2015 at 02:17:59 UTC, Timon Gehr wrote:
 On 07/03/2015 04:17 AM, Timon Gehr wrote:
 Bitwise are no math operations, these are CS operations
What does that even mean?
That there are no bitwise operations in math.
One obvious counterexample: https://en.wikipedia.org/wiki/Nimber (Also, clearly, bitwise operations are mathematical objects.)
Jul 03 2015
prev sibling parent reply "Dominikus Dittes Scherkl" writes:
On Tuesday, 30 June 2015 at 20:24:38 UTC, tsbockman wrote:
     Robert's version: 
 https://github.com/D-Programming-Language/phobos/pull/3389
     My version:       https://github.com/tsbockman/CheckedInt

     Some comparative benchmarks (note: these predate Iain's 
 update to GDC):
 https://github.com/D-Programming-Language/phobos/pull/3389#issuecomment-115997507

 I don't think either of our versions is ready to be pulled - 
 for one thing, mine has no documentation yet. However, we could 
 use some feedback from anyone interested in making use of 
 checked integer arithmetic:

     1. What name do you prefer? SafeInt!T? CheckedInt!T? 
 Something else?
I would prefer SafeInt!T
     2. Regardless of which name is selected, it will still be 
 rather verbose. I myself intend to use aliases like these to 
 streamline things:

 alias cint = CheckedInt!int;
 alias cuint = CheckedInt!uint;
Yup. For my personal taste, safe unsigned int is rather useless - if I use unsigned, I'm almost always about to do something including bit-operations for which overflow check is distracting at the best. For the signed types I use the abbreviations with "s" in front, and 16bit I still call a word, so the list beeing: sbyte, sword, sint, slong (, scent) I love to swing my sword!
        (These aliases could easily be placed in a separate 
 module, or in a mixin template, to prevent namespace pollution 
 when they aren't wanted.)

        Would anyone else like to see aliases like these 
 included in Phobos?
Yes! I would always recommend to use those instead of the buildin signed types, because signed types should be the first thing everybody uses and should protect you from shooting in your foot the best - they will be heavily used in D-scripts, where speed is not the main goal and not used for bit-operations anyway.
     3. I know D is on a big  nogc nothrow kick right now, but 
 this seems like something where exceptions could be really 
 useful. It would be easy to forget to manually check .isNaN.
Maybe, but if you print out the end result and see a "NaN", you are immediately sure there has happened an overflow somewhere and searching the bug will be very easy. If you want to throw an exception, do it yourself! I don't want a safe type to do it for me - it destroys the reason for having a NaN value.
     4. Robert has suggested that a SafeInt type should disable 
 bitwise operations like ~, &, |, ^, but I don't understand why.
I do - for signed types they make very little sence and are likely to destroy the bit pattern - so either you need to check if the pattern becomes NaN or was NaN from the start, or you allow that those operations create a number from a NaN input - both very bad. And as already mentioned: I will never use unsigned safe int, because I need the bit operations for unsigned types, and they ought to be fast! Because I use unsigned types only if I plan to do something outside standard math and than I have to do all checks myself anyway.
        It seems highly desirable to me to have NaN values 
 propagate sensibly through all standard integer "math"
Yes! especially for ^^
 not only the operators, but also things like core.bitop and the 
 integer operations in std.math.
May be, but I don't think using them for signed types make much sense in the first place.
        Ideally, I would like to have a sufficiently complete 
 set of CheckedInt!T-aware math operations, that the implicit 
 conversion to T could be removed so that the compiler would 
 remind people to check isNaN before passing the wrapped value 
 to non-CheckedInt!T-aware functions.
sounds nice, but I think it's not an easy goal.
        What features do you want?

     5. For anyone who cares to take a look at our 
 work-in-progress code, which version looks more promising to 
 you at this point? Robert's, or mine?
I'm currently taking a deeper look, get my answer later!
Jul 03 2015
parent "Dominikus Dittes Scherkl" writes:
Hmm. Your solution is rather longish.

I had once started something along this lines (but only for 
signed types):

alias sbyte = SafeSigned!byte;
alias sword = SafeSigned!short;
alias sint  = SafeSigned!int;
alias slong = SafeSigned!long;
alias scent = SafeSigned!cent;

template isSafeSigned(T)
{
    enum bool isSafeSigned = is(Unqual!T == sbyte) || is(Unqual!T 
== sword)
    || is(Unqual!T == sint) || is(Unqual!T == slong) || 
is(Unqual!T == scent);
}

template isNumericExt(T)
{
    enum bool isNumericExt = isNumeric!T || isSafeSigned!T;
}

struct SafeSigned(S) if(isIntegral!S && isSigned!S) // one of 
byte, short, int, long or cent
{
pure:
 safe:
 nogc:
nothrow:
    alias ST = this;
    enum S nan = S.min;

    static  property S min() { return -S.max; }
    static  property S max() { return S.max; }
    static  property S init() { return nan; }
    static  property S invalid() { return nan; }
     property bool isNaN() { return val == nan; }
     property string stringof() { return isNaN ? "NaN" : 
val.stringof; }

    this(T)(const(T) x) if(isSafeSigned!T)
    {
       val = safeCast!S(x.val);
    }

    this(T)(const(T) x) if(isNumeric!T)
    {
       val = safeCast!S(x);
    }

    ST opAssign(T)(const(T) x) if(isNumericExt!T)
    {
       return this(x);
    }

    bool opEquals(T)(const(T) x) const if(isNumericExt!T)
    {
       static if(isSafeSigned!T)
          return (isNaN || x.isNaN) ? false : val == x.val;
       else static if(isFloatingPoint!T)
          return (isNaN || x.isNaN) ? false : val == x;
       else static if(T.sizeof < S.sizeof)
          return isNaN ? false : val == cast(S)x;
       else static if(isSigned!T) // T has bigger range
          return isNaN ? false : cast(T)val == x;
       else // overlap
          return (val < 0 || x > max) ? false : (cast(T)val == x);
    }

    ssign_t opCmp(T)(const(T) x) const if(isNumericExt!T)
    {
       static if(isSafeSigned!T)
          return (isNaN || x.isNaN) ? NaN : val.opCmp(x.val);
       else static if(isFloatingPoint!T)
          return (isNaN || x.isNaN) ? NaN : val.opCmp(x);
       else static if(T.sizeof < S.sizeof)
          return isNaN ? NaN : val.opCmp(cast(S)x);
       else static if(isSigned!T) // T has bigger range
          return isNaN ? NaN : (cast(T)val).opCmp(x);
       else // overlap
          return isNaN ? NaN : (val < 0) ? -1 : (x > max) ? 1 : 
(cast(T)val).opCmp(x);
    }

    T opCast(T)() const if(isNumericExt!T)
    {
       static if(Unqual!T == Unqual!S)
          return val;
       else static if(is(Unqual!T == Unqual!ST))
          return this; // not really a cast
       else static if(is(Unqual!T == bool))
          return (isNaN || val == 0) ? false : true;
       else static if(isFloatingPoint!T)
          return isNaN ? invalid!T : cast(T)val;
       else // SafeSigned or Integral
          return (isNaN || val < T.min || val > T.max) ? invalid!T 
: cast(T)val;
    }

    ST opOpAssign(string op, T)(const(T) x) if(isNumericExt!T &&
      (op == "+" || op == "-" || op == "*" || op == "/" || op == 
"%" || op == "^^"
    || op == "&" || op == "|" || op == "^" || op == "<<" || op == 
">>" || op == ">>>"))
    {
       if(isNaN || x.isNaN) return this;
       static if(isSafeSigned!T)
       {
          return mixin("this "~op~"= x.val");
       }
       else static if(op == "^^")
       {
          if(x < 2)
          {
             if(x==0) val = 1;
             else static if(isSigned!T)
             {
                if(x < 0 && abs(val) != 1) val = nan; // no 
inverse is integral
                else if(even(x)) val = 1; // x is negative and 
abs(val) == 1
             }
          }
          else
          {
             auto r = abs(val);
             if(r > 1)
             {
                if(x > byte.max || r >= (S(1)<<(S.sizeof<<2)) || x 
 (maxpow(r)>>(5-bitlen(S.sizeof))))
val = nan; else { r ^^= x; if(r > max) val = nan; else val = (val < 0 && odd(exp)) ? -cast(S)r : cast(S)r; } } else if(val == -1 && even(x)) val = 1; } return this; } else static if(op == "/" || op == "%") { if(!x) val = nan; else mixin("val "~op~"= x"); return this; } else static if(op == "&" || op == ">>" || op == ">>>") { mixin("val "~op~"= x"); return this; } else static if(Unqual!T == Unqual!S || T.sizeof < S.sizeof) { static if(op == "|" || op == "^") { mixin("val "~op~"= x"); } else static if(op == "+" || op == "-" || op == "<<") { mixin("val "~op~"= x"); asm { jno ok; } val = nan; // overflow, set to NaN } else static if(op == "*") { val *= x; asm { jnc ok; } val = nan; // overflow, set to NaN } ok: return this; } else // T has bigger range { if(x >= min && x <= max) return mixin("val "~op~"= cast(S)x"); static if(op == "*" || op == "|" || op == "^" || op == "<<") { // will be definitely out of range val = nan; } else static if(isSigned!T) { static if(op == "+" || op == "-") { mixin("val = cast(ST)( cast(SafeSigned!T)val "~op~" x )"); } } else static if(op == "+") { val = (val<0) ? cast(ST)(x + cast(T)(-val)) : nan; } else static if(op == "-") { val = (val>0) ? -cast(ST)(x - cast(T)val) : nan; } return this; } } // for non-commutative operations overload only the left side operator: ST opBinary(string op, T)(const(T) x) if((isNumericExt!T) && (op == "/" || op == "%" || op == "^^" || op == "<<" || op == ">>" || op == ">>>")) { static if(isFloatingpoint!T) { T r = this; mixin("r "~op~"= x"); return cast(ST)r; } ST r = this; return mixin("r "~op~"= x"); } // for commutative operations use the bigger type as result type ST opBinary(string op, T)(const(T) x) if(((isSafeSigned!T && T.sizeof <= S.sizeof) || isSuperset(S, T)) && (op == "+" || op == "*" || op == "&" || op == "|" || op == "^")) { ST r = this; return mixin("r "~op~"= x"); } ST opBinaryRight(string op, T)(const(T) x) if((isSafeSigned!T || isIntegral!T) && (T.sizeof < S.sizeof) && (op == "+" || op == "*" || op == "&" || op == "|" || op == "^")) { ST r = this; return mixin("r "~op~"= x"); } ST opUnary(string op)() if(op == "++" || op == "--") { if(!isNaN) mixin(op~"val"); // ST.max+1 == ST.min-1 == NaN, so no extra conditions are necessary return this; } private: S val = nan; } /// calculate the maximal power of the value that would fit in an ucent /// for smaller types simply shift down the result /// (e.g. divide by 4 to calc the maxpower that would fit in an uint) ubyte maxpow(const ulong a) pure safe nogc nothrow { assert(a>1); // no useful maxpower exists for 0 and 1 static immutable ubyte[14] mp = [ 127, 80, 63, 55, 49, 45, 43, 40, 38, 37, 35, 34, 33, 32 ]; return (a<139) ? ((a<31) ? ((a<20) ? ((a<16) ? mp[cast(ubyte)a-2] : ((a<18) ? 31 : 30)) : ((a<24) ? ((a<22) ? 29 : 28) : ((a<27) ? 27 : 26))) : ((a<57) ? ((a<41) ? ((a<35) ? 25 : 24) : ((a<48) ? 23 : 22)) : ((a<85) ? ((a<69) ? 21 : 20) : ((a<107) ? 19 : 18)))) : ((a<7132) ? ((a<566) ? ((a<256) ? ((a<185) ? 17 : 16) : ((a<371) ? 15 : 14)) : ((a<1626) ? ((a<921) ? 13 : 12) : ((a<3184) ? 11 : 10))) : ((a<2642246) ? ((a<65536) ? ((a<19113) ? 9 : 8) : ((a<319558) ? 7 : 6)) : ((a<4294967296) ? ((a<50859009) ? 5 : 4) : ((a<6981463658332) ? 3 : 2)))); } /// return true if T can represent every possible value of U. /// e.g. isSuperset!(real, long) is false on systems with real.mant_dig < 63, else true template isSuperset(T, U) if(isNumeric!T && isNumeric!U) { static if(isIntegral!T) { static if(isIntegral!U) // unsigned >= unsigned or signed > unsigned or signed >= signed enum bool isSuperset = is(Unqual!T == Unqual!U) || ((isSigned!T || isUnsigned!U) && (T.sizeof > U.sizeof)); else // floatingpoint types can't be represented by integrals in general enum bool isSuperset = false; } else // T is floatingpoint { static if(isUnsigned!U) enum bool isSuperset = (T.mant_dig > U.sizeof<<3); else static if(isIntegral!U) enum bool isSuperset = (T.mant_dig >= U.sizeof<<3); else enum bool isSuperset = (T.mant_dig >= U.mant_dig); } } /// returns the number of the highest set bit +1 in the given value or 0 if no bit is set size_t bitlen(T)(const(T) a) pure safe nogc nothrow if(isUnsigned!T) { static if(T.sizeof <= size_t.sizeof) // ubyte, ushort or uint, maybe even ulong { return a ? bsr(a)+1 : 0; } else static if(T.sizeof == 8) // ulong if size_t == uint { return a ? a>>32 ? bsr(a>>32)+33 : bsr(a)+1 : 0; } } /// get the absolute value of x as unsigned type. always succeeds, even for T.min Unsigned!T abs(T)(const(T) x) pure safe nogc nothrow if(isIntegral!T) { static if(isSigned!T) if(x < 0) return -x; return x; } /// test if the lowest bit is set (also commonly used on signed values) bool odd(T)(const(T) a) pure safe nogc nothrow if(isIntegral!T) { return !!(a&1); } /// consider T.max to be NaN of unsigned types /// and T.min to be NaN of signed types property bool isNaN(T)(const(T) x) pure safe nogc nothrow if(isIntegral!T) { static if(isSigned!T) return x == T.min; else // unsigned return x == T.max; } /// add a property to numeric types that can be used as return value if a result is out of bounds template invalid(T) if(isNumeric!T) { static if(isFloatingPoint!T) property enum invalid = T.init; else static if(isSigned!T) property enum invalid = T.min; else // unsigned property enum invalid = T.max; } /// returns the save (not invalid) minimum value for the given type template smin(T) if(isNumeric!T) { static if(isFloatingPoint!T) property enum smin = -T.max; // T.min is the smallest representable positive value!! else static if(isSigned!T) property enum smin = T.min+1; else // unsigned property enum smin = T.min; } /// returns the save (not invalid) maximum value for the given type template smax(T) if(isNumeric!T) { static if(isUnsigned!T) property enum smax = T.max-1u; else property enum smax = T.max; } /// return true if the numeric value x is in the range of the target type /// - no matter if low bits get lost in the process bool fitIn(T, U)(const(U) x) pure safe nogc nothrow if(isNumeric!T && isNumeric!U) { static if(isSuperset!(T, U)) return true; else static if(isIntegral!T) { static if(isIntegral!U) { static if(T.min > U.min) if(x < T.min) return false; static if(T.max < U.max) if(x > T.max) return false; return true; } else return x >= T.min && x <= T.max; } else // T is floatingpoint { static if(isIntegral!U) return true; else return x.isNan || x.isInv || (x >= -T.max && x <= T.max); // return false if a value would be converted to infinity } } /// returns x if it fits in the target types range, else invalid!T T safeCast(T, U)(const(U) x) pure safe nogc nothrow if(isNumeric!T && isNumeric!U) { return (!x.isNaN && x.fitIn!T) ? cast(T)x : invalid!T; } unittest { int a = -128; auto b = saveCast!byte(a); assert(is(typeof(b) == byte)); assert(b == -128); auto c = saveCast!ushort(a); assert(is(typeof(c) == ushort)); assert(c.isNaN); real d = 1.0L; assert(saveCast!float(d) == 1.0f); d /= 3; assert(saveCast!float(d).isNaN); // would lose accuracy }
Jul 03 2015
prev sibling parent "Andrea Fontana" <nospam example.com> writes:
On Tuesday, 30 June 2015 at 18:25:14 UTC, Iain Buclaw wrote:
 I say 'you all', but what I really mean is 'bearophile'. :-)
I don't read messages on forum by him for weeks. :|
Jul 01 2015