How to detect integer overflow in D? (check carry flag?)
Original example:
ubyte a = 100;
ubyte b = 200;
ubyte c = a + b;
// c can't represent 300; how to detect the overflow now?
Revised example:
uint a = 2_000_000_000;
uint b = 3_000_000_000;
uint c = a + b;
// c can't represent 5_000_000_000; how to detect the overflow now?
Also with multiplication and pre/post-increment.
You need to check reversed before the multiplication. If it is bigger than INT_MAX/10, then it will overflow. And maybe you need other checks too. The working of your code assumes the value being reversed AND the reversed value can both be represented in a 32-bit integral type.
Following are the three main techniques for detecting unintended integer overflow: Precondition testing. Check the inputs to each arithmetic operator to ensure that overflow cannot occur. Throw an ArithmeticException when the operation would overflow if it were performed; otherwise, perform the operation.
In languages where integer overflow can occur, you can reduce its likelihood by using larger integer types, like Java's long or C's long long int. If you need to store something even bigger, there are libraries built to handle arbitrarily large numbers.
For starters, the code that you gave won't even compile, because all integer math with sizes smaller than int
is done with int
. So the result of a + b
is int
, and int
will not implicitly convert to ubyte
, because that's a narrowing conversion. If you want to assign it to c
, then you'll need to cast it.
ubyte c = cast(ubyte)(a + b);
Now, that's obviously an unchecked conversion, and it will happily stuff 44
into c
(since that's the result of the cast given the values of 100
and 200
). If you want a checked conversion, then use std.conv.to
:
ubyte c = to!ubyte(a + b);
That will throw a ConvOverflowException
(which is a subclass of ConvException
), because the result won't fit in the requested type.
If you want to do the cast yourself and then check whether there was overflow, then you're in essentially the same boat as C/C++, and there are no carry flags or anything of the sort. Maybe that sort of thing exists if you check with assembly code. I don't know. But the language certainly doesn't provide anything like that. std.conv.to
figures it out by checking the result and seeing if it is too large or too small (depending on the sign and types of the argument).
Update in 2021: this technique only works if you use dmd without optimizations (and even then probably better to do other approaches). To work in more cases, you can do all the operations in inline assembly (which btw is likely to break optimizers), or use the core.checkedint
operations which have a ref bool overflowed
argument you can check at the end of the operation (which is also inline-friendly btw for the optimizer again).
Original post follows:
You can check it pretty easily with some inline assembly:
asm { jo overflowed; } // for checking signed types
// or
asm { jc overflowed; } // use this for checking unsigned types
/* continue going */
return;
overflowed:
/* do whatever to handle it */
Note: you probably can't put this in a function because calling the function can reset the flag. You'll want to put it inline right after the operation you're interested in.
It is possible to make a type that uses operator overloading to throw on overflow: http://arsdnet.net/dcode/ranged.d is one example. Or I think the module is std.bigint in the standard library that avoids overflow by offering an arbitrarily large integer type.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With