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

- Iain Buclaw (8/8) Jun 30 2015 Hi,
- Walter Bright (2/5) Jun 30 2015 Pretty dazz, Iain!
- tsbockman (68/76) Jun 30 2015 core.checkedint is not very nice to work with directly, so Robert
- tsbockman (28/28) Jun 30 2015 After thinking about the both the exception issue, and the
- Robert burner Schadek (6/8) Jul 01 2015 The name SafeInt and the missing bitwise operations go in tandem.
- tsbockman (49/58) Jul 01 2015 That makes sense. However, I am still interested in knowing
- rsw0x (5/14) Jul 01 2015 all bitwise ops except for shifting are fundamental math
- Robert burner Schadek (7/10) Jul 02 2015 I must have missed something in my math classes.
- tsbockman (13/23) Jul 02 2015 I don't see why the closed-ness of the integers under an
- Robert burner Schadek (5/11) Jul 02 2015 I wanted to have an Integral type. And Math Integrals only have
- Timon Gehr (3/17) Jul 02 2015 Some designs are better than others.
- Timon Gehr (2/3) Jul 02 2015 What does that even mean?
- Robert burner Schadek (2/6) Jul 03 2015 That there are no bitwise operations in math.
- Tofu Ninja (3/10) Jul 03 2015 https://en.wikipedia.org/wiki/Bitwise_operation#Mathematical_equivalents
- rsw0x (4/11) Jul 03 2015 boolean algebra
- Matthias Bentrup (10/17) Jul 03 2015 You imply math == arithmetic, which is false. If you take a look
- Timon Gehr (3/10) Jul 03 2015 One obvious counterexample: https://en.wikipedia.org/wiki/Nimber
- Dominikus Dittes Scherkl (39/79) Jul 03 2015 Yup. For my personal taste, safe unsigned int is rather useless
- Dominikus Dittes Scherkl (415/416) Jul 03 2015 Hmm. Your solution is rather longish.
- Andrea Fontana (2/3) Jul 01 2015 I don't read messages on forum by him for weeks. :|

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

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/110Pretty dazz, Iain!

Jun 30 2015

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

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

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

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: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?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

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: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...)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

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

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: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.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

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

On 07/02/2015 11:22 PM, Robert burner Schadek wrote:On Thursday, 2 July 2015 at 20:24:55 UTC, tsbockman wrote:You're going to need more justification than that.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. ...Some designs are better than others.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

On 07/03/2015 04:17 AM, Timon Gehr wrote:Bitwise are no math operations, these are CS operationsWhat does that even mean?

Jul 02 2015

On Friday, 3 July 2015 at 02:17:59 UTC, Timon Gehr wrote:On 07/03/2015 04:17 AM, Timon Gehr wrote:That there are no bitwise operations in math.Bitwise are no math operations, these are CS operationsWhat does that even mean?

Jul 03 2015

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:https://en.wikipedia.org/wiki/Bitwise_operation#Mathematical_equivalentsOn 07/03/2015 04:17 AM, Timon Gehr wrote:That there are no bitwise operations in math.Bitwise are no math operations, these are CS operationsWhat does that even mean?

Jul 03 2015

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:boolean algebra the math came first.Bitwise are no math operations, these are CS operationsWhat does that even mean?

Jul 03 2015

On Friday, 3 July 2015 at 08:09:11 UTC, Robert burner Schadek wrote:Bitwise are no math operations, these are CS operationsWhat does that even mean?

Jul 03 2015

On 07/03/2015 10:09 AM, Robert burner Schadek wrote:Bitwise are no math operations, these are CS operationsWhat does that even mean?

Jul 03 2015

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!T2. 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.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

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

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