8

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.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Core Xii
  • 6,270
  • 4
  • 31
  • 42

2 Answers2

10

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

Jonathan M Davis
  • 37,181
  • 17
  • 72
  • 102
  • How about adding intrinsics like `add`, which return the overflow bit too? – user541686 Nov 25 '12 at 04:16
  • @Mehrdad There may very well be some intrinsics that give you extra functionality related to overflow, but I don't know anything about them. That's way lower level than I ever deal with, and I don't know how many of them are guaranteed to be there on all architectures. – Jonathan M Davis Nov 25 '12 at 04:30
  • The example code is just a mock to illustrate my question. Replace `ubyte` with `uint` at your leisure. – Core Xii Nov 25 '12 at 13:59
7

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.

Adam D. Ruppe
  • 25,382
  • 4
  • 41
  • 60
  • `jo` - *jump overflow*? This looks like exactly what I needed. – Core Xii Nov 26 '12 at 06:49
  • 4
    Yes... though note that overflow doesn't really happen on unsigneds. Consider the binary representation of -1: 11111111 (using ubyte here for brevity). That's the same as 255. But the processor doesn't know or care if the type is signed or not, it just sees the number. 255 + 1 == -1 + 1 == 0. So that will set the carry flag, but not the overflow flag. So if you're specifically using unsigned numbers, you'll want the jc instruction - jump if carry - instead. – Adam D. Ruppe Nov 26 '12 at 13:18
  • 2
    Another potential gotcha is the increment instruction does *not* set the carry flag.... and the compiler will optimize a +=1 into inc a if the one is known at compile time. So you're ok for a+b, since those are runtime, but a+=1 or a++ might give a surprising pass on the asm check. – Adam D. Ruppe Nov 26 '12 at 13:19
  • Is there any guarantee that placing this after the relevant instruction in C code will actually place it after that instruction in assembly code? I'd fear that an optimizing compiler might reorder instructions in such a way that the flags at the point of the check originate from some other operation than the intended one. – MvG Jan 31 '13 at 05:58
  • We could put an asm{} before it... D guarantees that asm blocks won't be reordered relative to each other. Doing it in C would probably need some the volatile attribute or something. – Adam D. Ruppe Jan 31 '13 at 13:24
  • In C this would be totally broken: nothing forces the optimizer to use `add` instead of `lea`, so the state of OF isn't a visible side-effect, and isn't a valid input to an asm statement. Does D really guarantee this is safe? – Peter Cordes Apr 05 '21 at 08:41
  • 1
    @PeterCordes indeed it doesn't guarantee it. If you use dmd without the -O flag it does work in practice, but with gdc and ldc the optimizer is free to switch instructions due to pretty obscure scheduling tricks even without -O options. In 2014, the core.checkedint module was added: http://dpldocs.info/experimental-docs/core.checkedint.html which checks the individual operations in a platform-independent, optimizer-friendly way. Then std.experimental.checkedint (which has been labeled experimental for years, ugh) builds user-friendliness on top of that too. – Adam D. Ruppe Apr 05 '21 at 17:03
  • Sounds like this old answer should have a big fat warning at the top, then. Or change it to do the `add` or whatever inside the `asm` statement, so you know you can get FLAGS from it. Also related: [Detecting signed overflow in C/C++](https://stackoverflow.com/q/3944505) / [Read flag register from C program](https://stackoverflow.com/a/56237860). And https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins.html if any D compiler supports those. – Peter Cordes Apr 05 '21 at 17:31
  • Yeah, the first link there is basically what the core.checkedint implementation does. The gcc things are available on gdc of course, but using the runtime lib's functions are probably best since it can be a little more compiler-independent that way. added a edit note btw – Adam D. Ruppe Apr 05 '21 at 17:52