Ich habe ein Programm in C++ geschrieben, um alle Lösungen von zu finden einB = Cwo ein, B und C zusammen alle Ziffern 0-9 genau einmal verwenden. Das Programm hat Werte von durchlaufen ein und Bund es wurde jedes Mal eine Ziffernzählroutine ausgeführt ein, B und einB um zu prüfen, ob die Ziffernbedingung erfüllt ist.
Es können jedoch falsche Lösungen erzeugt werden, wenn einB überschreitet die ganzzahlige Grenze. Am Ende habe ich dies mit Code wie folgt überprüft:
unsigned long b, c, c_test;
...
c_test=c*b; // Possible overflow
if (c_test/b != c) {/* There has been an overflow*/}
else c=c_test; // No overflow
Gibt es eine bessere Möglichkeit, auf Überlauf zu testen? Ich weiß, dass einige Chips ein internes Flag haben, das gesetzt wird, wenn ein Überlauf auftritt, aber ich habe noch nie gesehen, dass darauf über C oder C++ zugegriffen wurde.
Hüten Sie sich davor unterzeichnet int
Überlauf ist ein undefiniertes Verhalten in C und C++, und daher müssen Sie es erkennen, ohne es tatsächlich zu verursachen. Informationen zum Signed-Int-Überlauf vor dem Hinzufügen finden Sie unter Vorzeichenüberlauf in C/C++ erkennen.
Starting with C23, the standard header <stdckdint.h>
provides the following three function-like macros:
bool ckd_add(type1 *result, type2 a, type3 b);
bool ckd_sub(type1 *result, type2 a, type3 b);
bool ckd_mul(type1 *result, type2 a, type3 b);
where type1
, type2
and type3
are any integer type. These functions respectively add, subtract or multiply a and b with arbitrary precision and store the result in *result
. If the result cannot be represented exactly by type1
, the function returns true
(“calculation has overflowed”). (Arbitrary precision is an illusion; the calculations are very fast and almost all hardware available since the early 1990s can do it in just one or two instructions.)
Rewriting OP’s example:
unsigned long b, c, c_test;
// ...
if (ckd_mul(&c_test, c, b))
{
// returned non-zero: there has been an overflow
}
else
{
c = c_test; // returned 0: no overflow
}
c_test contains the potentially-overflowed result of the multiplication in all cases.
Long before C23, GCC 5+ and Clang 3.8+ offer built-ins that work the same way, except that the result pointer is passed last instead of first: __builtin_add_overflow
, __builtin_sub_overflow
and __builtin_mul_overflow
. These also work on types smaller than int
.
unsigned long b, c, c_test;
// ...
if (__builtin_mul_overflow(c, b, &c_test))
{
// returned non-zero: there has been an overflow
}
else
{
c = c_test; // returned 0: no overflow
}
Clang 3.4+ introduced arithmetic-overflow builtins with fixed types, but they are much less flexible and Clang 3.8 has been available for a long time now. Look for __builtin_umull_overflow
if you need to use this despite the more convenient newer alternative.
Visual Studio‘s cl.exe doesn’t have direct equivalents. For unsigned additions and subtractions, including <intrin.h>
will allow you to use addcarry_uNN
and subborrow_uNN
(where NN is the number of bits, like addcarry_u8
or subborrow_u64
). Their signature is a bit obtuse:
unsigned char _addcarry_u32(unsigned char c_in, unsigned int src1, unsigned int src2, unsigned int *sum);
unsigned char _subborrow_u32(unsigned char b_in, unsigned int src1, unsigned int src2, unsigned int *diff);
c_in
/b_in
is the carry/borrow flag on input, and the return value is the carry/borrow on output. It does not appear to have equivalents for signed operations or multiplications.
Otherwise, Clang for Windows is now production-ready (good enough for Chrome), so that could be an option, too.
Some compilers provide access to the integer overflow flag in the CPU which you could then test but this isn’t standard.
You could also test for the possibility of overflow before you perform the multiplication:
if ( b > ULONG_MAX / a ) // a * b would overflow
Warning: GCC can optimize away an overflow check when compiling with -O2
.
The option -Wall
will give you a warning in some cases like
if (a + b < a) { /* Deal with overflow */ }
but not in this example:
b = abs(a);
if (b < 0) { /* Deal with overflow */ }
The only safe way is to check for overflow before it occurs, as described in the CERT paper, and this would be incredibly tedious to use systematically.
Compiling with -fwrapv
solves the problem, but disables some optimizations.
We desperately need a better solution. I think the compiler should issue a warning by default when making an optimization that relies on overflow not occurring. The present situation allows the compiler to optimize away an overflow check, which is unacceptable in my opinion.
Clang now supports dynamic overflow checks for both signed and unsigned integers. See the -fsanitize=integer switch. For now, it is the only C++ compiler with fully supported dynamic overflow checking for debug purposes.
Hilfreiche Informationen zu diesem Thema: Kapitel 5 von “Secure Coding in C and C++” von Seacord – http://www.informit.com/content/images/0321335724/samplechapter/seacord_ch05.pdf SafeInt-Klassen für C++ – http://blogs.msdn.com/david_leblanc/archive/2008/09/30/safeint-3-on-codeplex.aspx – http://www.codeplex.com/SafeInt IntSafe-Bibliothek für C: – [blogs.msdn.com/michael_howard/archiv
Oct 13, 2008 at 23:28
Seacord’s Secure Coding is a great resource, but don’t use IntegerLib. See blog.regehr.org/archives/593.
Sep 26, 2011 at 0:53
The gcc compiler option
-ftrapv
will cause it to generate a SIGABRT on (signed) integer overflow. See here.Oct 17, 2012 at 20:12
It does not answer the overflow question, but another way to come at the problem would be to use a BigNum library like GMP to guarantee you always have enough precision. You will not have to worry about overflow if you allocate enough digits up front.
Sep 14, 2013 at 2:00
The information given by @HeadGeek in his answer is pretty much what I would say as well. However, with one addition. The way you are detecting overflown for a multiplication now is probably the fastest. On ARM as I’ve commented in HeadGeek’s answer you can use the
clz
instruction or the__clz(unsigned)
function to determine the rank of the number (where its highest bit is). Since I’m unsure if this is available on x86 or x64 I will assume it is not and say that finding the most significant bit will take at worstlog(sizeof(int)*8)
instructions.Oct 4, 2013 at 0:10