1 /** 2 Checked integer arithmetic operations, functions, and types with improved handling of errors and corner cases compared 3 to the basic integral types. 4 5 $(B Note:) Normally this module should not be imported directly. Instead, import one of 6 $(LINK2 ./throws.html, `checkedint.throws`), $(LINK2 ./asserts.html, `checkedint.asserts`), or 7 $(LINK2 ./noex.html, `checkedint.noex`), depending on which error signalling policy you want to use. (See below.) 8 9 Copyright: Copyright Thomas Stuart Bockman 2015 10 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0). 11 Authors: Thomas Stuart Bockman 12 13 $(BIG $(B Problems solved by `checkedint`)) $(BR) 14 As in many other programming languages (C, C++, Java, etc.) D's basic integral types (such as `int` or `ulong`) are 15 surprisingly difficult to use correctly in the general case, due to variuos departures from the behaviour of ideal 16 mathematical integers: 17 $(UL 18 $(LI Overflow silently wraps around: `assert(uint.max + 1 == 0);`) 19 $(LI Mixed signed/unsigned comparisons often give the wrong result: `assert(-1 > 1u);`) 20 $(LI Mixed signed/unsigned arithmetic operations can also give the wrong result.) 21 $(LI Integer division by zero crashes the program with a mis-named and uncatchable `Floating Point Exception` 22 (FPE).) 23 $(LI `int.min / -1` and `int.min % -1` may also crash with an FPE, even though the latter should simply yield `0`.) 24 $(LI If `x` is any integer value, and `y` is any negative integer value, `x ^^ y` will crash with an FPE.) 25 $(LI No bounds checking is done when casting from one integer type to another.) 26 $(LI The result of the bitshift operations (`<<`, `>>`, `>>>`) is formally undefined if the shift size is less than 27 zero or greater than `(8 * N.sizeof) - 1`.) 28 ) 29 The `checkedint` package offers solutions to all of these issues and more. 30 31 $(BIG $(B `SafeInt` versus `SmartInt`)) $(BR) 32 Two different approaches are available: 33 $(UL 34 $(LI `SmartInt` and `smartOp` strive to actually give the mathematically correct answer whenever possible, rather 35 than just signaling an error.) 36 $(LI `SafeInt` and `safeOp` strive to match the behaviour of the basic integral types exactly, $(B except) that 37 where the behaviour of the basic type is wrong, or very unintuitive, an error is signaled instead.) 38 ) 39 There is no meaningful performance difference between `SafeInt` and `SmartInt`. For general use, choose `SmartInt` to 40 simplify your code and maximize the range of inputs it accepts. 41 42 `SafeInt` is intended mainly as a debugging tool, to help identify problems in code that must also work correctly with 43 the basic integral types. The `DebugInt` `template` `alias` makes it simple to use of `SafeInt` in debug builds, and raw 44 basic types in release builds. 45 46 $(TABLE 47 $(TR $(TD) $(TH `int` (basic type)) $(TH `SafeInt!int`) $(TH `SmartInt!int`)) 48 $(TR $(TH `int.max + 1`) $(TD `0`) $(TD `raise(IntFlag.over)`) $(TD `raise(IntFlag.over)`)) 49 $(TR $(TH `-1 > 1u`) $(TD `true`) $(TD compile-time error) $(TD `false`)) 50 $(TR $(TH `-1 - 2u`) $(TD `4294967293`) $(TD compile-time error) $(TD `-3`)) 51 $(TR $(TH `1 / 0`) $(TD crash by FPE) $(TD `raise(IntFlag.div0)`) $(TD `raise(IntFlag.div0)`)) 52 $(TR $(TH `int.min % -1`) $(TD crash by FPE) $(TD `raise(IntFlag.posOver)`) $(TD `0`)) 53 $(TR $(TH `-1 ^^ -7`) $(TD crash by FPE) $(TD `raise(IntFlag.undef)`) $(TD `-1`)) 54 $(TR $(TH `cast(uint)-1`) $(TD `4294967295`) $(TD compile-time error) $(TD `raise(IntFlag.negOver)`)) 55 $(TR $(TH `-1 >> 100`) $(TD undefined) $(TD `raise(IntFlag.undef)`) $(TD `-1`)) 56 ) 57 58 $(BIG $(B Error Signaling)) $(BR) 59 Some types of problem are signaled by a compile-time error, others at runtime. Runtime signaling is done through 60 $(LINK2 ./flags.html, `checkedint.flags`). Three different runtime signalling policies are available: 61 $(UL 62 $(LI With `IntFlagPolicy.throws`, a `CheckedIntException` is thrown. These are normal exceptions; not FPEs. As 63 such, they can be caught and include a stack trace.) 64 $(LI With `IntFlagPolicy.asserts`, an assertion failure will be triggered. This policy is compatible with 65 `pure nothrow @nogc` code, but will crash the program in the event of a runtime integer math error.) 66 $(LI Alternatively, `IntFlagPolicy.noex` can be selected so that a thread-local flag is set when an operation fails. 67 This allows `checkedint` to be used from `nothrow` and `@nogc` (but not `pure`) code without crashing the 68 program, but requires the API user to manually insert checks of `IntFlags.local`.) 69 ) 70 In normal code, there is no performance penalty for allowing `checkedint` to `throw`. Doing so is highly recommended 71 because this makes it easier to use correctly, and yields more precise error messages when something goes wrong. 72 73 $(BIG $(B Generic Code)) $(BR) 74 The $(LINK2 ./traits.html, `checkedint.traits`) module provides `checkedint`-aware versions of various numerical type 75 traits from `std.traits`, such as `Signed`, `isSigned` and `isIntegral`. This allows writing generic algorithms that 76 work with any of `SmartInt`, `SafeInt`, and the built-in numeric types such as `uint` and `long`. 77 78 Also of note is the `idx()` function, which concisely and safely casts from any integral type (built-in, `SmartInt`, or 79 `SafeInt`) to either `size_t` or `ptrdiff_t` for easy array indexing. 80 81 $(BIG $(B Performance)) $(BR) 82 Replacing all basic integer types with `SmartInt` or `SafeInt` will slow down exectuion somewhat. How much depends on 83 many factors, but for most code following a few simple rules should keep the penalty low: 84 $(OL 85 $(LI Build with $(B $(RED `--inline`)) and $(B `-O`) (DMD) or $(B `-O3`) (GDC and LDC). This by itself can improve 86 the performance of `checkedint` by around $(B 1,000%).) 87 $(LI With GDC or LDC, the performance hit in code that is bottlenecked by integer math will probably be between 30% 88 and 100%. The performance hit may be considerably larger with DMD, due to the weakness of the inliner.) 89 $(LI `checkedint` can't slow down code where it's not used! If you really need more speed, try switching to 90 `DebugInt` for the hottest code in your program (like inner loops) before giving up on `checkedint` entirely.) 91 ) 92 The above guidelines should be more than sufficient for most programs. But, here are some micro-optimization tips as 93 well, if you need them: 94 $(UL 95 $(LI Always use `mulPow2()`, `divPow2()`, and `modPow2()` whenever they can naturally express your intent - they're 96 faster than a regular `/`, `%`, or `pow()`.) 97 $(LI Unsigned types are a little bit faster than signed types, assuming negative values aren't needed.) 98 $(LI Although they are perfectly safe with `checkedint`, mixed signed/unsigned operations are a little bit slower 99 than same-signedness ones.) 100 $(LI The assignment operators (`++` or `+=`, for example) should never be slower than the equivalent two operation 101 sequence, and are sometimes a little bit faster.) 102 ) 103 **/ 104 module checkedint; 105 import checkedint.flags; 106 107 import future.bitop, core.checkedint, std.algorithm, std.format, future.traits0, std.typecons; 108 static import std.math; 109 static if (__VERSION__ >= 2068) 110 { 111 version(GNU) { static assert(false); } 112 import std.meta; 113 } 114 else 115 { 116 import std.typetuple; 117 private alias AliasSeq = TypeTuple; 118 } 119 120 @safe: 121 122 /+pragma(inline, true) 123 {+/ 124 // smart ///////////////////////////////////////////////// 125 /// Wrapper for any basic integral type `N` that uses the checked operations from `smartOp` and bounds checks 126 /// assignments with `checkedint.to()`. 127 /// 128 /// $(UL 129 /// $(LI `policy` controls the error signalling policy (see `checkedint.flags`).) 130 /// $(LI `bitOps` may be set to `No.bitOps` if desired, to turn bitwise operations on this type into a 131 /// compile-time error.) 132 /// ) 133 struct SmartInt(N, IntFlagPolicy _policy, Flag!"bitOps" bitOps = Yes.bitOps) 134 if (isIntegral!N && isUnqual!N) 135 { 136 /// The error signalling policy used by this `SmartInt` type. 137 enum IntFlagPolicy policy = _policy; 138 139 static if (bitOps) 140 { 141 /// The basic integral value of this `SmartInt`. Accessing this directly may be useful for: 142 /// $(UL 143 /// $(LI Intentionally doing modular (unchecked) arithmetic, or) 144 /// $(LI Interacting with APIs that are not `checkedint` aware.) 145 /// ) 146 N bscal; 147 /// 148 unittest 149 { 150 import checkedint.throws : SmartInt; // use IntFlagPolicy.throws 151 152 SmartInt!uint n; 153 static assert(is(typeof(n.bscal) == uint)); 154 155 n = 7; 156 assert(n.bscal == 7); 157 158 n.bscal -= 8; 159 assert(n == uint.max); 160 } 161 162 /// Get a view of this `SmartInt` that allows bitwise operations. 163 @property ref inout(SmartInt!(N, policy, Yes.bitOps)) bits() /+return+/ inout pure nothrow @nogc 164 { 165 return this; 166 } 167 /// 168 unittest 169 { 170 import checkedint.throws : SmartInt; // use IntFlagPolicy.throws 171 172 SmartInt!(int, No.bitOps) n = 1; 173 static assert(!__traits(compiles, n << 2)); 174 assert(n.bits << 2 == 4); 175 } 176 } 177 else 178 { 179 @property ref inout(N) bscal() /+return+/ inout pure nothrow @nogc 180 { 181 return bits.bscal; 182 } 183 SmartInt!(N, policy, Yes.bitOps) bits; 184 } 185 186 static if (__VERSION__ >= 2067) 187 { 188 version(GNU) { static assert(false); } 189 190 /// The most negative possible value of this `SmartInt` type. 191 enum SmartInt!(N, policy, bitOps) min = typeof(this)(trueMin!N); 192 /// 193 unittest 194 { 195 import checkedint.throws : SmartInt; // use IntFlagPolicy.throws 196 197 assert(SmartInt!(int).min == int.min); 198 assert(SmartInt!(uint).min == uint.min); 199 } 200 201 /// The most positive possible value of this `SmartInt` type. 202 enum SmartInt!(N, policy, bitOps) max = typeof(this)(trueMax!N); 203 /// 204 unittest 205 { 206 import checkedint.throws : SmartInt; // use IntFlagPolicy.throws; 207 208 assert(SmartInt!(int).max == int.max); 209 assert(SmartInt!(uint).max == uint.max); 210 } 211 } 212 else 213 { 214 static @property auto min() pure nothrow @nogc 215 { 216 return typeof(this)(trueMin!N); 217 } 218 static @property auto max() pure nothrow @nogc 219 { 220 return typeof(this)(trueMax!N); 221 } 222 } 223 224 // Construction, assignment, and casting ///////////////////////////////////////////////// 225 /// Assign the value of `that` to this `SmartInt` instance. 226 /// 227 /// `checkedint.to()` is used to verify `that >= N.min && that <= N.max`. If not, an `IntFlag` will be raised. 228 this(M)(const M that) 229 if (isCheckedInt!M || isScalarType!M) 230 { 231 this.bscal = to!(N, policy)(that); 232 } 233 /// ditto 234 ref typeof(this) opAssign(M)(const M that) 235 if (isCheckedInt!M || isScalarType!M) 236 { 237 this.bscal = to!(N, policy)(that); 238 return this; 239 } 240 /// 241 unittest 242 { 243 import checkedint.noex : SmartInt; // use IntFlagPolicy.noex 244 245 // Any basic scalar or checkedint *type* is accepted... 246 SmartInt!int n = 0; 247 n = cast(ulong)0; 248 n = cast(dchar)0; 249 n = cast(byte)0; 250 n = cast(real)0; 251 assert(!IntFlags.local); 252 253 // ...but not any *value*. 254 n = uint.max; 255 n = long.min; 256 n = real.nan; 257 assert(IntFlags.local.clear() == (IntFlag.posOver | IntFlag.negOver | IntFlag.undef)); 258 } 259 260 /// Convert this value to floating-point. This always succeeds, although some loss of precision may 261 /// occur if M.sizeof <= N.sizeof. 262 M opCast(M)() const pure nothrow @nogc 263 if (isFloatingPoint!M) 264 { 265 return cast(M)bscal; 266 } 267 /// 268 unittest { 269 import checkedint.throws : SmartInt; // use IntFlagPolicy.throws 270 271 SmartInt!int n = 92; 272 auto f = cast(double)n; 273 static assert(is(typeof(f) == double)); 274 assert(f == 92.0); 275 } 276 277 /// `this != 0` 278 M opCast(M)() const pure nothrow @nogc 279 if (is(M == bool)) 280 { 281 return bscal != 0; 282 } 283 /// 284 unittest 285 { 286 import checkedint.throws : SmartInt; // use IntFlagPolicy.throws 287 288 SmartInt!int n = -315; 289 assert( cast(bool)n); 290 291 n = 0; 292 assert(!cast(bool)n); 293 } 294 295 /// Convert this value to type `M` using `checkedint.to()` for bounds checking. An `IntFlag` will be raised if 296 /// `M` cannot represent the current value of this `SmartInt`. 297 M opCast(M)() const 298 if (isCheckedInt!M || isIntegral!M || isSomeChar!M) 299 { 300 return to!(M, policy)(bscal); 301 } 302 /// 303 unittest 304 { 305 import checkedint.noex : SmartInt; // use IntFlagPolicy.noex 306 307 SmartInt!ulong n = 52; 308 auto a = cast(int)n; 309 static assert(is(typeof(a) == int)); 310 assert(!IntFlags.local); 311 assert(a == 52); 312 313 auto m = SmartInt!long(-1).mulPow2(n); 314 auto b = cast(wchar)m; 315 static assert(is(typeof(b) == wchar)); 316 assert(IntFlags.local.clear() == IntFlag.negOver); 317 } 318 319 /// Convert this value to a type suitable for indexing an array: 320 /// $(UL 321 /// $(LI If `N` is signed, a `ptrdiff_t` is returned.) 322 /// $(LI If `N` is unsigned, a `size_t` is returned.) 323 /// ) 324 /// `checkedint.to()` is used for bounds checking. 325 @property Select!(isSigned!N, ptrdiff_t, size_t) idx() const 326 { 327 return to!(typeof(return), policy)(bscal); 328 } 329 /// 330 unittest 331 { 332 import checkedint.throws : SmartInt; // use IntFlagPolicy.throws 333 334 char[3] arr = ['a', 'b', 'c']; 335 SmartInt!long n = 1; 336 337 // On 32-bit, `long` cannot be used directly for array indexing, 338 static if (size_t.sizeof < long.sizeof) 339 static assert(!__traits(compiles, arr[n])); 340 // but idx can be used to concisely and safely cast to ptrdiff_t: 341 assert(arr[n.idx] == 'b'); 342 343 // The conversion is bounds checked: 344 static if (size_t.sizeof < long.sizeof) 345 { 346 n = long.min; 347 try 348 { 349 arr[n.idx] = '?'; 350 } 351 catch(CheckedIntException e) 352 { 353 assert(e.intFlags == IntFlag.negOver); 354 } 355 } 356 } 357 358 /// Get a simple hashcode for this value. 359 size_t toHash() const pure nothrow @nogc 360 { 361 static if (N.sizeof > size_t.sizeof) 362 { 363 static assert(N.sizeof == (2 * size_t.sizeof)); 364 return cast(size_t)bscal ^ cast(size_t)(bscal >>> 32); 365 } 366 else 367 return cast(size_t)bscal; 368 } 369 370 /// Get a `string` representation of this value. 371 string toString() const 372 { 373 return to!(string, IntFlagPolicy.noex)(bscal); 374 } 375 /// ditto 376 void toString(Writer, Char)(Writer sink, FormatSpec!Char fmt = (FormatSpec!Char).init) const @trusted 377 { 378 formatValue(sink, bscal, fmt); 379 } 380 /// 381 unittest 382 { 383 import checkedint.throws : smartInt; // use IntFlagPolicy.throws 384 assert(smartInt(-753).toString() == "-753"); 385 } 386 387 // Comparison ///////////////////////////////////////////////// 388 /// Returns `true` if this value is mathematically precisely equal to `right`. 389 bool opEquals(M)(const M right) const pure nothrow @nogc 390 if (isCheckedInt!M || isScalarType!M) 391 { 392 return smartOp!(policy).cmp!"=="(this.bscal, right.bscal); 393 } 394 /// Perform a mathematically correct comparison to `right`. 395 /// 396 /// Returns: $(UL 397 /// $(LI `-1` if this value is less than `right`.) 398 /// $(LI ` 0` if this value is precisely equal to `right`.) 399 /// $(LI ` 1` if this value is greater than `right`.) 400 /// $(LI `float.nan` if `right` is a floating-point `nan` value.) 401 /// ) 402 auto opCmp(M)(const M right) const pure nothrow @nogc 403 if (isFloatingPoint!M) 404 { 405 return 406 (bscal < right)? -1 : 407 (bscal > right)? 1 : 408 (bscal == right)? 0 : float.nan; 409 } 410 /// ditto 411 int opCmp(M)(const M right) const pure nothrow @nogc 412 if (isCheckedInt!M || isScalarType!M) 413 { 414 return smartOp!(policy).cmp(this.bscal, right.bscal); 415 } 416 417 // Unary ///////////////////////////////////////////////// 418 /// See `smartOp`. 419 typeof(this) opUnary(string op)() const pure nothrow @nogc 420 if (op == "~") 421 { 422 static assert(bitOps, 423 "Bitwise operations are disabled."); 424 425 return typeof(return)(smartOp!(policy).unary!op(bscal)); 426 } 427 /// ditto 428 SmartInt!(Signed!N, policy, bitOps) opUnary(string op)() const 429 if (op == "+" || op == "-") 430 { 431 return typeof(return)(smartOp!(policy).unary!op(bscal)); 432 } 433 /// ditto 434 ref typeof(this) opUnary(string op)() /+return+/ 435 if (op.among!("++", "--")) 436 { 437 smartOp!(policy).unary!op(bscal); 438 return this; 439 } 440 441 /// ditto 442 SmartInt!(Unsigned!N, policy, bitOps) abs() const pure nothrow @nogc 443 { 444 return typeof(return)(smartOp!(policy).abs(bscal)); 445 } 446 447 /// Count the number of set bits using `core.bitop.popcnt()`. 448 SmartInt!(int, policy, bitOps) popcnt()() const pure nothrow @nogc 449 { 450 static assert(bitOps, "Bitwise operations are disabled."); 451 452 import future.bitop : stdPC = popcnt; 453 return typeof(return)(stdPC(bscal)); 454 } 455 456 /// See `smartOp`. 457 SmartInt!(ubyte, policy, bitOps) bsf()() const 458 { 459 static assert(bitOps, "Bitwise operations are disabled."); 460 461 return typeof(return)(smartOp!(policy).bsf(bscal)); 462 } 463 /// ditto 464 SmartInt!(ubyte, policy, bitOps) bsr()() const 465 { 466 static assert(bitOps, "Bitwise operations are disabled. Consider using ilogb() instead?"); 467 468 return typeof(return)(smartOp!(policy).bsr(bscal)); 469 } 470 471 /// ditto 472 SmartInt!(ubyte, policy, bitOps) ilogb() const 473 { 474 return typeof(return)(smartOp!(policy).ilogb(bscal)); 475 } 476 477 // Binary ///////////////////////////////////////////////// 478 /// ditto 479 auto opBinaryRight(string op, M)(const M left) const pure nothrow @nogc 480 if (isFloatingPoint!M) 481 { 482 return smartOp!(policy).binary!op(left, bscal); 483 } 484 /// ditto 485 auto opBinary(string op, M)(const M right) const pure nothrow @nogc 486 if (isFloatingPoint!M) 487 { 488 return smartOp!(policy).binary!op(bscal, right); 489 } 490 /// ditto 491 auto opBinaryRight(string op, M)(const M left) const 492 if (isSafeInt!M || isFixedPoint!M) 493 { 494 enum mixPolicy = .max(policy, intFlagPolicyOf!M); 495 enum mixBitOps = bitOps && hasBitOps!M; 496 static assert(mixBitOps || !op.among!("<<", ">>", ">>>", "&", "|", "^"), 497 "Bitwise operations are disabled. Consider using mulPow2(), divPow2(), or modPow2() instead?"); 498 499 const wret = smartOp!(mixPolicy).binary!op(left.bscal, this.bscal); 500 return SmartInt!(typeof(wret), mixPolicy, mixBitOps)(wret); 501 } 502 /// ditto 503 auto opBinary(string op, M)(const M right) const 504 if (isCheckedInt!M || isFixedPoint!M) 505 { 506 enum mixPolicy = .max(policy, intFlagPolicyOf!M); 507 enum mixBitOps = bitOps && hasBitOps!M; 508 static assert(mixBitOps || !op.among!("<<", ">>", ">>>", "&", "|", "^"), 509 "Bitwise operations are disabled. Consider using mulPow2(), divPow2(), or modPow2() instead?"); 510 511 const wret = smartOp!(mixPolicy).binary!op(this.bscal, right.bscal); 512 return SmartInt!(typeof(wret), mixPolicy, mixBitOps)(wret); 513 } 514 /// ditto 515 ref typeof(this) opOpAssign(string op, M)(const M right) /+return+/ 516 if (isCheckedInt!M || isFixedPoint!M) 517 { 518 static assert((bitOps && hasBitOps!M) || !op.among!("<<", ">>", ">>>", "&", "|", "^"), 519 "Bitwise operations are disabled. Consider using mulPow2(), divPow2(), or modPow2() instead?"); 520 521 smartOp!(.max(policy, intFlagPolicyOf!M)).binary!(op ~ "=")(this.bscal, right.bscal); 522 return this; 523 } 524 525 /// ditto 526 auto mulPow2(M)(const M exp) const pure nothrow @nogc 527 if (isFloatingPoint!M) 528 { 529 return smartOp!(policy).mulPow2(bscal, exp); 530 } 531 /// ditto 532 auto mulPow2(M)(const M exp) const 533 if (isCheckedInt!M || isFixedPoint!M) 534 { 535 enum mixPolicy = .max(policy, intFlagPolicyOf!M); 536 const wret = smartOp!(mixPolicy).mulPow2(this.bscal, exp.bscal); 537 return SmartInt!(typeof(wret), mixPolicy, bitOps && hasBitOps!M)(wret); 538 } 539 /// ditto 540 auto divPow2(M)(const M exp) const pure nothrow @nogc 541 if (isFloatingPoint!M) 542 { 543 return smartOp!(policy).divPow2(bscal, exp); 544 } 545 /// ditto 546 auto divPow2(M)(const M exp) const 547 if (isCheckedInt!M || isFixedPoint!M) 548 { 549 enum mixPolicy = .max(policy, intFlagPolicyOf!M); 550 const wret = smartOp!(mixPolicy).divPow2(this.bscal, exp.bscal); 551 return SmartInt!(typeof(wret), mixPolicy, bitOps && hasBitOps!M)(wret); 552 } 553 /// ditto 554 auto modPow2(M)(const M exp) const pure nothrow @nogc 555 if (isFloatingPoint!M) 556 { 557 return smartOp!(policy).modPow2(bscal, exp); 558 } 559 /// ditto 560 auto modPow2(M)(const M exp) const 561 if (isCheckedInt!M || isFixedPoint!M) 562 { 563 enum mixPolicy = .max(policy, intFlagPolicyOf!M); 564 const wret = smartOp!(mixPolicy).modPow2(this.bscal, exp.bscal); 565 return SmartInt!(typeof(wret), mixPolicy, bitOps && hasBitOps!M)(wret); 566 } 567 568 /// Raise `this` to the `exp` power using `std.math.pow()`. 569 auto pow(M)(const M exp) const pure nothrow @nogc 570 if (isFloatingPoint!M) 571 { 572 return std.math.pow(bscal, exp); 573 } 574 /// See `smartOp`. 575 auto pow(M)(const M exp) const 576 if (isCheckedInt!M || isFixedPoint!M) 577 { 578 enum mixPolicy = .max(policy, intFlagPolicyOf!M); 579 const wret = smartOp!(mixPolicy).pow(this.bscal, exp.bscal); 580 return SmartInt!(typeof(wret), mixPolicy, bitOps && hasBitOps!M)(wret); 581 } 582 } 583 /// ditto 584 template SmartInt(N, IntFlagPolicy policy, Flag!"bitOps" bitOps = Yes.bitOps) 585 if ((isIntegral!N && !isUnqual!N) || isCheckedInt!N) 586 { 587 alias SmartInt = SmartInt!(BasicScalar!N, policy, bitOps); 588 } 589 /// 590 unittest 591 { 592 // Mixing standard signed and unsigned types is dangerous, but... 593 int ba = -1; 594 uint bb = 0; 595 assert(ba > bb); 596 597 auto bc = ba + bb; 598 assert(is(typeof(bc) == uint)); 599 assert(bc == 4294967295u); 600 601 // ...with SmartInt, mixed signed/unsigned operations "just work": 602 import checkedint.throws : SmartInt; // use IntFlagPolicy.throws 603 604 SmartInt!int ma = -1; 605 SmartInt!uint mb = 0; 606 assert(ma < mb); 607 608 auto mc = ma + mb; 609 assert(is(typeof(mc) == SmartInt!int)); 610 assert(mc != 4294967295u); 611 assert(mc == -1); 612 } 613 /// 614 unittest 615 { 616 // When IntFlagPolicy.throws is used, failed SmartInt operations will throw a CheckedIntException. 617 import checkedint.throws : SmartInt; 618 619 SmartInt!uint ma = 1; 620 SmartInt!uint mb = 0; 621 622 bool overflow = false; 623 try 624 { 625 SmartInt!uint mc = mb - ma; 626 assert(false); 627 } 628 catch(CheckedIntException e) 629 { 630 assert(e.intFlags == IntFlag.negOver); 631 overflow = true; 632 } 633 assert(overflow); 634 635 bool div0 = false; 636 try 637 { 638 // With standard integers, this would crash the program with an unrecoverable FPE... 639 SmartInt!uint mc = ma / mb; 640 assert(false); 641 } 642 catch(CheckedIntException e) 643 { 644 // ...but with SmartInt, it just throws a normal Exception. 645 assert(e.intFlags == IntFlag.div0); 646 div0 = true; 647 } 648 assert(div0); 649 } 650 /// 651 unittest 652 { 653 // When IntFlagPolicy.noex is used, failed SmartInt operations set one or more bits in IntFlags.local. 654 import checkedint.noex : SmartInt; 655 656 SmartInt!uint ma = 1; 657 SmartInt!uint mb = 0; 658 SmartInt!uint mc; 659 660 mc = mb - ma; 661 assert(IntFlags.local == IntFlag.negOver); 662 663 // With standard integers, this would crash the program with an unrecoverable FPE... 664 mc = ma / mb; 665 // ...but with SmartInt, it just sets a bit in IntFlags.local. 666 assert(IntFlags.local & IntFlag.div0); 667 668 // Each flag will remain set until cleared: 669 assert(IntFlags.local.clear() == (IntFlag.negOver | IntFlag.div0)); 670 assert(!IntFlags.local); 671 } 672 673 private template SmartInt(N, IntFlagPolicy policy, bool bitOps) 674 if (isIntegral!N) 675 { 676 alias SmartInt = SmartInt!( 677 Unqual!N, 678 policy, 679 cast(Flag!"bitOps")bitOps); 680 } 681 682 /// Get the value of `num` as a `SmartInt!N`. The integral type `N` can be infered from the argument. 683 SmartInt!(N, policy, bitOps) smartInt(IntFlagPolicy policy, Flag!"bitOps" bitOps = Yes.bitOps, N)(N num) 684 if (isIntegral!N || isCheckedInt!N) 685 { 686 return typeof(return)(num.bscal); 687 } 688 /// 689 unittest 690 { 691 import checkedint.throws : smartInt, SmartInt; // use IntFlagPolicy.throws 692 693 auto a = smartInt(55uL); 694 static assert(is(typeof(a) == SmartInt!ulong)); 695 assert(a == 55); 696 } 697 698 /// Implements various integer math operations with error checking. 699 /// 700 /// `smartOp` strives to give the mathematically correct result, with integer-style rounding, for all inputs. Only 701 /// if the correct result is undefined or not representable by the return type is an error signalled, using 702 /// `checkedint.flags`. 703 /// 704 /// The error-signalling policy may be selected using the `policy` template parameter. 705 template smartOp(IntFlagPolicy policy) 706 { 707 // NOTE: ddoc only scans the first branch of a static if 708 static if (policy == IntFlagPolicy.none) 709 { 710 // No need to redundantly instantiate members which don't depend on `policy`. 711 712 private void cmpTypeCheck(N, M)() pure nothrow @nogc 713 { 714 static assert(isBoolean!N == isBoolean!M, 715 "The intent of a direct comparison of " ~ 716 N.stringof ~ " with " ~ M.stringof ~ 717 " is unclear. Add an explicit cast." 718 ); 719 } 720 721 /** 722 Compare `left` and `right` using `op`. 723 $(UL 724 $(LI Unlike the standard integer comparison operator, this function correctly handles negative 725 values in signed/unsigned comparisons.) 726 $(LI Like the standard operator, comparisons involving any floating-point `nan` value always return 727 `false`.) 728 ) $(BR) 729 Direct comparisons between boolean values and numeric ones are forbidden. Make your intention explicit: 730 $(UL 731 $(LI `cast(N)boolean == numeric`) 732 $(LI `boolean == (numeric != 0)`) 733 ) 734 **/ 735 bool cmp(string op, N, M)(const N left, const M right) pure nothrow @nogc 736 if (isScalarType!N && isScalarType!M) 737 { 738 cmpTypeCheck!(N, M)(); 739 740 static if (isSigned!N != isSigned!M) 741 { 742 static if (isSigned!N) 743 { 744 if (left < 0) 745 return mixin("-1 " ~ op ~ " 0"); 746 } 747 else 748 { 749 if (right < 0) 750 return mixin("0 " ~ op ~ " -1"); 751 } 752 } 753 754 return mixin("left " ~ op ~ " right"); 755 } 756 /// 757 unittest 758 { 759 import checkedint.noex : smartOp; // smartOp.cmp() never throws 760 761 assert(uint.max == -1); 762 assert( smartOp.cmp!"!="(uint.max, -1)); 763 assert(-3156 > 300u); 764 assert( smartOp.cmp!"<"(-3156, 300u)); 765 766 assert(!smartOp.cmp!"<"(1, real.nan)); 767 assert(!smartOp.cmp!"<"(real.nan, 1)); 768 } 769 770 /** 771 Defines a total order on all basic scalar values, using the same rules as `std.math.cmp()`. 772 773 $(UL 774 $(LI Mixed signed/unsigned comparisons return the mathematically correct result.) 775 $(LI If neither `left` nor `right` is floating-point, this function is faster than 776 `std.math.cmp()`.) 777 $(LI If either `left` or `right` $(I is) floating-point, this function forwards to 778 `std.math.cmp()`.) 779 ) $(BR) 780 Direct comparisons between boolean values and numeric ones are forbidden. Make your intention explicit: 781 $(UL 782 $(LI `cast(N)boolean == numeric`) 783 $(LI `boolean == (numeric != 0)`) 784 ) 785 **/ 786 int cmp(N, M)(const N left, const M right) pure nothrow @nogc 787 if (isScalarType!N && isScalarType!M) 788 { 789 cmpTypeCheck!(N, M)(); 790 791 static if (isFloatingPoint!N || isFloatingPoint!M) 792 { 793 import future.math : stdCmp = cmp; 794 return stdCmp(left, right); 795 } 796 else 797 { 798 static if (isSigned!N != isSigned!M) 799 { 800 static if (isSigned!N) 801 { 802 if (left < 0) 803 return -1; 804 } 805 else 806 { 807 if (right < 0) 808 return 1; 809 } 810 } 811 812 return (left < right)? -1 : (right < left); 813 } 814 } 815 /// 816 unittest 817 { 818 import checkedint.noex : smartOp; // smartOp.cmp() never throws 819 820 assert(smartOp.cmp(325.0, 325u) == 0); 821 assert(smartOp.cmp(uint.max, -1) == 1); 822 assert(smartOp.cmp(-3156, 300u) == -1); 823 } 824 825 /// Get the absolute value of `num`. Because the return type is always unsigned, overflow is not possible. 826 Unsigned!N abs(N)(const N num) pure nothrow @nogc 827 if (isIntegral!N) 828 { 829 static if (!isSigned!N) 830 return num; 831 else 832 return cast(typeof(return))(num < 0? 833 -num : // -num doesn't need to be checked for overflow 834 num); 835 } 836 /// ditto 837 IntFromChar!N abs(N)(const N num) pure nothrow @nogc 838 if (isSomeChar!N) 839 { 840 return num; 841 } 842 /// 843 unittest 844 { 845 import checkedint.noex : smartOp; // smartOp.abs() never throws 846 847 assert(smartOp.abs(int.min) == std.math.pow(2.0, 31)); 848 assert(smartOp.abs(-25) == 25u); 849 assert(smartOp.abs(745u) == 745u); 850 } 851 852 private template Result(N, string op, M) 853 if (isNumeric!N && isNumeric!M) 854 { 855 private: 856 enum reqFloat = isFloatingPoint!N || isFloatingPoint!M; 857 enum precN = precision!N, precM = precision!M; 858 enum precStd = reqFloat? precision!float : precision!uint; 859 enum smallSub = (op == "-") && precN < precision!int && precM < precision!int; 860 861 enum reqSign = reqFloat || 862 (op.among!("+", "-", "*" , "/") && (isSigned!N || isSigned!M || smallSub)) || 863 (op.among!("%", "^^", "<<", ">>", ">>>") && isSigned!N) || 864 (op.among!("&", "|", "^") && (isSigned!N && isSigned!M)); 865 866 enum reqPrec = reqFloat? max(precStd, precN, precM) : 867 op.among!("+", "-", "*")? max(precStd, precN, precM) - 1 : 868 op == "/"? (isSigned!M? max(precStd, precN) - 1 : precN) : 869 op == "%"? min(precision!N, precision!M) : 870 op == "^^"? max(precStd - 1, precN) : 871 op.among!("<<", ">>", ">>>")? precN : 872 /+op.among!("&", "|", "^")?+/ max(precN, precM); 873 874 public: 875 alias Result = Select!(reqFloat, 876 Select!(reqPrec <= double.mant_dig || double.mant_dig >= real.mant_dig, 877 Select!(reqPrec <= float.mant_dig, float, double), 878 real), 879 Select!(reqSign, 880 Select!(reqPrec <= 15, 881 Select!(reqPrec <= 7, byte, short), 882 Select!(reqPrec <= 31, int, long)), 883 Select!(reqPrec <= 16, 884 Select!(reqPrec <= 8, ubyte, ushort), 885 Select!(reqPrec <= 32, uint, ulong)))); 886 } 887 private template Result(N, string op, M) 888 if (isScalarType!N && isScalarType!M && 889 (!isNumeric!N || !isNumeric!M)) 890 { 891 alias Result = Result!(NumFromScal!N, op, NumFromScal!M); 892 } 893 } 894 else 895 { 896 alias cmp = smartOp!(IntFlagPolicy.none).cmp; 897 alias abs = smartOp!(IntFlagPolicy.none).abs; 898 private alias Result = smartOp!(IntFlagPolicy.none).Result; 899 } 900 901 /** 902 Perform the unary (single-argument) integer operation specified by `op`. 903 904 Key differences from the standard unary operators: 905 $(UL 906 $(LI `-` and `+` always return a signed value.) 907 $(LI `-` is checked for overflow, because `-int.min` is greater than `int.max`.) 908 $(LI `++` and `--` are checked for overflow.) 909 ) $(BR) 910 Note that like the standard operators, `++` and `--` take the operand by `ref` and overwrite its value with 911 the result. 912 **/ 913 N unary(string op, N)(const N num) pure nothrow @nogc 914 if (isIntegral!N && op == "~") 915 { 916 return ~num; 917 } 918 /// ditto 919 IntFromChar!N unary(string op, N)(const N num) pure nothrow @nogc 920 if (isSomeChar!N && op == "~") 921 { 922 return ~num; 923 } 924 /// ditto 925 Signed!(Promoted!N) unary(string op, N)(const N num) 926 if (isFixedPoint!N && op.among!("-", "+")) 927 { 928 alias R = typeof(return); 929 alias UR = Unsigned!R; 930 931 static if (op == "-") 932 { 933 static if (isSigned!N) 934 { 935 if (num < -trueMax!R) 936 IntFlag.posOver.raise!policy(); 937 } 938 else 939 { 940 if (num > cast(UR)trueMin!R) 941 IntFlag.negOver.raise!policy(); 942 } 943 944 return -cast(R)num; 945 } 946 else 947 { 948 static if (!isSigned!N) 949 { 950 if (num > trueMax!R) 951 IntFlag.posOver.raise!policy(); 952 } 953 954 return num; 955 } 956 } 957 /// ditto 958 /+ref+/ N unary(string op, N)(/+return+/ ref N num) 959 if (isIntegral!N && op.among!("++", "--")) 960 { 961 static if (op == "++") 962 { 963 if (num >= trueMax!N) 964 IntFlag.posOver.raise!policy(); 965 966 return ++num; 967 } 968 else static if (op == "--") 969 { 970 if (num <= trueMin!N) 971 IntFlag.negOver.raise!policy(); 972 973 return --num; 974 } 975 else 976 static assert(false); 977 } 978 /// 979 unittest 980 { 981 import checkedint.noex : smartOp; // use IntFlagPolicy.noex 982 983 assert(smartOp.unary!"~"(0u) == uint.max); 984 985 auto a = smartOp.unary!"-"(20uL); 986 static assert(is(typeof(a) == long)); 987 assert(a == -20); 988 989 auto b = smartOp.unary!"+"(uint.max); 990 static assert(is(typeof(b) == int)); 991 assert(IntFlags.local.clear() == IntFlag.posOver); 992 993 uint c = 1u; 994 assert(smartOp.unary!"--"(c) == 0u); 995 assert(c == 0u); 996 smartOp.unary!"--"(c); 997 assert(IntFlags.local.clear() == IntFlag.negOver); 998 999 int d = 7; 1000 assert(smartOp.unary!"++"(d) == 8); 1001 assert(d == 8); 1002 } 1003 1004 /// `core.bitop.bsf` without the undefined behaviour. `smartOp.bsf(0)` will raise `IntFlag.undef`. 1005 ubyte bsf(N)(const N num) 1006 if (isFixedPoint!N) 1007 { 1008 return cast(ubyte) bsfImpl!policy(num); 1009 } 1010 /// 1011 unittest 1012 { 1013 import checkedint.noex : smartOp; 1014 1015 assert(smartOp.bsf(20) == 2); 1016 1017 smartOp.bsf(0); 1018 assert(IntFlags.local.clear() == IntFlag.undef); 1019 } 1020 1021 /// `core.bitop.bsr` without the undefined behaviour. `smartOp.bsr(0)` will raise `IntFlag.undef`. 1022 ubyte bsr(N)(const N num) 1023 if (isFixedPoint!N) 1024 { 1025 return cast(ubyte) bsrImpl!policy(num); 1026 } 1027 /// 1028 unittest 1029 { 1030 import checkedint.noex : smartOp; 1031 1032 assert(smartOp.bsr( 20) == 4); 1033 assert(smartOp.bsr(-20) == 31); 1034 1035 smartOp.bsr(0); 1036 assert(IntFlags.local.clear() == IntFlag.undef); 1037 } 1038 1039 /// Get the base 2 logarithm of `abs(num)`, rounded down to the nearest integer. 1040 /// 1041 /// `smartOp.ilogb(0)` will raise `IntFlag.undef`. 1042 ubyte ilogb(N)(const N num) 1043 if (isFixedPoint!N) 1044 { 1045 return cast(ubyte) bsrImpl!policy(abs(num)); 1046 } 1047 /// 1048 unittest 1049 { 1050 import checkedint.noex : smartOp; 1051 1052 assert(smartOp.ilogb(20) == 4); 1053 assert(smartOp.ilogb(-20) == 4); 1054 1055 smartOp.ilogb(0); 1056 assert(IntFlags.local.clear() == IntFlag.undef); 1057 } 1058 1059 private auto binaryImpl(string op, N, M)(const N left, const M right) 1060 if (isIntegral!N && isIntegral!M) 1061 { 1062 enum wop = (op[$-1] == '=')? op[0 .. $-1] : op; 1063 alias 1064 UN = Unsigned!N, 1065 UM = Unsigned!M, 1066 W = Result!(N, wop, M), 1067 R = Select!(wop == op, W, N); 1068 1069 static if (wop.among!("+", "-", "*")) 1070 { 1071 enum safePrec = (wop == "*")? 1072 precision!N + precision!M : 1073 max(precision!N, precision!M) + 1; 1074 enum safeR = precision!R >= safePrec; 1075 1076 static if (safeR) 1077 return mixin("cast(R)left " ~ wop ~ " cast(R)right"); 1078 else 1079 { 1080 enum safeW = precision!W >= safePrec; 1081 enum matched = (isSigned!N == isSigned!M); 1082 enum oX = staticIndexOf!(wop, "+", "-", "*") << 1; 1083 alias cops = AliasSeq!(addu, adds, subu, subs, mulu, muls); 1084 1085 static if (safeW || matched || wop == "*") 1086 { 1087 bool over; 1088 static if (safeW) 1089 const wR = mixin("cast(W)left " ~ wop ~ " cast(W)right"); 1090 else 1091 { 1092 static if (matched) 1093 { 1094 alias cop = cops[oX + isSigned!W]; 1095 const wR = cop(left, right, over); 1096 } 1097 else 1098 { 1099 // integer multiplication is commutative 1100 static if (isSigned!N) 1101 W sa = left, ub = right; 1102 else 1103 W ub = left, sa = right; 1104 1105 static if (isSigned!R) 1106 { 1107 W wR = muls(sa, ub, over); 1108 if (ub < 0) 1109 over = (sa != 0) && (ub != trueMin!W || sa != -1); 1110 } 1111 else 1112 { 1113 over = (sa < 0) && (ub != 0); 1114 const wR = mulu(sa, ub, over); 1115 } 1116 } 1117 } 1118 1119 alias WR = typeof(wR); 1120 static if (isSigned!WR && trueMin!WR < trueMin!R) 1121 { 1122 if (wR < trueMin!R) 1123 over = true; 1124 } 1125 static if (trueMax!WR > trueMax!R) 1126 { 1127 if (wR > trueMax!R) 1128 over = true; 1129 } 1130 } 1131 else 1132 { 1133 alias UW = Unsigned!W; 1134 alias WR = Select!(isSigned!R, W, UW); 1135 alias cop = cops[oX]; 1136 1137 bool hiBit = false; 1138 const wR = cast(WR) cop(cast(UW)left, cast(UW)right, hiBit); 1139 const bool wSign = (Select!(isSigned!N, left, right) < 0) ^ hiBit; 1140 1141 static if (isSigned!WR) 1142 { 1143 static if (trueMax!WR > trueMax!R) 1144 { 1145 const over = (wR < 0)? 1146 !wSign || (wR < trueMin!R) : 1147 wSign || (wR > trueMax!R); 1148 } 1149 else 1150 const over = (wR < 0) != wSign; 1151 } 1152 else 1153 { 1154 static if (trueMax!WR > trueMax!R) 1155 const over = wSign || (wR > trueMax!R); 1156 else 1157 alias over = wSign; 1158 } 1159 } 1160 1161 if (over) 1162 IntFlag.over.raise!policy(); 1163 return cast(R) wR; 1164 } 1165 } 1166 else static if (wop == "/") 1167 { 1168 static if (!isSigned!N && !isSigned!M) 1169 { 1170 R ret = void; 1171 if (right == 0) 1172 { 1173 IntFlag.div0.raise!policy(); 1174 ret = 0; 1175 } 1176 else 1177 ret = cast(R)(left / right); 1178 1179 return ret; 1180 } 1181 else 1182 { 1183 alias P = Select!(precision!N <= 32 && precision!M <= 32, uint, ulong); 1184 1185 IntFlag flag; 1186 R ret = void; 1187 if (right == 0) 1188 { 1189 flag = IntFlag.div0; 1190 ret = 0; 1191 } 1192 else 1193 { 1194 static if (isSigned!N && isSigned!M) 1195 { 1196 if (left == trueMin!R && right == -1) 1197 { 1198 flag = IntFlag.posOver; 1199 ret = 0; 1200 } 1201 else 1202 ret = cast(R)(left / right); 1203 } 1204 else 1205 { 1206 alias UR = Unsigned!R; 1207 1208 P wL = void; 1209 P wG = void; 1210 static if (isSigned!N) 1211 { 1212 const negR = left < 0; 1213 alias side = left; 1214 alias wS = wL; 1215 wG = cast(P)right; 1216 } 1217 else 1218 { 1219 const negR = right < 0; 1220 wL = cast(P)left; 1221 alias side = right; 1222 alias wS = wG; 1223 } 1224 1225 if (negR) 1226 { 1227 wS = -cast(P)side; 1228 const P wR = wL / wG; 1229 1230 if (wR > cast(UR)trueMin!R) 1231 flag = IntFlag.negOver; 1232 ret = -cast(R)wR; 1233 } 1234 else 1235 { 1236 wS = cast(P)side; 1237 const P wR = wL / wG; 1238 1239 if (wR > cast(UR)trueMax!R) 1240 flag = IntFlag.posOver; 1241 ret = cast(R)wR; 1242 } 1243 } 1244 } 1245 1246 if (!flag.isNull) 1247 flag.raise!policy(); 1248 return ret; 1249 } 1250 } 1251 else static if (wop == "%") 1252 { 1253 R ret = void; 1254 static if (isSigned!M) 1255 const wG = cast(UM)((right < 0)? -right : right); 1256 else 1257 const wG = right; 1258 1259 if (wG <= trueMax!N) 1260 { 1261 if (wG) 1262 ret = cast(R)(left % cast(N)wG); 1263 else 1264 { 1265 IntFlag.div0.raise!policy(); 1266 ret = 0; 1267 } 1268 } 1269 else 1270 { 1271 static if (isSigned!N) 1272 { 1273 ret = (wG != cast(UN)trueMin!N || left != trueMin!N)? 1274 cast(R)left : 1275 cast(R)0; 1276 } 1277 else 1278 ret = cast(R)left; 1279 } 1280 1281 return ret; 1282 } 1283 else static if (wop.among!("<<", ">>", ">>>")) 1284 { 1285 const negG = right < 0; 1286 const shR = (wop == "<<")? 1287 negG : 1288 !negG; 1289 1290 R ret = void; 1291 static if (wop == ">>>") 1292 const wL = cast(UN)left; 1293 else 1294 alias wL = left; 1295 const absG = negG? 1296 -cast(UM)right : 1297 cast(UM)right; 1298 1299 enum maxSh = precision!UN - 1; 1300 if (absG <= maxSh) 1301 { 1302 const wG = cast(int)absG; 1303 ret = cast(R)(shR? 1304 wL >> wG : 1305 wL << wG); 1306 } 1307 else 1308 { 1309 ret = cast(R)((isSigned!N && (wop != ">>>") && shR)? 1310 (wL >> maxSh) : 1311 0); 1312 } 1313 1314 return ret; 1315 } 1316 else static if (wop.among!("&", "|", "^")) 1317 return cast(R)mixin("left " ~ wop ~ " right"); 1318 else 1319 static assert(false); 1320 } 1321 1322 /** 1323 Perform the binary (two-argument) integer operation specified by `op`. 1324 1325 Key differences from the standard binary operators: 1326 $(UL 1327 $(LI `+`, `-`, `*`, `/`, and `%` return a signed type if the result could be negative, unless $(B both) 1328 inputs are unsigned.) 1329 $(LI `+`, `-`, `*`, and `/` are checked for overflow.) 1330 $(LI `/` and `%` are checked for divide-by-zero, and will never generate an FPE.) 1331 $(LI `<<`, `>>`, and `>>>` are well-defined for all possible values of `right`. Large shifts return the 1332 same result as shifting by `1` `right` times in a row. (But, much faster because no actual loop is 1333 used.)) 1334 ) $(BR) 1335 Note also: 1336 $(UL 1337 $(LI The shift operators are $(B not) checked for overflow and should not be used for 1338 multiplication, division, or exponentiation. Instead, use `mulPow2()` and `divPow2()`, which internally 1339 use the bitshifts for speed, but check for overflow and correctly handle negative values.) 1340 $(LI Likewise, `modPow2()` should be used for remainders instead of `&`.) 1341 $(LI `^^` and `^^=` will remain disabled in favour of `pow` until DMD issues 15288 and 15412 are fixed.) 1342 ) $(BR) 1343 Like the standard equiavlents, the assignment operators (`+=`, `-=`, `*=`, etc.) take `left` by `ref` and will 1344 overwrite it with the result of the operation. 1345 **/ 1346 auto binary(string op, N, M)(const N left, const M right) 1347 if (isFixedPoint!N && isFixedPoint!M && 1348 op.among!("+", "-", "*", "/", "%", "^^", "<<", ">>", ">>>", "&", "|", "^")) 1349 { 1350 static assert(op != "^^", 1351 "pow() should be used instead of operator ^^ because of issue 15288."); 1352 1353 return binaryImpl!(op, NumFromScal!N, NumFromScal!M)(left, right); 1354 } 1355 /// ditto 1356 /+ref+/ N binary(string op, N, M)(/+return+/ ref N left, const M right) 1357 if (isIntegral!N && isFixedPoint!M && (op[$ - 1] == '=')) 1358 { 1359 static assert(op != "^^=", 1360 "pow() should be used instead of operator ^^= because of issue 15412."); 1361 1362 left = binaryImpl!(op, NumFromScal!N, NumFromScal!M)(left, right); 1363 return left; 1364 } 1365 /// 1366 unittest 1367 { 1368 import checkedint.noex : smartOp; // use IntFlagPolicy.noex 1369 1370 ulong a = 18_446_744_073_709_551_615uL; 1371 long b = -6_744_073_709_551_615L; 1372 auto c = smartOp.binary!"+"(a, b); 1373 static assert(isSigned!(typeof(c))); 1374 assert(IntFlags.local.clear() == IntFlag.posOver); 1375 1376 assert(smartOp.binary!"+="(a, b) == 18_440_000_000_000_000_000uL); 1377 assert(a == 18_440_000_000_000_000_000uL); 1378 1379 uint d = 25u; 1380 int e = 32; 1381 auto f = smartOp.binary!"-"(d, e); 1382 static assert(isSigned!(typeof(f))); 1383 assert(f == -7); 1384 1385 smartOp.binary!"-="(d, e); 1386 assert(IntFlags.local.clear() == IntFlag.negOver); 1387 1388 uint g = 1u << 31; 1389 int h = -1; 1390 auto i = smartOp.binary!"*"(g, h); 1391 static assert(isSigned!(typeof(i))); 1392 assert(i == int.min); 1393 1394 smartOp.binary!"*="(g, h); 1395 assert(IntFlags.local.clear() == IntFlag.negOver); 1396 1397 long j = long.min; 1398 ulong k = 1uL << 63; 1399 auto m = smartOp.binary!"/"(j, k); 1400 static assert(isSigned!(typeof(m))); 1401 assert(m == -1); 1402 1403 smartOp.binary!"/="(j, -1); 1404 assert(IntFlags.local.clear() == IntFlag.posOver); 1405 1406 ushort n = 20u; 1407 ulong p = ulong.max; 1408 auto q = smartOp.binary!"%"(n, p); 1409 static assert(is(typeof(q) == ushort)); 1410 assert(q == 20u); 1411 1412 smartOp.binary!"%="(n, 0); 1413 assert(IntFlags.local.clear() == IntFlag.div0); 1414 } 1415 /// 1416 unittest 1417 { 1418 import checkedint.noex : smartOp; // use IntFlagPolicy.noex 1419 1420 assert(smartOp.binary!"<<"(-0x80, -2) == -0x20); 1421 ubyte a = 0x3u; 1422 long b = long.max; 1423 auto c = smartOp.binary!"<<"(a, b); 1424 static assert(is(typeof(c) == ubyte)); 1425 assert(c == 0u); 1426 1427 assert(smartOp.binary!"<<="(a, 7) == 0x80u); 1428 assert(a == 0x80u); 1429 1430 short d = -0xC; 1431 ubyte e = 5u; 1432 auto f = smartOp.binary!">>"(d, e); 1433 static assert(is(typeof(f) == short)); 1434 assert(f == -0x1); 1435 1436 assert(smartOp.binary!">>="(d, -8) == -0xC00); 1437 assert(d == -0xC00); 1438 1439 int g = -0x80; 1440 ulong h = 2u; 1441 auto i = smartOp.binary!">>>"(g, h); 1442 static assert(is(typeof(i) == int)); 1443 assert(i == 0x3FFF_FFE0); 1444 1445 assert(smartOp.binary!">>>="(g, 32) == 0); 1446 assert(g == 0); 1447 1448 ubyte j = 0x6Fu; 1449 short k = 0x4076; 1450 auto m = smartOp.binary!"&"(j, k); 1451 static assert(is(typeof(m) == ushort)); 1452 assert(m == 0x66u); 1453 1454 assert(smartOp.binary!"&="(j, k) == 0x66u); 1455 assert(j == 0x66u); 1456 1457 byte n = 0x6F; 1458 ushort p = 0x4076u; 1459 auto q = smartOp.binary!"|"(n, p); 1460 static assert(is(typeof(q) == ushort)); 1461 assert(q == 0x407Fu); 1462 1463 assert(smartOp.binary!"|="(n, p) == 0x7F); 1464 assert(n == 0x7F); 1465 1466 int r = 0x6F; 1467 int s = 0x4076; 1468 auto t = smartOp.binary!"^"(r, s); 1469 static assert(is(typeof(t) == int)); 1470 assert(t == 0x4019); 1471 1472 assert(smartOp.binary!"^="(r, s) == 0x4019); 1473 assert(r == 0x4019); 1474 1475 assert(!IntFlags.local); 1476 } 1477 1478 /// Equivalent to `left * pow(2, exp)`, but faster and works with a wider range of inputs. This is a safer 1479 /// alternative to `left << exp` that is still very fast. 1480 /// 1481 /// Note that (conceptually) rounding occurs $(I after) the `*`, meaning that `mulPow2(left, -exp)` is 1482 /// equivalent to `divPow2(left, exp)`. 1483 auto mulPow2(N, M)(const N left, const M exp) pure nothrow @nogc 1484 if ((isFloatingPoint!N && isScalarType!M) || (isScalarType!N && isFloatingPoint!M)) 1485 { 1486 return byPow2Impl!("*", NumFromScal!N, NumFromScal!M)(left, exp); 1487 } 1488 /// ditto 1489 auto mulPow2(N, M)(const N left, const M exp) 1490 if (isFixedPoint!N && isFixedPoint!M) 1491 { 1492 return byPow2Impl!("*", policy, NumFromScal!N, NumFromScal!M)(left, exp); 1493 } 1494 /// 1495 unittest 1496 { 1497 import checkedint.noex : smartOp; // use IntFlagPolicy.noex 1498 1499 assert(smartOp.mulPow2(-23, 5) == -736); 1500 smartOp.mulPow2(10_000_000, 10); 1501 assert(IntFlags.local.clear() == IntFlag.posOver); 1502 1503 assert(smartOp.mulPow2(65536, -8) == 256); 1504 assert(smartOp.mulPow2(-100, -100) == 0); 1505 } 1506 1507 /// Equivalent to `left / pow(2, exp)`, but faster and works with a wider range of inputs. This is a safer 1508 /// alternative to `left >> exp` that is still very fast. 1509 /// 1510 /// Note that (conceptually) rounding occurs $(I after) the `/`, meaning that `divPow2(left, -exp)` is 1511 /// equivalent to `mulPow2(left, exp)`. 1512 auto divPow2(N, M)(const N left, const M exp) pure nothrow @nogc 1513 if ((isFloatingPoint!N && isScalarType!M) || (isScalarType!N && isFloatingPoint!M)) 1514 { 1515 return byPow2Impl!("/", NumFromScal!N, NumFromScal!M)(left, exp); 1516 } 1517 /// ditto 1518 auto divPow2(N, M)(const N left, const M exp) 1519 if (isFixedPoint!N && isFixedPoint!M) 1520 { 1521 return byPow2Impl!("/", policy, NumFromScal!N, NumFromScal!M)(left, exp); 1522 } 1523 /// 1524 unittest 1525 { 1526 import checkedint.noex : smartOp; // use IntFlagPolicy.noex 1527 1528 assert(smartOp.divPow2(65536, 8) == 256); 1529 assert(smartOp.divPow2(-100, 100) == 0); 1530 assert(smartOp.divPow2(-23, -5) == -736); 1531 1532 smartOp.divPow2(10_000_000, -10); 1533 assert(IntFlags.local.clear() == IntFlag.posOver); 1534 } 1535 1536 /// Equivalent to `left % pow(2, exp)`, but faster and works with a wider range of inputs. This is a safer 1537 /// alternative to `left & ((1 << exp) - 1)` that is still very fast. 1538 auto modPow2(N, M)(const N left, const M exp) pure nothrow @nogc 1539 if ((isFloatingPoint!N && isScalarType!M) || (isScalarType!N && isFloatingPoint!M)) 1540 { 1541 return byPow2Impl!("%", NumFromScal!N, NumFromScal!M)(left, exp); 1542 } 1543 /// ditto 1544 auto modPow2(N, M)(const N left, const M exp) pure nothrow @nogc 1545 if (isFixedPoint!N && isFixedPoint!M) 1546 { 1547 return byPow2Impl!("%", IntFlagPolicy.noex, NumFromScal!N, NumFromScal!M)(left, exp); 1548 } 1549 /// 1550 unittest 1551 { 1552 import checkedint.noex : smartOp; // use IntFlagPolicy.noex 1553 1554 assert(smartOp.modPow2( 101, 1) == 1); 1555 assert(smartOp.modPow2( 101, 3) == 5); 1556 assert(smartOp.modPow2(-101, 3) == -5); 1557 1558 assert(smartOp.modPow2(101, -2) == 0); 1559 assert(smartOp.modPow2(101, 1_000) == 101); 1560 } 1561 1562 /// Raise `base` to the `exp` power. 1563 /// 1564 /// Errors that may be signalled if neither input is floating-point: 1565 /// $(UL 1566 /// $(LI `IntFlag.posOver` or `IntFlag.negOver` if the absolute value of the result is too large to 1567 /// represent with the return type.) 1568 /// $(LI `IntFlag.div0` if `base == 0` and `exp < 0`.) 1569 /// ) 1570 auto pow(N, M)(const N base, const M exp) pure nothrow @nogc 1571 if ((isFloatingPoint!N && isScalarType!M) || (isScalarType!N && isFloatingPoint!M)) 1572 { 1573 alias R = Result!(N, "^^", M); 1574 static assert(is(typeof(return) == R)); 1575 return std.math.pow(cast(R)base, exp); 1576 } 1577 /// ditto 1578 auto pow(N, M)(const N base, const M exp) 1579 if (isFixedPoint!N && isFixedPoint!M) 1580 { 1581 alias R = Result!(N, "^^", M); 1582 1583 const po = powImpl!(R, Select!(isSigned!M, long, ulong))(base, exp); 1584 static assert(is(typeof(po.num) == const(R))); 1585 1586 if (!po.flag.isNull) 1587 po.flag.raise!policy(); 1588 return po.num; 1589 } 1590 /// 1591 unittest 1592 { 1593 import checkedint.noex : smartOp; // use IntFlagPolicy.noex 1594 1595 assert(smartOp.pow(-10, 3) == -1_000); 1596 assert(smartOp.pow(16, 4uL) == 65536); 1597 assert(smartOp.pow(2, -1) == 0); 1598 1599 smartOp.pow(-3, 27); 1600 assert(IntFlags.local.clear() == IntFlag.negOver); 1601 smartOp.pow(0, -5); 1602 assert(IntFlags.local.clear() == IntFlag.div0); 1603 } 1604 } 1605 private alias smartOp(bool throws) = smartOp!(cast(Flag!"throws")throws); 1606 1607 // debug ///////////////////////////////////////////////// 1608 /** 1609 `template` `alias` that evaluates to `SafeInt!(N, policy, bitOps)` in debug mode, and `N` in release mode. This 1610 way, you can use `SafeInt!N` to debug your integer logic in testing, but switch to basic `N` in release mode for 1611 maximum speed and the smallest binaries. 1612 1613 While this may be very helpful for debuggin your algorithms, note that `DebugInt` is $(B $(RED not)) a substitute 1614 for input validation in release mode. Unrecoverable FPEs or silent data-corrupting overflow can still occur in 1615 release mode if you get your algorithm wrong, or fail to add manual bounds checks in the right places. 1616 1617 If performance is your only motivation for using `DebugInt` rather than `SmartInt`, consider limiting `DebugInt` 1618 to only the hotest code paths - inner loops and the like. For most programs, this should provide nearly the same 1619 performance boost as using it everywhere, with far less loss of safety. 1620 **/ 1621 template DebugInt(N, IntFlagPolicy policy, Flag!"bitOps" bitOps = Yes.bitOps) 1622 if (isIntegral!N || isCheckedInt!N) 1623 { 1624 version (Debug) 1625 alias DebugInt = SafeInt!(N, policy, bitOps); 1626 else 1627 alias DebugInt = Unqual!(BasicScalar!N); 1628 } 1629 1630 // safe ///////////////////////////////////////////////// 1631 /** 1632 Wrapper for any basic integral type `N` that uses the checked operations from `safeOp` and rejects attempts to 1633 directly assign values that cannot be proven to be within the range representable by `N`. (`checkedint.to()` can be 1634 used to safely assign values of incompatible types, with runtime bounds checking.) 1635 1636 `SafeInt` is designed to be as interchangeable with `N` as possible, without compromising safety. The `DebugInt` 1637 `template` allows a variable to use `SafeInt` in debug mode to find bugs, and `N` directly in release mode for 1638 greater speed and a smaller binary. 1639 1640 If you're not trying to write generic code that works with both `SafeInt!N` and `N`, you should probably be using 1641 `SmartInt` instead. It generates far fewer error messages; mostly it "just works". 1642 1643 $(UL 1644 $(LI `policy` controls the error signalling policy (see `checkedint.flags`).) 1645 $(LI `bitOps` may be set to `No.bitOps` if desired, to turn bitwise operations on this type into a compile-time 1646 error.) 1647 ) 1648 **/ 1649 struct SafeInt(N, IntFlagPolicy _policy, Flag!"bitOps" bitOps = Yes.bitOps) 1650 if (isIntegral!N && isUnqual!N) 1651 { 1652 /// The error signalling policy used by this `SafeInt` type. 1653 enum IntFlagPolicy policy = _policy; 1654 1655 static if (bitOps) 1656 { 1657 /// The basic integral value of this `SafeInt`. Accessing this directly may be useful for: 1658 /// $(UL 1659 /// $(LI Intentionally doing modular (unchecked) arithmetic, or) 1660 /// $(LI Interacting with APIs that are not `checkedint` aware.) 1661 /// ) 1662 N bscal; 1663 /// 1664 unittest 1665 { 1666 import checkedint.throws : SafeInt; // use IntFlagPolicy.throws 1667 1668 SafeInt!uint n; 1669 static assert(is(typeof(n.bscal) == uint)); 1670 1671 n = 7u; 1672 assert(n.bscal == 7u); 1673 1674 n.bscal -= 8u; 1675 assert(n == uint.max); 1676 } 1677 1678 /// Get a view of this `SafeInt` that allows bitwise operations. 1679 @property ref inout(SafeInt!(N, policy, Yes.bitOps)) bits() /+return+/ inout pure nothrow @nogc 1680 { 1681 return this; 1682 } 1683 /// 1684 unittest 1685 { 1686 import checkedint.throws : SafeInt; // use IntFlagPolicy.throws 1687 1688 SafeInt!(int, No.bitOps) n = 1; 1689 static assert(!__traits(compiles, n << 2)); 1690 assert(n.bits << 2 == 4); 1691 } 1692 } 1693 else 1694 { 1695 @property ref inout(N) bscal() /+return+/ inout pure nothrow @nogc 1696 { 1697 return bits.bscal; 1698 } 1699 SafeInt!(N, policy, Yes.bitOps) bits; 1700 } 1701 1702 static if (__VERSION__ >= 2067) 1703 { 1704 version(GNU) { static assert(false); } 1705 1706 /// The most negative possible value of this `SafeInt` type. 1707 enum SafeInt!(N, policy, bitOps) min = typeof(this)(trueMin!N); 1708 /// 1709 unittest 1710 { 1711 import checkedint.throws : SafeInt; // use IntFlagPolicy.throws 1712 1713 assert(SafeInt!(int).min == int.min); 1714 assert(SafeInt!(uint).min == uint.min); 1715 } 1716 1717 /// The most positive possible value of this `SafeInt` type. 1718 enum SafeInt!(N, policy, bitOps) max = typeof(this)(trueMax!N); 1719 /// 1720 unittest 1721 { 1722 import checkedint.throws : SafeInt; // use IntFlagPolicy.throws; 1723 1724 assert(SafeInt!(int).max == int.max); 1725 assert(SafeInt!(uint).max == uint.max); 1726 } 1727 } 1728 else 1729 { 1730 static @property auto min() pure nothrow @nogc 1731 { 1732 return typeof(this)(trueMin!N); 1733 } 1734 static @property auto max() pure nothrow @nogc 1735 { 1736 return typeof(this)(trueMax!N); 1737 } 1738 } 1739 1740 // Construction, assignment, and casting ///////////////////////////////////////////////// 1741 private void checkImplicit(M)() const 1742 if (isCheckedInt!M || isScalarType!M) 1743 { 1744 alias MB = BasicScalar!M; 1745 static assert(trueMin!MB >= cast(real)N.min && MB.max <= cast(real)N.max, 1746 "SafeInt does not support implicit conversions from " ~ 1747 MB.stringof ~ " to " ~ N.stringof ~ 1748 ", because they are unsafe when unchecked. Use the explicit checkedint.to()." 1749 ); 1750 } 1751 1752 /// Assign the value of `that` to this `SafeInt` instance. 1753 /// 1754 /// Trying to assign a value that cannot be proven at compile time to be representable by `N` is an error. Use 1755 /// `checkedint.to()` to safely convert `that` with runtime bounds checking, instead. 1756 this(M)(const M that) pure nothrow @nogc 1757 if (isCheckedInt!M || isScalarType!M) 1758 { 1759 checkImplicit!M(); 1760 this.bscal = that.bscal; 1761 } 1762 /// ditto 1763 ref typeof(this) opAssign(M)(const M that) /+return+/ pure nothrow @nogc 1764 if (isCheckedInt!M || isScalarType!M) 1765 { 1766 checkImplicit!M(); 1767 this.bscal = that.bscal; 1768 return this; 1769 } 1770 /// 1771 unittest 1772 { 1773 import checkedint.noex : SafeInt, to; // use IntFlagPolicy.noex 1774 1775 // Only types that for which N can represent all values are accepted directly: 1776 SafeInt!int n = int.max; 1777 n = byte.max; 1778 n = wchar.max; 1779 1780 // Values of a type that could be `< N.min` or `> N.max` are rejected at compile time: 1781 static assert(!__traits(compiles, n = long.max)); 1782 static assert(!__traits(compiles, n = uint.max)); 1783 static assert(!__traits(compiles, n = real.max)); 1784 1785 // Instead, use checkedint.to(), which does runtime bounds checking: 1786 n = to!int(315L); 1787 assert(n == 315); 1788 1789 n = to!int(long.max); 1790 assert(IntFlags.local.clear() == IntFlag.posOver); 1791 } 1792 1793 /// Convert this value to floating-point. This always succeeds, although some loss of precision may 1794 /// occur if M.sizeof <= N.sizeof. 1795 M opCast(M)() const pure nothrow @nogc 1796 if (isFloatingPoint!M) 1797 { 1798 return cast(M)bscal; 1799 } 1800 /// 1801 unittest 1802 { 1803 import checkedint.throws : SafeInt; // use IntFlagPolicy.throws 1804 1805 SafeInt!int n = 92; 1806 auto f = cast(double)n; 1807 static assert(is(typeof(f) == double)); 1808 assert(f == 92.0); 1809 } 1810 1811 /// `this != 0` 1812 M opCast(M)() const pure nothrow @nogc 1813 if (is(M == bool)) 1814 { 1815 return bscal != 0; 1816 } 1817 /// 1818 unittest 1819 { 1820 import checkedint.throws : SafeInt; // use IntFlagPolicy.throws 1821 1822 SafeInt!int n = -315; 1823 assert( cast(bool)n); 1824 1825 n = 0; 1826 assert(!cast(bool)n); 1827 } 1828 1829 /// Convert this value to type `M` using `checkedint.to()` for bounds checking. An `IntFlag` will be raised if 1830 /// `M` cannot represent the current value of this `SafeInt`. 1831 M opCast(M)() const 1832 if (isCheckedInt!M || isIntegral!M || isSomeChar!M) 1833 { 1834 return to!(M, policy)(bscal); 1835 } 1836 /// 1837 unittest 1838 { 1839 import checkedint.noex : SafeInt; // use IntFlagPolicy.noex 1840 1841 SafeInt!ulong n = 52uL; 1842 auto a = cast(int)n; 1843 static assert(is(typeof(a) == int)); 1844 assert(!IntFlags.local); 1845 assert(a == 52); 1846 1847 auto m = SafeInt!long(-1).mulPow2(n); 1848 auto b = cast(wchar)m; 1849 static assert(is(typeof(b) == wchar)); 1850 assert(IntFlags.local.clear() == IntFlag.negOver); 1851 } 1852 1853 /// Convert this value to a type suitable for indexing an array: 1854 /// $(UL 1855 /// $(LI If `N` is signed, a `ptrdiff_t` is returned.) 1856 /// $(LI If `N` is unsigned, a `size_t` is returned.) 1857 /// ) 1858 /// `checkedint.to()` is used for bounds checking. 1859 @property Select!(isSigned!N, ptrdiff_t, size_t) idx() const 1860 { 1861 return to!(typeof(return), policy)(bscal); 1862 } 1863 /// 1864 unittest 1865 { 1866 import checkedint.throws : SafeInt; // use IntFlagPolicy.throws 1867 1868 char[3] arr = ['a', 'b', 'c']; 1869 SafeInt!long n = 1; 1870 1871 // On 32-bit, `long` cannot be used directly for array indexing, 1872 static if (size_t.sizeof < long.sizeof) 1873 static assert(!__traits(compiles, arr[n])); 1874 // but idx can be used to concisely and safely cast to ptrdiff_t: 1875 assert(arr[n.idx] == 'b'); 1876 1877 // The conversion is bounds checked: 1878 static if (size_t.sizeof < long.sizeof) 1879 { 1880 n = long.min; 1881 try 1882 { 1883 arr[n.idx] = '?'; 1884 } 1885 catch(CheckedIntException e) 1886 { 1887 assert(e.intFlags == IntFlag.negOver); 1888 } 1889 } 1890 } 1891 1892 /// Get a simple hashcode for this value. 1893 size_t toHash() const pure nothrow @nogc 1894 { 1895 static if (N.sizeof > size_t.sizeof) 1896 { 1897 static assert(N.sizeof == (2 * size_t.sizeof)); 1898 return cast(size_t)bscal ^ cast(size_t)(bscal >>> 32); 1899 } 1900 else 1901 return cast(size_t)bscal; 1902 } 1903 1904 /// Get a `string` representation of this value. 1905 void toString(Writer, Char)(Writer sink, FormatSpec!Char fmt = (FormatSpec!Char).init) const @trusted 1906 { 1907 formatValue(sink, bscal, fmt); 1908 } 1909 /// ditto 1910 string toString() const 1911 { 1912 return to!(string, IntFlagPolicy.noex)(bscal); 1913 } 1914 /// 1915 unittest 1916 { 1917 import checkedint.throws : safeInt; // use IntFlagPolicy.throws 1918 assert(safeInt(-753).toString() == "-753"); 1919 } 1920 1921 // Comparison ///////////////////////////////////////////////// 1922 /// See `safeOp`. 1923 bool opEquals(M)(const M right) const pure nothrow @nogc 1924 if (isSafeInt!M || isScalarType!M) 1925 { 1926 return safeOp!(policy).cmp!"=="(this.bscal, right.bscal); 1927 } 1928 1929 /// Perform a floating-point comparison to `right`. 1930 /// 1931 /// Returns: $(UL 1932 /// $(LI `-1` if this value is less than `right`.) 1933 /// $(LI ` 0` if this value is equal to `right`.) 1934 /// $(LI ` 1` if this value is greater than `right`.) 1935 /// $(LI `float.nan` if `right` is a floating-point `nan` value.)) 1936 auto opCmp(M)(const M right) const pure nothrow @nogc 1937 if (isFloatingPoint!M) 1938 { 1939 return 1940 (left < right)? -1 : 1941 (left > right)? 1 : 1942 (left == right)? 0 : float.nan; 1943 } 1944 /// See `safeOp`. 1945 int opCmp(M)(const M right) const pure nothrow @nogc 1946 if (isSafeInt!M || isFixedPoint!M) 1947 { 1948 return 1949 safeOp!(policy).cmp!"<"(this.bscal, right.bscal)? -1 : 1950 safeOp!(policy).cmp!">"(this.bscal, right.bscal); 1951 } 1952 1953 // Unary ///////////////////////////////////////////////// 1954 /// ditto 1955 typeof(this) opUnary(string op)() const 1956 if (op.among!("-", "+", "~")) 1957 { 1958 static assert(bitOps || (op != "~"), 1959 "Bitwise operations are disabled."); 1960 1961 return typeof(return)(safeOp!(policy).unary!op(bscal)); 1962 } 1963 /// ditto 1964 ref typeof(this) opUnary(string op)() /+return+/ 1965 if (op.among!("++", "--")) 1966 { 1967 safeOp!(policy).unary!op(bscal); 1968 return this; 1969 } 1970 1971 /// ditto 1972 typeof(this) abs() const 1973 { 1974 return typeof(return)(safeOp!(policy).abs(bscal)); 1975 } 1976 1977 /// Count the number of set bits using `core.bitop.popcnt()`. 1978 SafeInt!(int, policy, bitOps) popcnt()() const pure nothrow @nogc 1979 { 1980 static assert(bitOps, "Bitwise operations are disabled."); 1981 1982 import future.bitop : stdPC = popcnt; 1983 return typeof(return)(stdPC(bscal)); 1984 } 1985 1986 /// See `safeOp`. 1987 SafeInt!(int, policy, bitOps) bsf()() const 1988 { 1989 static assert(bitOps, "Bitwise operations are disabled."); 1990 1991 return typeof(return)(safeOp!(policy).bsf(bscal)); 1992 } 1993 /// ditto 1994 SafeInt!(int, policy, bitOps) bsr()() const 1995 { 1996 static assert(bitOps, "Bitwise operations are disabled. Consider using ilogb() instead?"); 1997 1998 return typeof(return)(safeOp!(policy).bsr(bscal)); 1999 } 2000 2001 /// ditto 2002 SafeInt!(int, policy, bitOps) ilogb() const 2003 { 2004 return typeof(return)(safeOp!(policy).ilogb(bscal)); 2005 } 2006 2007 // Binary ///////////////////////////////////////////////// 2008 /// Perform a floating-point math operation. 2009 M opBinaryRight(string op, M)(const M left) const pure nothrow @nogc 2010 if (isFloatingPoint!M) 2011 { 2012 return mixin("left " ~ op ~ " bscal"); 2013 } 2014 /// ditto 2015 M opBinary(string op, M)(const M right) const pure nothrow @nogc 2016 if (isFloatingPoint!M) 2017 { 2018 return mixin("bscal " ~ op ~ " right"); 2019 } 2020 /// See `safeOp`. 2021 SafeInt!(OpType!(M, op, N), policy, bitOps) opBinaryRight(string op, M)(const M left) const 2022 if (isFixedPoint!M) 2023 { 2024 static assert(bitOps || !op.among!("<<", ">>", ">>>", "&", "|", "^"), 2025 "Bitwise operations are disabled. Consider using mulPow2(), divPow2(), or modPow2() instead?"); 2026 2027 return typeof(return)(safeOp!(policy).binary!op(left, bscal)); 2028 } 2029 /// ditto 2030 SafeInt!(OpType!(N, op, BasicScalar!M), .max(policy, intFlagPolicyOf!M), bitOps && hasBitOps!M) opBinary(string op, M)(const M right) const 2031 if (isSafeInt!M || isFixedPoint!M) 2032 { 2033 static assert(bitOps && hasBitOps!M || !op.among!("<<", ">>", ">>>", "&", "|", "^"), 2034 "Bitwise operations are disabled. Consider using mulPow2(), divPow2(), or modPow2() instead?"); 2035 2036 return typeof(return)(safeOp!(.max(policy, intFlagPolicyOf!M)).binary!op(this.bscal, right.bscal)); 2037 } 2038 /// ditto 2039 ref typeof(this) opOpAssign(string op, M)(const M right) /+return+/ 2040 if (isCheckedInt!M || isFixedPoint!M) 2041 { 2042 static assert((bitOps && hasBitOps!M) || !op.among!("<<", ">>", ">>>", "&", "|", "^"), 2043 "Bitwise operations are disabled. Consider using mulPow2(), divPow2(), or modPow2() instead?"); 2044 checkImplicit!(OpType!(N, op, BasicScalar!M))(); 2045 2046 safeOp!(.max(policy, intFlagPolicyOf!M)).binary!(op ~ "=")(this.bscal, right.bscal); 2047 return this; 2048 } 2049 2050 /// ditto 2051 auto mulPow2(M)(const M exp) const pure nothrow @nogc 2052 if (isFloatingPoint!M) 2053 { 2054 return safeOp!(policy).mulPow2(bscal, exp); 2055 } 2056 /// ditto 2057 auto mulPow2(M)(const M exp) const 2058 if (isCheckedInt!M || isFixedPoint!M) 2059 { 2060 enum mixPolicy = .max(policy, intFlagPolicyOf!M); 2061 const wret = safeOp!(mixPolicy).mulPow2(this.bscal, exp.bscal); 2062 return SafeInt!(typeof(wret), mixPolicy, bitOps && hasBitOps!M)(wret); 2063 } 2064 /// ditto 2065 auto divPow2(M)(const M exp) const pure nothrow @nogc 2066 if (isFloatingPoint!M) 2067 { 2068 return safeOp!(policy).divPow2(bscal, exp); 2069 } 2070 /// ditto 2071 auto divPow2(M)(const M exp) const 2072 if (isCheckedInt!M || isFixedPoint!M) 2073 { 2074 enum mixPolicy = .max(policy, intFlagPolicyOf!M); 2075 const wret = safeOp!(mixPolicy).divPow2(this.bscal, exp.bscal); 2076 return SafeInt!(typeof(wret), mixPolicy, bitOps && hasBitOps!M)(wret); 2077 } 2078 /// ditto 2079 auto modPow2(M)(const M exp) const pure nothrow @nogc 2080 if (isFloatingPoint!M) 2081 { 2082 return safeOp!(policy).modPow2(bscal, exp); 2083 } 2084 /// ditto 2085 auto modPow2(M)(const M exp) const 2086 if (isCheckedInt!M || isFixedPoint!M) 2087 { 2088 enum mixPolicy = .max(policy, intFlagPolicyOf!M); 2089 const wret = safeOp!(mixPolicy).modPow2(this.bscal, exp.bscal); 2090 return SafeInt!(typeof(wret), mixPolicy, bitOps && hasBitOps!M)(wret); 2091 } 2092 2093 /// Raise `this` to the `exp` power using `std.math.pow()`. 2094 M pow(M)(const M exp) const pure nothrow @nogc 2095 if (isFloatingPoint!M) 2096 { 2097 return std.math.pow(bscal, exp); 2098 } 2099 /// See `safeOp`. 2100 SafeInt!(CallType!(std.math.pow, N, BasicScalar!M), .max(policy, intFlagPolicyOf!M), bitOps && hasBitOps!M) pow(M)(const M exp) const 2101 if (isCheckedInt!M || isFixedPoint!M) 2102 { 2103 return typeof(return)(safeOp!(.max(policy, intFlagPolicyOf!M)).pow(this.bscal, exp.bscal)); 2104 } 2105 } 2106 /// ditto 2107 template SafeInt(N, IntFlagPolicy policy, Flag!"bitOps" bitOps = Yes.bitOps) 2108 if ((isIntegral!N && !isUnqual!N) || isCheckedInt!N) 2109 { 2110 alias SafeInt = SafeInt!(BasicScalar!N, policy, bitOps); 2111 } 2112 /// 2113 unittest 2114 { 2115 // Mixing standard signed and unsigned types is dangerous... 2116 int ba = -1; 2117 uint bb = 0; 2118 assert(ba > bb); 2119 2120 auto bc = ba + bb; 2121 assert(is(typeof(bc) == uint)); 2122 assert(bc == 4294967295u); 2123 2124 // ...that's why SafeInt won't let you do it. 2125 import checkedint.throws : SafeInt, to; // use IntFlagPolicy.throws 2126 2127 SafeInt!int sa = -1; 2128 SafeInt!uint sb = 0u; 2129 static assert(!__traits(compiles, sa < sb)); 2130 static assert(!__traits(compiles, sa + sb)); 2131 2132 // Instead, use checkedint.to() to safely convert to a common type... 2133 auto sbi = to!(SafeInt!int)(sb); 2134 assert(sa < sbi); 2135 auto sc = sa + sbi; 2136 assert(sc == -1); 2137 // (...or just switch to SmartInt.) 2138 } 2139 /// 2140 unittest 2141 { 2142 // When IntFlagPolicy.throws is set, SafeInt operations that fail at runtime will throw a CheckedIntException. 2143 import checkedint.throws : SafeInt; 2144 2145 SafeInt!uint sa = 1u; 2146 SafeInt!uint sb = 0u; 2147 2148 bool overflow = false; 2149 try 2150 { 2151 SafeInt!uint sc = sb - sa; 2152 assert(false); 2153 } 2154 catch(CheckedIntException e) 2155 { 2156 assert(e.intFlags == IntFlag.negOver); 2157 overflow = true; 2158 } 2159 assert(overflow); 2160 2161 bool div0 = false; 2162 try 2163 { 2164 // With standard integers, this would crash the program with an unrecoverable FPE... 2165 SafeInt!uint sc = sa / sb; 2166 assert(false); 2167 } 2168 catch(CheckedIntException e) 2169 { 2170 // ...but with SafeInt, it just throws a normal Exception. 2171 assert(e.intFlags == IntFlag.div0); 2172 div0 = true; 2173 } 2174 assert(div0); 2175 } 2176 /// 2177 unittest 2178 { 2179 // When IntFlagPolicy.noex is set, SafeInt operations that fail at runtime set one or more bits in IntFlags.local. 2180 import checkedint.noex : SafeInt; 2181 2182 SafeInt!uint sa = 1u; 2183 SafeInt!uint sb = 0u; 2184 SafeInt!uint sc; 2185 2186 sc = sb - sa; 2187 assert(IntFlags.local == IntFlag.negOver); 2188 2189 // With standard integers, this would crash the program with an unrecoverable FPE... 2190 sc = sa / sb; 2191 // ...but with SmartInt, it just sets a bit in IntFlags.local. 2192 assert(IntFlags.local & IntFlag.div0); 2193 2194 // Each flag will remain set until cleared: 2195 assert(IntFlags.local.clear() == (IntFlag.negOver | IntFlag.div0)); 2196 assert(!IntFlags.local); 2197 } 2198 2199 private template SafeInt(N, IntFlagPolicy policy, bool bitOps) 2200 if (isIntegral!N) 2201 { 2202 alias SafeInt = SafeInt!( 2203 Unqual!N, 2204 policy, 2205 cast(Flag!"bitOps")bitOps); 2206 } 2207 2208 /// Get the value of `num` as a `SafeInt!N`. The integral type `N` can be infered from the argument. 2209 SafeInt!(N, policy, bitOps) safeInt(IntFlagPolicy policy, Flag!"bitOps" bitOps = Yes.bitOps, N)(N num) 2210 if (isIntegral!N || isCheckedInt!N) 2211 { 2212 return typeof(return)(num.bscal); 2213 } 2214 /// 2215 unittest 2216 { 2217 import checkedint.throws : safeInt, SafeInt; // use IntFlagPolicy.throws 2218 2219 auto a = safeInt(55uL); 2220 static assert(is(typeof(a) == SafeInt!ulong)); 2221 assert(a == 55u); 2222 } 2223 2224 /// Implements various integer math operations with error checking. 2225 /// 2226 /// `safeOp` strives to mimic the standard integer math operations in every way, except: 2227 /// $(UL 2228 /// $(LI If the operation is generally untrustworthy - for example, signed/unsigned comparisons - a compile-time error 2229 /// is generated. The message will usually suggest a workaround.) 2230 /// $(LI At runtime, if the result is mathematically incorrect an appropriate `IntFlag` will be raised.) 2231 /// ) 2232 /// The runtime error-signalling policy may be selected using the `policy` template parameter. 2233 template safeOp(IntFlagPolicy policy) 2234 { 2235 // NOTE: ddoc only scans the first branch of a static if 2236 static if (policy == IntFlagPolicy.none) 2237 { 2238 // No need to redundantly instantiate members which don't depend on `policy`. 2239 2240 private void cmpTypeCheck(N, M)() pure nothrow @nogc 2241 { 2242 static assert(isBoolean!N == isBoolean!M, 2243 "The intent of a direct comparison of " ~ 2244 N.stringof ~ " with " ~ M.stringof ~ 2245 " is unclear. Add an explicit cast." 2246 ); 2247 2248 alias OT = OpType!(N, "+", M); 2249 static assert(isFloatingPoint!OT || isSigned!OT || !(isSigned!N || isSigned!M), 2250 "The standard signed/unsigned comparisons of " ~ N.stringof ~ " to " ~ M.stringof ~ 2251 " are unsafe. Use an explicit cast, or switch to smartOp/SmartInt." 2252 ); 2253 } 2254 2255 /** 2256 Compare `left` and `right` using `op`. 2257 2258 Unsafe signed/unsigned comparisons will trigger a compile-time error. Possible solutions include: 2259 $(UL 2260 $(LI Should the inputs really have different signedness? Changing the type of one to match the other is the simplest 2261 solution.) 2262 $(LI Consider using `smartOp.cmp()`, instead, as it can safely do signed/unsigned comparisons.) 2263 $(LI Alternately, `checkedint.to()` can be used to safely convert the type of one input, with runtime bounds 2264 checking.) 2265 ) $(BR) 2266 Direct comparisons between boolean values and numeric ones are also forbidden. Make your intention explicit: 2267 $(UL 2268 $(LI `cast(N)boolean == numeric`) 2269 $(LI `boolean == (numeric != 0)`) 2270 ) 2271 **/ 2272 bool cmp(string op, N, M)(const N left, const M right) pure nothrow @nogc 2273 if (isScalarType!N && isScalarType!M) 2274 { 2275 cmpTypeCheck!(N, M)(); 2276 return mixin("left " ~ op ~ " right"); 2277 } 2278 /// 2279 unittest 2280 { 2281 import checkedint.noex : safeOp; // safeOp.cmp() never throws 2282 2283 assert(safeOp.cmp!"=="(int.max, 0x7FFF_FFFF)); 2284 assert(safeOp.cmp!"!="(uint.min, 5u)); 2285 assert(safeOp.cmp!"<="(int.min, 0)); 2286 2287 static assert(!__traits(compiles, safeOp.cmp!"=="(uint.max, -1))); 2288 static assert(!__traits(compiles, safeOp.cmp!">"(-1, 1u))); 2289 } 2290 } 2291 else 2292 alias cmp = safeOp!(IntFlagPolicy.none).cmp; 2293 2294 /// Perform the unary (single-argument) integer operation specified by `op`. 2295 /// 2296 /// Trying to negate `-` an unsigned value will generate a compile-time error, because mathematically, the result should 2297 /// always be negative (except for -0), but the unsigned return type cannot represent this. 2298 /// 2299 /// `++` and `--` are checked for overflow at runtime, and will raise `IntFlag.posOver` or `IntFlag.negOver` if needed. 2300 N unary(string op, N)(const N num) 2301 if ((isIntegral!N) && op.among!("-", "+", "~")) 2302 { 2303 static assert(isSigned!N || op != "-", 2304 "The standard unary - operation for " ~ N.stringof ~ 2305 " is unsafe. Use an explicit cast to a signed type, or switch to smartOp/SmartInt." 2306 ); 2307 2308 static if (op == "-") 2309 { 2310 static if (is(N == int) || is(N == long)) 2311 { 2312 bool over = false; 2313 const N ret = negs(num, over); 2314 } 2315 else 2316 { 2317 const over = (num <= trueMin!N); 2318 const N ret = -num; 2319 } 2320 2321 if (over) 2322 IntFlag.posOver.raise!policy(); 2323 2324 return ret; 2325 } 2326 else 2327 return mixin(op ~ "num"); 2328 } 2329 /// ditto 2330 /+ref+/ N unary(string op, N)(/+return+/ ref N num) 2331 if ((isIntegral!N) && op.among!("++", "--")) 2332 { 2333 static if (op == "++") 2334 { 2335 if (num >= trueMax!N) 2336 IntFlag.posOver.raise!policy(); 2337 } 2338 else static if (op == "--") 2339 { 2340 if (num <= trueMin!N) 2341 IntFlag.negOver.raise!policy(); 2342 } 2343 2344 return mixin(op ~ "num"); 2345 } 2346 /// 2347 unittest 2348 { 2349 import checkedint.noex : safeOp; // use IntFlagPolicy.noex 2350 2351 assert(safeOp.unary!"~"(0u) == uint.max); 2352 2353 assert(safeOp.unary!"-"(20L) == -20L); 2354 static assert(!__traits(compiles, safeOp.unary!"-"(20uL))); 2355 safeOp.unary!"-"(long.min); 2356 assert(IntFlags.local.clear() == IntFlag.posOver); 2357 2358 auto a = safeOp.unary!"+"(uint.max); 2359 static assert(is(typeof(a) == uint)); 2360 assert(a == uint.max); 2361 2362 uint b = 1u; 2363 assert(safeOp.unary!"--"(b) == 0u); 2364 assert(b == 0u); 2365 safeOp.unary!"--"(b); 2366 assert(IntFlags.local.clear() == IntFlag.negOver); 2367 2368 int c = 7; 2369 assert(safeOp.unary!"++"(c) == 8); 2370 assert(c == 8); 2371 } 2372 2373 /// Get the absolute value of `num`. 2374 /// 2375 /// `IntFlag.posOver` is raised if `N` is signed and `num == N.min`. 2376 N abs(N)(const N num) 2377 if (isIntegral!N || isBoolean!N) 2378 { 2379 static if (isSigned!N) 2380 { 2381 if (num < 0) 2382 return unary!"-"(num); 2383 } 2384 return num; 2385 } 2386 /// 2387 unittest 2388 { 2389 import checkedint.noex : safeOp; // use IntFlagPolicy.noex 2390 2391 assert(safeOp.abs(-25) == 25); 2392 assert(safeOp.abs(745u) == 745u); 2393 2394 safeOp.abs(int.min); 2395 assert(IntFlags.local.clear() == IntFlag.posOver); 2396 } 2397 2398 /// `core.bitop.bsf` without the undefined behaviour. `safeOp.bsf(0)` will raise `IntFlag.undef`. 2399 int bsf(N)(const N num) 2400 if (isFixedPoint!N) 2401 { 2402 return bsfImpl!policy(num); 2403 } 2404 /// 2405 unittest 2406 { 2407 import checkedint.noex : safeOp; // use IntFlagPolicy.noex 2408 2409 assert(safeOp.bsf(20) == 2); 2410 2411 safeOp.bsf(0); 2412 assert(IntFlags.local.clear() == IntFlag.undef); 2413 } 2414 2415 /// `core.bitop.bsr` without the undefined behaviour. `safeOp.bsr(0)` will raise `IntFlag.undef`. 2416 int bsr(N)(const N num) 2417 if (isFixedPoint!N) 2418 { 2419 return bsrImpl!policy(num); 2420 } 2421 /// 2422 unittest 2423 { 2424 import checkedint.noex : safeOp; // use IntFlagPolicy.noex 2425 2426 assert(safeOp.bsr( 20) == 4); 2427 assert(safeOp.bsr(-20) == 31); 2428 2429 safeOp.bsr(0); 2430 assert(IntFlags.local.clear() == IntFlag.undef); 2431 } 2432 2433 /// Get the base 2 logarithm of `abs(num)`, rounded down to the nearest integer. 2434 /// 2435 /// `safeOp.ilogb(0)` will raise `IntFlag.undef`. 2436 int ilogb(N)(const N num) 2437 if (isFixedPoint!N) 2438 { 2439 static if (isSigned!N) 2440 const absN = cast(Unsigned!N) (num < 0? -num : num); 2441 else 2442 alias absN = num; 2443 2444 return bsrImpl!policy(absN); 2445 } 2446 /// 2447 unittest 2448 { 2449 import checkedint.noex : safeOp; // use IntFlagPolicy.noex 2450 2451 assert(safeOp.ilogb( 20) == 4); 2452 assert(safeOp.ilogb(-20) == 4); 2453 2454 safeOp.ilogb(0); 2455 assert(IntFlags.local.clear() == IntFlag.undef); 2456 } 2457 2458 private auto binaryImpl(string op, N, M)(const N left, const M right) 2459 if (isFixedPoint!N && isFixedPoint!M) 2460 { 2461 enum wop = (op[$-1] == '=')? op[0 .. $-1] : op; 2462 alias P = OpType!(N, wop, M); 2463 alias R = Select!(wop == op, P, N); 2464 2465 static if (wop.among!("+", "-", "*")) 2466 { 2467 enum isPromSafe = !(isSigned!N || isSigned!M) || (isSigned!P && isSigned!R); 2468 enum needCOp = (wop == "*")? 2469 (precision!N + precision!M) > precision!P : 2470 (max(precision!N, precision!M) + 1) > precision!P; 2471 2472 bool over = false; 2473 static if (needCOp) 2474 { 2475 enum cx = (staticIndexOf!(wop, "+", "-", "*") << 1) + isSigned!P; 2476 alias cop = AliasSeq!(addu, adds, subu, subs, mulu, muls)[cx]; 2477 2478 const pR = cop(cast(P)left, cast(P)right, over); 2479 } 2480 else 2481 const pR = mixin("left " ~ wop ~ " right"); 2482 2483 static if (isSigned!P && trueMin!P < trueMin!R) 2484 { 2485 if (pR < trueMin!R) 2486 over = true; 2487 } 2488 static if (trueMax!P > trueMax!R) 2489 { 2490 if (pR > trueMax!R) 2491 over = true; 2492 } 2493 2494 if (over) 2495 IntFlag.over.raise!policy(); 2496 return cast(R)pR; 2497 } 2498 else static if (wop.among!("/", "%")) 2499 { 2500 enum isPromSafe = !(isSigned!N || isSigned!M) || 2501 (isSigned!P && (wop == "%"? (isSigned!R || !isSigned!N) : isSigned!R)); 2502 2503 const div0 = (right == 0); 2504 static if (isSigned!N && isSigned!M) 2505 const posOver = (left == trueMin!R) && (right == -1); 2506 else 2507 enum posOver = false; 2508 2509 R ret = void; 2510 if (div0 || posOver) 2511 { 2512 (posOver? IntFlag.posOver : IntFlag.div0).raise!policy(); 2513 ret = 0; // Prevent unrecoverable FPE 2514 } 2515 else 2516 ret = cast(R)mixin("left " ~ wop ~ " right"); 2517 2518 return ret; 2519 } 2520 else static if (wop.among!("<<", ">>", ">>>")) 2521 { 2522 enum isPromSafe = !isSigned!N || isSigned!R || (op == ">>>"); 2523 2524 enum invalidSh = ~cast(M)(8 * P.sizeof - 1); 2525 if (right & invalidSh) 2526 IntFlag.undef.raise!policy(); 2527 2528 return cast(R) mixin("cast(P)left " ~ wop ~ " right"); 2529 } 2530 else static if (wop.among!("&", "|", "^")) 2531 { 2532 enum isPromSafe = true; 2533 2534 return cast(R)mixin("left " ~ wop ~ " right"); 2535 } 2536 else 2537 static assert(false); 2538 2539 static assert(isPromSafe, 2540 "The standard " ~ N.stringof ~ " " ~ op ~ " " ~ M.stringof ~ 2541 " operation is unsafe, due to a signed/unsigned mismatch. " ~ 2542 "Use an explicit cast, or switch to smartOp/SmartInt." 2543 ); 2544 } 2545 2546 /** 2547 Perform the binary (two-argument) integer operation specified by `op`. 2548 $(UL 2549 $(LI Unsafe signed/unsigned operations will generate a compile-time error.) 2550 $(LI `+`, `-`, `*`, `/`, and `%` are checked for overflow at runtime.) 2551 $(LI `/` and `%` are also checked for divide-by-zero.) 2552 $(LI `<<`, `>>`, and `>>>` are checked to verify that `right >= 0` and `right < (8 * typeof(left).sizeof)`. 2553 Otherwise, `IntFlag.undef` is raised.) 2554 ) $(BR) 2555 Note also: 2556 $(UL 2557 $(LI The shift operators are $(B $(RED not)) checked for overflow and should not be used for multiplication, 2558 division, or exponentiation. Instead, use `mulPow2()` and `divPow2()`, which internally use the bitshifts for speed, 2559 but check for overflow and correctly handle negative values.) 2560 $(LI Likewise, `modPow2()` should be used for remainders instead of `&`.) 2561 $(LI `^^` and `^^=` will remain disabled in favour of `pow` until DMD issues 15288 and 15412 are fixed.) 2562 ) $(BR) 2563 Like the standard equiavlents, the assignment operators (`+=`, `-=`, `*=`, etc.) take `left` by `ref` and will overwrite 2564 it with the result of the operation. 2565 **/ 2566 OpType!(N, op, M) binary(string op, N, M)(const N left, const M right) 2567 if (isFixedPoint!N && isFixedPoint!M && 2568 op.among!("+", "-", "*", "/", "%", "^^", "<<", ">>", ">>>", "&", "|", "^")) 2569 { 2570 static assert(op != "^^", 2571 "pow() should be used instead of operator ^^ because of issue 15288."); 2572 2573 return binaryImpl!op(left, right); 2574 } 2575 /// ditto 2576 /+ref+/ N binary(string op, N, M)(/+return+/ ref N left, const M right) 2577 if (isIntegral!N && isFixedPoint!M && (op[$ - 1] == '=')) 2578 { 2579 static assert(op != "^^=", 2580 "pow() should be used instead of operator ^^= because of issue 15412."); 2581 2582 left = binaryImpl!op(left, right); 2583 return left; 2584 } 2585 /// 2586 unittest 2587 { 2588 import checkedint.noex : safeOp; // use IntFlagPolicy.noex 2589 2590 assert(safeOp.binary!"+"(17, -5) == 12); 2591 static assert(!__traits(compiles, safeOp.binary!"+"(-1, 1u))); 2592 2593 ulong a = 18_446_744_073_709_551_615uL; 2594 safeOp.binary!"+="(a, 1u); 2595 assert(IntFlags.local.clear() == IntFlag.posOver); 2596 2597 assert(safeOp.binary!"-"(17u, 5u) == 12u); 2598 safeOp.binary!"-"(5u, 17u); 2599 assert(IntFlags.local.clear() == IntFlag.negOver); 2600 2601 ulong b = 123_456_789_987_654_321uL; 2602 static assert(!__traits(compiles, safeOp.binary!"-="(b, 987_654_321))); 2603 assert(safeOp.binary!"-="(b, 987_654_321u) == 123_456_789_000_000_000uL); 2604 assert(b == 123_456_789_000_000_000uL); 2605 2606 assert(safeOp.binary!"*"(-1 << 30, 2) == int.min); 2607 safeOp.binary!"*"(1 << 30, 2); 2608 assert(IntFlags.local.clear() == IntFlag.negOver); 2609 2610 uint c = 1u << 18; 2611 assert(safeOp.binary!"*="(c, 1u << 4) == 1u << 22); 2612 assert(c == 1u << 22); 2613 2614 assert(safeOp.binary!"/"(22, 11) == 2); 2615 assert(!__traits(compiles, safeOp.binary!"/"(-22, 11u))); 2616 safeOp.binary!"/"(0, 0); 2617 assert(IntFlags.local.clear() == IntFlag.div0); 2618 2619 long j = long.min; 2620 safeOp.binary!"/="(j, -1); 2621 assert(IntFlags.local.clear() == IntFlag.posOver); 2622 2623 assert(safeOp.binary!"%"(20u, 7u) == 6u); 2624 static assert(!__traits(compiles, safeOp.binary!"%"(20u, -7))); 2625 safeOp.binary!"%"(20u, 0u); 2626 assert(IntFlags.local.clear() == IntFlag.div0); 2627 2628 short n = 75; 2629 assert(safeOp.binary!"%="(n, -10) == 5); 2630 assert(n == 5); 2631 } 2632 /// 2633 unittest 2634 { 2635 import checkedint.noex : safeOp; // use IntFlagPolicy.noex 2636 2637 assert(safeOp.binary!"<<"(-0x80, 2) == -0x200); 2638 safeOp.binary!"<<"(-0x80, -2); 2639 assert(IntFlags.local.clear() == IntFlag.undef); 2640 2641 ubyte a = 0x3u; 2642 safeOp.binary!"<<="(a, 7); 2643 assert(a == 0x80u); 2644 2645 assert(safeOp.binary!">>"(-0xC, 5u) == -0x1); 2646 safeOp.binary!">>"(-0xC, long.max); 2647 assert(IntFlags.local.clear() == IntFlag.undef); 2648 2649 short b = 0x700; 2650 assert(safeOp.binary!">>="(b, 8) == 0x7); 2651 assert(b == 0x7); 2652 2653 assert(safeOp.binary!">>>"(-0x80, 2u) == 0x3FFF_FFE0); 2654 safeOp.binary!">>>"(-0x80, 32); 2655 assert(IntFlags.local.clear() == IntFlag.undef); 2656 2657 int c = 0xFE_DCBA; 2658 assert(safeOp.binary!">>>="(c, 12) == 0xFED); 2659 assert(c == 0xFED); 2660 2661 assert(safeOp.binary!"&"(0x6Fu, 0x4076) == 0x66u); 2662 2663 ubyte d = 0x6Fu; 2664 assert(safeOp.binary!"&="(d, 0x4076) == 0x66u); 2665 assert(d == 0x66u); 2666 2667 assert(safeOp.binary!"|"(0x6F, 0x4076u) == 0x407Fu); 2668 2669 byte e = 0x6F; 2670 assert(safeOp.binary!"|="(e, 0x4076u) == 0x7F); 2671 assert(e == 0x7F); 2672 2673 assert(safeOp.binary!"^"(0x6F, 0x4076) == 0x4019); 2674 2675 int f = 0x6F; 2676 assert(safeOp.binary!"^="(f, 0x4076) == 0x4019); 2677 assert(f == 0x4019); 2678 2679 assert(!IntFlags.local); 2680 } 2681 2682 /// Equivalent to `left * pow(2, exp)`, but faster and works with a wider range of inputs. This is a safer alternative to 2683 /// `left << exp` that is still very fast. 2684 /// 2685 /// Note that (conceptually) rounding occurs $(I after) the `*`, meaning that `mulPow2(left, -exp)` is equivalent to 2686 /// `divPow2(left, exp)`. 2687 auto mulPow2(N, M)(const N left, const M exp) pure nothrow @nogc 2688 if ((isFloatingPoint!N && isScalarType!M) || (isScalarType!N && isFloatingPoint!M)) 2689 { 2690 return byPow2Impl!("*", NumFromScal!N, NumFromScal!M)(left, exp); 2691 } 2692 /// ditto 2693 auto mulPow2(N, M)(const N left, const M exp) 2694 if (isFixedPoint!N && isFixedPoint!M) 2695 { 2696 return byPow2Impl!("*", policy, NumFromScal!N, NumFromScal!M)(left, exp); 2697 } 2698 /// 2699 unittest 2700 { 2701 import checkedint.noex : safeOp; // use IntFlagPolicy.noex 2702 2703 assert(safeOp.mulPow2(-23, 5) == -736); 2704 safeOp.mulPow2(10_000_000, 10); 2705 assert(IntFlags.local.clear() == IntFlag.posOver); 2706 2707 assert(safeOp.mulPow2(65536, -8) == 256); 2708 assert(safeOp.mulPow2(-100, -100) == 0); 2709 } 2710 2711 /// Equivalent to `left / pow(2, exp)`, but faster and works with a wider range of inputs. This is a safer alternative to 2712 /// `left >> exp` that is still very fast. 2713 /// 2714 /// Note that (conceptually) rounding occurs $(I after) the `/`, meaning that `divPow2(left, -exp)` is equivalent to 2715 /// `mulPow2(left, exp)`. 2716 auto divPow2(N, M)(const N left, const M exp) pure nothrow @nogc 2717 if ((isFloatingPoint!N && isScalarType!M) || (isScalarType!N && isFloatingPoint!M)) 2718 { 2719 return byPow2Impl!("/", NumFromScal!N, NumFromScal!M)(left, exp); 2720 } 2721 /// ditto 2722 auto divPow2(N, M)(const N left, const M exp) 2723 if (isFixedPoint!N && isFixedPoint!M) 2724 { 2725 return byPow2Impl!("/", policy, NumFromScal!N, NumFromScal!M)(left, exp); 2726 } 2727 /// 2728 unittest 2729 { 2730 import checkedint.noex : safeOp; // use IntFlagPolicy.noex 2731 2732 assert(safeOp.divPow2(65536, 8) == 256); 2733 assert(safeOp.divPow2(-100, 100) == 0); 2734 assert(safeOp.divPow2(-23, -5) == -736); 2735 2736 safeOp.divPow2(10_000_000, -10); 2737 assert(IntFlags.local.clear() == IntFlag.posOver); 2738 } 2739 2740 /// Equivalent to `left % pow(2, exp)`, but faster and works with a wider range of inputs. This is a safer alternative to 2741 /// `left & ((1 << exp) - 1)` that is still very fast. 2742 auto modPow2(N, M)(const N left, const M exp) pure nothrow @nogc 2743 if ((isFloatingPoint!N && isScalarType!M) || (isScalarType!N && isFloatingPoint!M)) 2744 { 2745 return byPow2Impl!("%", NumFromScal!N, NumFromScal!M)(left, exp); 2746 } 2747 /// ditto 2748 auto modPow2(N, M)(const N left, const M exp) pure nothrow @nogc 2749 if (isFixedPoint!N && isFixedPoint!M) 2750 { 2751 return byPow2Impl!("%", IntFlagPolicy.noex, NumFromScal!N, NumFromScal!M)(left, exp); 2752 } 2753 /// 2754 unittest 2755 { 2756 import checkedint.noex : safeOp; // use IntFlagPolicy.noex 2757 2758 assert(safeOp.modPow2( 101, 1) == 1); 2759 assert(safeOp.modPow2( 101, 3) == 5); 2760 assert(safeOp.modPow2(-101, 3) == -5); 2761 2762 assert(safeOp.modPow2(101, -2) == 0); 2763 assert(safeOp.modPow2(101, 1_000) == 101); 2764 } 2765 2766 /// Raise `base` to the `exp` power. 2767 /// 2768 /// Errors that may be signalled if neither input is floating-point: 2769 /// $(UL 2770 /// $(LI `IntFlag.posOver` or `IntFlag.negOver` if the absolute value of the result is too large to 2771 /// represent with the return type.) 2772 /// $(LI `exp < 0`, `IntFlag.undef` is raised because `std.math.pow` would trigger an FPE given the same input.) 2773 /// ) 2774 CallType!(std.math.pow, N, M) pow(N, M)(const N base, const M exp) 2775 if (isFixedPoint!N && isFixedPoint!M) 2776 { 2777 alias R = typeof(return); 2778 static assert(!isSigned!N || isSigned!R, 2779 "std.math.pow(" ~ N.stringof ~ ", " ~ M.stringof ~ 2780 ") is unsafe, due to a signed/unsigned mismatch. Use an explicit cast, or switch to smartOp/SmartInt." 2781 ); 2782 2783 auto po = powImpl!(R, Select!(isSigned!M, long, ulong))(base, exp); 2784 static assert(is(typeof(po.num) == R)); 2785 if (exp < 0) 2786 po.flag = IntFlag.undef; 2787 2788 if (!po.flag.isNull) 2789 po.flag.raise!policy(); 2790 return po.num; 2791 } 2792 /// 2793 unittest 2794 { 2795 import checkedint.noex : safeOp; // use IntFlagPolicy.noex 2796 2797 assert(safeOp.pow(-10, 3) == -1_000); 2798 static assert(!__traits(compiles, safeOp.pow(16, 4uL))); 2799 safeOp.pow(2, -1); 2800 assert(IntFlags.local.clear() == IntFlag.undef); 2801 2802 safeOp.pow(-3, 27); 2803 assert(IntFlags.local.clear() == IntFlag.negOver); 2804 safeOp.pow(0, -5); 2805 assert(IntFlags.local.clear() == IntFlag.undef); 2806 } 2807 } 2808 private alias safeOp(bool throws) = safeOp!(cast(Flag!"throws")throws); 2809 2810 // conv ///////////////////////////////////////////////// 2811 2812 /// A wrapper for `std.conv.to()` which uses `checkedint.flags` for error signaling when converting between any combination 2813 /// of basic scalar types and `checkedint` types. With an appropriate `policy`, this allows `checkedint.to()` to be used 2814 /// for numeric conversions in `pure nothrow` code, unlike `std.conv.to()`. 2815 /// 2816 /// Conversions involving any other type are simply forwarded to `std.conv.to()`, with no runtime overhead. 2817 template to(T, IntFlagPolicy policy) 2818 { 2819 private enum useFlags(S) = (isCheckedInt!T || isScalarType!T) && (isCheckedInt!S || isScalarType!S); 2820 private enum reqAttrs = 2821 ((policy == IntFlagPolicy.noex || policy == IntFlagPolicy.asserts)? " nothrow" : "") ~ 2822 ((policy == IntFlagPolicy.asserts || policy == IntFlagPolicy.throws)? " pure" : ""); 2823 2824 T to(S)(const S value) 2825 if (useFlags!S) 2826 { 2827 static if (isCheckedInt!T || isCheckedInt!S) 2828 return T(checkedint.to!(BasicScalar!T, policy)(value.bscal)); 2829 else 2830 { 2831 static if (policy != IntFlagPolicy.none && !isFloatingPoint!T) 2832 { 2833 static if (isFloatingPoint!S) 2834 { 2835 if (value >= trueMin!T) 2836 { 2837 if (value > trueMax!T) 2838 IntFlag.posOver.raise!policy(); 2839 } 2840 else 2841 (std.math.isNaN(value)? IntFlag.undef : IntFlag.negOver).raise!policy(); 2842 } 2843 else 2844 { 2845 static if (cast(long)trueMin!S < cast(long)trueMin!T) 2846 { 2847 if (value < cast(S)trueMin!T) 2848 IntFlag.negOver.raise!policy(); 2849 } 2850 static if (cast(ulong)trueMax!S > cast(ulong)trueMax!T) 2851 { 2852 if (value > cast(S)trueMax!T) 2853 IntFlag.posOver.raise!policy(); 2854 } 2855 } 2856 } 2857 return cast(T)value; 2858 } 2859 } 2860 2861 mixin(` 2862 T to(S)(S value)` ~ reqAttrs ~ ` 2863 if (!useFlags!S) 2864 { 2865 import std.conv : impl = to; 2866 return impl!T(value); 2867 }`); 2868 } 2869 /// 2870 unittest 2871 { 2872 // Conversions involving only basic scalars or checkedint types use IntFlags for error signalling. 2873 import checkedint.noex : smartInt, SmartInt, smartOp, to; // use IntFlagPolicy.noex 2874 2875 assert(to!int(smartInt(-421751L)) == -421751); 2876 assert(to!(SmartInt!ubyte)(100) == 100u); 2877 2878 assert(is(typeof(to!int(50u)) == int)); 2879 assert(to!int(50u) == 50); 2880 assert(!IntFlags.local); 2881 2882 // If IntFlagPolicy.noex is set, failed conversions return garbage, but... 2883 assert(smartOp.cmp!"!="(to!int(uint.max), uint.max)); 2884 // ...IntFlags.local can be checked to see if anything went wrong. 2885 assert(IntFlags.local.clear() == IntFlag.posOver); 2886 } 2887 /// 2888 unittest 2889 { 2890 // Everything else forwards to std.conv.to(). 2891 assert(to!(string, IntFlagPolicy.throws)(55) == "55"); 2892 assert(to!(real, IntFlagPolicy.throws)("3.141519e0") == 3.141519L); 2893 2894 // Setting IntFlagPolicy.noex or .asserts will block std.conv.to(), unless the instantiation is nothrow. 2895 // Setting IntFlagPolicy.asserts or .throws will block std.conv.to(), unless the instantiation is pure. 2896 static assert(!__traits(compiles, to!(real, IntFlagPolicy.noex)("3.141519e0"))); 2897 } 2898 2899 @property { 2900 /// Get a view or copy of `num` as a basic scalar. 2901 /// 2902 /// Useful in generic code that handles both basic types, and `checkedint` types. 2903 /+ref inout(N) bscal(N)(return ref inout(N) num) 2904 if (isScalarType!N) 2905 { 2906 return num; 2907 } 2908 /// ditto 2909 ref inout(N) bscal(N)(return ref inout(N) num) 2910 if (isCheckedInt!N) 2911 { 2912 return num.bscal; 2913 } 2914 /// ditto +/ 2915 N bscal(N)(const N num) 2916 if (isScalarType!N) 2917 { 2918 return num; 2919 } 2920 /// ditto 2921 BasicScalar!N bscal(N)(const N num) 2922 if (isCheckedInt!N) 2923 { 2924 return num.bscal; 2925 } 2926 /// 2927 unittest 2928 { 2929 import checkedint.throws : smartInt, SmartInt; // use IntFlagPolicy.throws 2930 2931 assert(is(typeof(bscal(2u)) == uint)); 2932 assert(is(typeof(bscal(SmartInt!int(2))) == int)); 2933 2934 assert(bscal(-3153) == -3153); 2935 assert(bscal(smartInt(75_000)) == 75_000); 2936 } 2937 2938 /// Get a view or copy of `num` that supports bitwise operations. 2939 /// 2940 /// Useful in generic code that handles both basic types and `checkedint` types. 2941 /+ref inout(N) bits(N)(return ref inout(N) num) 2942 if (isFixedPoint!N) 2943 { 2944 return num; 2945 } 2946 /// ditto 2947 ref inout(N) bits(N)(return ref inout(N) num) 2948 if (isCheckedInt!N) 2949 { 2950 return num.bits; 2951 } 2952 /// ditto +/ 2953 N bits(N)(const N num) 2954 if (isFixedPoint!N) 2955 { 2956 return num; 2957 } 2958 /// ditto 2959 SmartInt!(BasicScalar!N, N.policy, Yes.bitOps) bits(N)(const N num) 2960 if (isSmartInt!N) 2961 { 2962 return num.bits; 2963 } 2964 /// ditto 2965 SafeInt!(BasicScalar!N, N.policy, Yes.bitOps) bits(N)(const N num) 2966 if (isSafeInt!N) 2967 { 2968 return num.bits; 2969 } 2970 /// 2971 unittest 2972 { 2973 import checkedint.throws : SmartInt; // use IntFlagPolicy.throws 2974 2975 assert(is(typeof(bits(5)) == int)); 2976 2977 SmartInt!(int, No.bitOps) noBits = 5; 2978 assert(is(typeof(bits(noBits)) == SmartInt!(int, Yes.bitOps))); 2979 2980 static assert(!__traits(compiles, noBits << 2)); 2981 assert((bits(noBits) << 2) == 20); 2982 } 2983 2984 /// Cast `num` to a basic type suitable for indexing an array. 2985 /// 2986 /// For signed types, `ptrdiff_t` is returned. For unsigned types, `size_t` is returned. 2987 Select!(isSigned!N, ptrdiff_t, size_t) idx(IntFlagPolicy policy, N)(const N num) 2988 if (isScalarType!N || isCheckedInt!N) 2989 { 2990 return to!(typeof(return), policy)(num.bscal); 2991 } 2992 /// ditto 2993 Select!(isSigned!(BasicScalar!N), ptrdiff_t, size_t) idx(N)(const N num) 2994 if (isCheckedInt!N) 2995 { 2996 return num.idx; 2997 } 2998 /// 2999 unittest 3000 { 3001 import checkedint.noex : idx, SmartInt, safeInt; // use IntFlagPolicy.noex 3002 3003 assert(is(typeof(idx(cast(long)1)) == ptrdiff_t)); 3004 assert(is(typeof(idx(cast(ubyte)1)) == size_t)); 3005 assert(is(typeof(idx(SmartInt!ulong(1))) == size_t)); 3006 3007 assert(idx(17uL) == 17); 3008 assert(idx(-3) == -3); 3009 assert(idx(safeInt(cast(byte)100)) == 100); 3010 3011 static if (size_t.sizeof == 4) 3012 { 3013 idx(ulong.max); 3014 assert(IntFlags.local.clear() == IntFlag.posOver); 3015 3016 idx(long.min); 3017 assert(IntFlags.local.clear() == IntFlag.negOver); 3018 } 3019 } 3020 } 3021 /+}+/ 3022 3023 // traits ///////////////////////////////////////////////// 3024 3025 /// Evaluates to `true` if `T` is an instance of `SafeInt`. 3026 enum isSafeInt(T) = isInstanceOf!(SafeInt, T); 3027 /// 3028 unittest 3029 { 3030 import checkedint.throws : SmartInt, SafeInt; // use IntFlagPolicy.throws 3031 3032 assert( isSafeInt!(SafeInt!int)); 3033 3034 assert(!isSafeInt!int); 3035 assert(!isSafeInt!(SmartInt!int)); 3036 } 3037 3038 /// Evaluates to `true` if `T` is an instance of `SmartInt`. 3039 enum isSmartInt(T) = isInstanceOf!(SmartInt, T); 3040 /// 3041 unittest 3042 { 3043 import checkedint.throws : SmartInt, SafeInt; // use IntFlagPolicy.throws 3044 3045 assert( isSmartInt!(SmartInt!int)); 3046 3047 assert(!isSmartInt!int); 3048 assert(!isSmartInt!(SafeInt!int)); 3049 } 3050 3051 /// Evaluates to `true` if `T` is an instance of `SafeInt` or `SmartInt`. 3052 enum isCheckedInt(T) = isSafeInt!T || isSmartInt!T; 3053 /// 3054 unittest 3055 { 3056 import checkedint.throws : SmartInt, SafeInt; // use IntFlagPolicy.throws 3057 3058 assert( isCheckedInt!(SafeInt!int)); 3059 assert( isCheckedInt!(SmartInt!int)); 3060 3061 assert(!isCheckedInt!int); 3062 } 3063 3064 /// Evaluates to `true` if either: 3065 /// $(UL 3066 /// $(LI `isScalarType!T`, or) 3067 /// $(LI `isCheckedInt!T`) 3068 /// ) 3069 /// $(B And) bitwise operators such as `<<` and `~` are available for `T`. 3070 template hasBitOps(T) 3071 { 3072 static if (isCheckedInt!T) 3073 enum hasBitOps = TemplateArgsOf!T[2]; 3074 else 3075 enum hasBitOps = isFixedPoint!T; 3076 } 3077 /// 3078 unittest 3079 { 3080 import checkedint.throws : SmartInt, SafeInt; // use IntFlagPolicy.throws 3081 3082 assert( hasBitOps!(SafeInt!(int, Yes.bitOps))); 3083 assert( hasBitOps!(SmartInt!(int, Yes.bitOps))); 3084 assert( hasBitOps!int); 3085 assert( hasBitOps!bool); 3086 assert( hasBitOps!dchar); 3087 3088 assert(!hasBitOps!(SafeInt!(int, No.bitOps))); 3089 assert(!hasBitOps!(SmartInt!(int, No.bitOps))); 3090 assert(!hasBitOps!float); 3091 } 3092 3093 /// Aliases to the basic scalar type associated with `T`, assuming either: 3094 /// $(UL 3095 /// $(LI `isScalarType!T`, or) 3096 /// $(LI `isCheckedInt!T`) 3097 /// ) 3098 /// Otherwise, `BasicScalar` aliases to `void`. 3099 template BasicScalar(T) 3100 { 3101 static if (isScalarType!T) 3102 alias BasicScalar = Unqual!T; 3103 else static if (isCheckedInt!T) 3104 alias BasicScalar = TemplateArgsOf!T[0]; 3105 else 3106 alias BasicScalar = void; 3107 } 3108 /// 3109 unittest 3110 { 3111 import checkedint.throws : SmartInt, SafeInt; // use IntFlagPolicy.throws 3112 3113 assert(is(BasicScalar!(SafeInt!int) == int)); 3114 assert(is(BasicScalar!(SmartInt!ushort) == ushort)); 3115 3116 assert(is(BasicScalar!int == int)); 3117 assert(is(BasicScalar!(const shared real) == real)); 3118 } 3119 3120 // internal ///////////////////////////////////////////////// 3121 private 3122 { 3123 enum N trueMin(N) = mostNegative!N; 3124 template trueMax(N) 3125 if (isScalarType!N) 3126 { 3127 static if (is(Unqual!N == dchar)) 3128 enum N trueMax = ~cast(N)0; 3129 else 3130 enum N trueMax = N.max; 3131 } 3132 3133 template NumFromScal(N) 3134 if (isScalarType!N) 3135 { 3136 static if (isNumeric!N) 3137 alias NumFromScal = N; 3138 else static if (isSomeChar!N) 3139 alias NumFromScal = IntFromChar!N; 3140 else //if (isBoolean!N) 3141 alias NumFromScal = ubyte; 3142 } 3143 3144 /+pragma(inline, true) 3145 {+/ 3146 int bsfImpl(IntFlagPolicy policy, N)(const N num) 3147 if (isFixedPoint!N) 3148 { 3149 static if (isSigned!N) 3150 return bsfImpl!(policy, Unsigned!N)(num); 3151 else 3152 { 3153 static assert(N.sizeof <= ulong.sizeof); 3154 3155 int ret = void; 3156 if (num == 0) 3157 { 3158 IntFlag.undef.raise!policy(); 3159 ret = int.min; 3160 } 3161 else 3162 ret = bsf(num); 3163 3164 return ret; 3165 } 3166 } 3167 int bsrImpl(IntFlagPolicy policy, N)(const N num) 3168 if (isFixedPoint!N) 3169 { 3170 static if (isSigned!N) 3171 return bsrImpl!(policy, Unsigned!N)(num); 3172 else 3173 { 3174 static assert(N.sizeof <= ulong.sizeof); 3175 3176 int ret = void; 3177 if (num == 0) 3178 { 3179 IntFlag.undef.raise!policy(); 3180 ret = int.min; 3181 } 3182 else 3183 ret = bsr(num); 3184 3185 return ret; 3186 } 3187 } 3188 3189 auto byPow2Impl(string op, N, M)(const N left, const M exp) pure nothrow @nogc 3190 if (op.among!("*", "/", "%") && ((isFloatingPoint!N && isNumeric!M) || (isNumeric!N && isFloatingPoint!M))) 3191 { 3192 import std.math : exp2, isFinite, frexp, ldexp; 3193 3194 enum wantPrec = max(precision!N, precision!M); 3195 alias R = 3196 Select!(wantPrec <= precision!float, float, 3197 Select!(wantPrec <= precision!double, double, real)); 3198 3199 static if (isFloatingPoint!M) 3200 { 3201 R ret = void; 3202 3203 static if (op.among!("*", "/")) 3204 { 3205 if (left == 0 && exp.isFinite) 3206 ret = 0; 3207 else 3208 { 3209 R wexp = cast(R)exp; 3210 static if (op == "/") 3211 wexp = -wexp; 3212 3213 ret = cast(R)left * exp2(wexp); 3214 } 3215 } 3216 else 3217 { 3218 const p2 = exp2(cast(R)exp); 3219 ret = 3220 p2.isFinite? cast(R)left % p2 : 3221 (p2 > 0)? cast(R)left : 3222 (p2 < 0)? cast(R)0 : 3223 R.nan; 3224 } 3225 3226 return ret; 3227 } 3228 else 3229 { 3230 static if (op.among!("*", "/")) 3231 { 3232 int wexp = 3233 (exp > int.max)? int.max : 3234 (cast(long)exp < -int.max)? -int.max : cast(int)exp; 3235 static if (op == "/") 3236 wexp = -wexp; 3237 3238 return ldexp(cast(R)left, wexp); 3239 } 3240 else 3241 { 3242 int expL; 3243 real mantL = frexp(left, expL); 3244 3245 static if (!isSigned!M) 3246 const retL = expL <= exp; 3247 else 3248 const retL = (expL < 0) || (expL <= exp); 3249 3250 R ret = void; 3251 if (retL) 3252 ret = left; 3253 else 3254 { 3255 const expDiff = expL - exp; 3256 ret = (expDiff > N.mant_dig)? 3257 cast(R)0 : 3258 left - ldexp(floor(ldexp(mantissa, expDiff)), expL - expDiff); 3259 } 3260 3261 return ret; 3262 } 3263 } 3264 } 3265 auto byPow2Impl(string op, IntFlagPolicy policy, N, M)(const N left, const M exp) 3266 if (op.among!("*", "/", "%") && isIntegral!N && isIntegral!M) 3267 { 3268 alias R = Select!(op.among!("*", "/") != 0, Promoted!N, N); 3269 enum Unsigned!M maxSh = 8 * N.sizeof - 1; 3270 3271 R ret = void; 3272 static if (op.among!("*", "/")) 3273 { 3274 const rc = cast(R)left; 3275 const negE = exp < 0; 3276 const absE = cast(Unsigned!M)(negE? 3277 -exp : 3278 exp); 3279 const bigSh = (absE > maxSh); 3280 3281 R back = void; 3282 if ((op == "*")? negE : !negE) 3283 { 3284 if (bigSh) 3285 ret = 0; 3286 else 3287 { 3288 // ">>" rounds as floor(), but we want trunc() like "/" 3289 ret = (rc < 0)? 3290 -(-rc >>> absE) : 3291 rc >>> absE; 3292 } 3293 } 3294 else 3295 { 3296 if (bigSh) 3297 { 3298 ret = 0; 3299 back = 0; 3300 } 3301 else 3302 { 3303 ret = rc << absE; 3304 back = ret >> absE; 3305 } 3306 3307 if (back != rc) 3308 IntFlag.over.raise!policy(); 3309 } 3310 } 3311 else 3312 { 3313 if (exp & ~maxSh) 3314 ret = (exp < 0)? 0 : left; 3315 else 3316 { 3317 const mask = ~(~cast(N)0 << exp); 3318 ret = cast(R)(left < 0? 3319 -(-left & mask) : 3320 left & mask); 3321 } 3322 } 3323 3324 return ret; 3325 } 3326 /+}+/ 3327 3328 struct PowOut(B) 3329 { 3330 B num; 3331 IntFlag flag; 3332 } 3333 3334 // Minimize template bloat by using a common pow() implementation 3335 PowOut!B powImpl(B, E)(const B base, const E exp) 3336 if ((is(B == int) || is(B == uint) || is(B == long) || is(B == ulong)) && 3337 (is(E == long) || is(E == ulong))) 3338 { 3339 static if (__VERSION__ >= 2068) pragma(inline, false); 3340 PowOut!B ret; 3341 3342 static if (isSigned!B) 3343 { 3344 alias cmul = muls; 3345 const smallB = (1 >= base && base >= -1); 3346 } 3347 else 3348 { 3349 alias cmul = mulu; 3350 const smallB = (base <= 1); 3351 } 3352 3353 if (smallB) 3354 { 3355 if (base == 0) 3356 { 3357 static if (isSigned!E) 3358 { 3359 if (exp < 0) 3360 ret.flag = IntFlag.div0; 3361 } 3362 3363 ret.num = (exp == 0); 3364 } 3365 else 3366 ret.num = (exp & 0x1)? base : 1; 3367 3368 return ret; 3369 } 3370 if (exp <= 0) 3371 { 3372 ret.num = (exp == 0); 3373 return ret; 3374 } 3375 3376 ret.num = 1; 3377 if (exp <= precision!B) 3378 { 3379 B b = base; 3380 int e = cast(int)exp; 3381 if (e & 0x1) 3382 ret.num = b; 3383 e >>>= 1; 3384 3385 bool over = false; 3386 while (e != 0) 3387 { 3388 b = cmul(b, b, over); 3389 if (e & 0x1) 3390 ret.num = cmul(ret.num, b, over); 3391 3392 e >>>= 1; 3393 } 3394 3395 if (!over) 3396 return ret; 3397 } 3398 3399 ret.flag = (base < 0 && (exp & 0x1))? 3400 IntFlag.negOver : 3401 IntFlag.posOver; 3402 return ret; 3403 } 3404 }