Bagaimana cara saya mendeteksi kelebihan integer bilangan bulat yang tidak ditandatangani?

618

Saya sedang menulis sebuah program dalam C ++ untuk menemukan semua solusi dari a b = c , di mana sebuah , b dan c bersama-sama menggunakan semua angka 0-9 tepat sekali. Program dilingkarkan di atas nilai-nilai yang dan b , dan berlari digit penghitungan rutin setiap kali di sebuah , b dan a b untuk memeriksa apakah kondisi digit puas.

Namun, solusi palsu dapat dihasilkan ketika sebuah b meluap batas integer. Saya akhirnya memeriksa ini menggunakan kode seperti:

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

Apakah ada cara yang lebih baik untuk menguji luapan? Saya tahu bahwa beberapa chip memiliki flag internal yang diatur ketika terjadi overflow, tetapi saya belum pernah melihatnya diakses melalui C atau C ++.


Waspadalah bahwa overflow yang ditandatangani int adalah perilaku tidak terdefinisi dalam C dan C ++ , dan karenanya Anda harus mendeteksinya tanpa benar-benar menyebabkannya. Untuk limpahan int yang ditandatangani sebelum penambahan, lihat Mendeteksi limpahan yang ditandatangani di C / C ++ .

Chris Johnson
sumber
21
Informasi yang mungkin berguna mengenai hal ini: Bab 5 dari "Secure Coding di C dan C ++" oleh Seacord - http://www.informit.com/content/images/0321335724/samplechapter/seacord_ch05.pdf Kelas SafeInt untuk C ++ - http : //blogs.msdn.com/david_leblanc/archive/2008/09/30/safeint-3-on-codeplex.aspx - http://www.codeplex.com/SafeInt IntSafe library untuk C: - [ blogs.msdn .com / michael_howard / archiv
Michael Burr
3
Secure Coding Seacord adalah sumber yang bagus, tetapi jangan gunakan IntegerLib. Lihat blog.regehr.org/archives/593 .
jww
32
Opsi kompiler gcc -ftrapvakan membuatnya menghasilkan SIGABRT pada overflow integer yang ditandatangani. Lihat di sini .
nibot
1
Itu tidak menjawab pertanyaan overflow, tetapi cara lain untuk mengatasi masalah ini adalah dengan menggunakan perpustakaan BigNum seperti GMP untuk menjamin Anda selalu memiliki cukup presisi. Anda tidak perlu khawatir meluap jika Anda mengalokasikan angka yang cukup di depan.
wrdieter
1
Informasi yang diberikan oleh @HeadGeek dalam jawabannya cukup banyak apa yang akan saya katakan juga. Namun, dengan satu tambahan. Cara Anda mendeteksi overflown untuk multiplikasi sekarang mungkin yang tercepat. Pada ARM seperti yang saya komentari dalam jawaban HeadGeek, Anda dapat menggunakan clzinstruksi atau __clz(unsigned)fungsi untuk menentukan peringkat nomor (di mana bit tertinggi adalah). Karena saya tidak yakin apakah ini tersedia di x86 atau x64, saya akan menganggapnya tidak dan mengatakan bahwa menemukan bit yang paling signifikan akan mengambil log(sizeof(int)*8)instruksi terburuk .
nonsensickle

Jawaban:

229

Saya melihat Anda menggunakan bilangan bulat yang tidak ditandatangani. Menurut definisi, di C (saya tidak tahu tentang C ++), aritmatika yang tidak ditandatangani tidak melimpah ... jadi, setidaknya untuk C, poin Anda bisa diperdebatkan :)

Dengan bilangan bulat yang ditandatangani, setelah terjadi overflow, perilaku tidak terdefinisi (UB) telah terjadi dan program Anda dapat melakukan apa saja (misalnya: render tes tidak meyakinkan). 

#include <limits.h>

int a = <something>;
int x = <something>;
a += x;              /* UB */
if (a < 0) {         /* Unreliable test */
  /* ... */
}

Untuk membuat program yang sesuai, Anda perlu menguji untuk overflow sebelum membuat kata overflow. Metode ini dapat digunakan dengan bilangan bulat tidak bertanda juga:

// For addition
#include <limits.h>

int a = <something>;
int x = <something>;
if ((x > 0) && (a > INT_MAX - x)) /* `a + x` would overflow */;
if ((x < 0) && (a < INT_MIN - x)) /* `a + x` would underflow */;

// For subtraction
#include <limits.h>
int a = <something>;
int x = <something>;
if ((x < 0) && (a > INT_MAX + x)) /* `a - x` would overflow */;
if ((x > 0) && (a < INT_MIN + x)) /* `a - x` would underflow */;

// For multiplication
#include <limits.h>

int a = <something>;
int x = <something>;
// There may be a need to check for -1 for two's complement machines.
// If one number is -1 and another is INT_MIN, multiplying them we get abs(INT_MIN) which is 1 higher than INT_MAX
if ((a == -1) && (x == INT_MIN)) /* `a * x` can overflow */
if ((x == -1) && (a == INT_MIN)) /* `a * x` (or `a / x`) can overflow */
// general case
if (a > INT_MAX / x) /* `a * x` would overflow */;
if ((a < INT_MIN / x)) /* `a * x` would underflow */;

Untuk pembagian (kecuali untuk INT_MINdan -1kasus khusus), tidak ada kemungkinan untuk beralih INT_MINatau INT_MAX.

pmg
sumber
97
Bilangan bulat yang tidak ditandatangani tidak sepenuhnya melimpah di C ++ (ISO / IEC 14882: 2003 3.9.1.4). Penggunaan saya 'overflow' dalam pertanyaan adalah makna yang lebih sehari-hari, dimaksudkan untuk menyertakan pembungkus yang jelas dari tipe yang tidak ditandatangani, karena saya tertarik pada int unsigned yang mewakili bilangan bulat positif matematika, bukan bilangan bulat positif mod 2 ^ 32 (atau 2 ^ 64). Perbedaan antara overflow sebagai penyimpangan dari perilaku integer matematis berukuran tak terbatas, dan overflow sebagai perilaku yang tidak terdefinisi dalam bahasa tampaknya jarang dibuat eksplisit.
Chris Johnson
15
Tes itu tidak perlu x >= 0- x > 0akan cukup (jika x == 0, maka x + atidak bisa meluap karena alasan yang jelas).
caf
2
@ pmg, apakah ada kutipan pendukung dari standar?
Pacerier
5
Saya suka pendekatan ini ... Namun, berhati-hatilah: deteksi multiplikasi melimpah mengasumsikan positif x. Untuk x == 0, itu mengarah ke membagi dengan deteksi nol, dan untuk x negatif, selalu salah mendeteksi overflow.
Franz D.
4
if ((a < INT_MIN / x))Tes sudah terlambat. Sebuah if (x == -1) tes diperlukan pertama.
chux
164

Ada adalah cara untuk menentukan apakah operasi cenderung meluap, menggunakan posisi yang paling signifikan satu-bit dalam operan dan pengetahuan biner-matematika dasar sedikit.

Sebagai tambahan, setiap dua operan akan menghasilkan (paling banyak) satu bit lebih banyak dari operand tertinggi satu bit terbesar. Sebagai contoh:

bool addition_is_safe(uint32_t a, uint32_t b) {
    size_t a_bits=highestOneBitPosition(a), b_bits=highestOneBitPosition(b);
    return (a_bits<32 && b_bits<32);
}

Untuk penggandaan, setiap dua operan akan menghasilkan (paling banyak) jumlah bit dari operan. Sebagai contoh:

bool multiplication_is_safe(uint32_t a, uint32_t b) {
    size_t a_bits=highestOneBitPosition(a), b_bits=highestOneBitPosition(b);
    return (a_bits+b_bits<=32);
}

Demikian pula, Anda dapat memperkirakan ukuran maksimum hasil ahingga kekuatan bseperti ini:

bool exponentiation_is_safe(uint32_t a, uint32_t b) {
    size_t a_bits=highestOneBitPosition(a);
    return (a_bits*b<=32);
}

(Pengganti jumlah bit untuk target integer Anda, tentu saja.)

Saya tidak yakin dengan cara tercepat untuk menentukan posisi bit tertinggi dalam sebuah angka, berikut ini adalah metode brute-force:

size_t highestOneBitPosition(uint32_t a) {
    size_t bits=0;
    while (a!=0) {
        ++bits;
        a>>=1;
    };
    return bits;
}

Itu tidak sempurna, tetapi itu akan memberi Anda ide yang baik apakah ada dua angka yang bisa meluap sebelum Anda melakukan operasi. Saya tidak tahu apakah itu akan lebih cepat daripada hanya memeriksa hasilnya seperti yang Anda sarankan, karena loop dalam highestOneBitPositionfungsi, tetapi mungkin (terutama jika Anda tahu berapa banyak bit di operan sebelumnya).

Kepala Geek
sumber
98
dan tentu saja Anda dapat mengganti nama tertinggiOneBitPosisi untuk login :)
Oliver Hallam
37
Ya, ini adalah operasi yang sama dengan log2, tetapi itu tidak selalu jelas bagi seseorang yang tidak memiliki latar belakang matematika.
Kepala Geek
48
Bukankah algoritma ini meremehkan jawaban aman? 2 ^ 31 + 0 akan mendeteksi tidak aman karena tertinggiOneBitPosition (2 ^ 31) = 32. (2 ^ 32 - 1) * 1 akan mendeteksi tidak aman sejak 32 + 1> 32. 1 ^ 100 akan mendeteksi tidak aman sejak 1 * 100 > 32.
clahey
19
menurut Anda multiplication_is_safe 0x8000 * 0x10000akan melimpah (posisi bit adalah 16 + 17 = 33 yang > 32 ), meskipun tidak karena 0x8000 * 0x10000 = 0x80000000yang jelas masih cocok dengan int 32 bit yang tidak ditandatangani. Ini hanya satu dari banyak contoh yang kode ini tidak berfungsi. 0x8000 * 0x10001, ...
Michi
13
@ GT_mh: Maksud Anda? Seperti yang saya katakan, itu tidak sempurna; itu aturan-of-thumb yang definitif akan mengatakan ketika sesuatu adalah aman, tetapi tidak ada cara untuk menentukan apakah setiap perhitungan akan baik-baik saja tanpa melakukan perhitungan penuh. 0x8000 * 0x10000bukan "aman," menurut definisi ini, meskipun ternyata baik-baik saja.
Kepala Geek
147

Dentang 3.4+ dan GCC 5+ menawarkan builtin aritmatika yang diperiksa. Mereka menawarkan solusi yang sangat cepat untuk masalah ini, terutama jika dibandingkan dengan pemeriksaan keamanan pengujian bit.

Sebagai contoh dalam pertanyaan OP, itu akan berfungsi seperti ini:

unsigned long b, c, c_test;
if (__builtin_umull_overflow(b, c, &c_test))
{
    // Returned non-zero: there has been an overflow
}
else
{
    // Return zero: there hasn't been an overflow
}

Dokumentasi Dentang tidak menentukan apakah c_testberisi hasil meluap jika terjadi overflow, tetapi dokumentasi GCC mengatakan itu. Mengingat bahwa keduanya suka __builtin-compatible, mungkin aman untuk berasumsi bahwa ini adalah cara kerja dentang juga.

Ada __builtinuntuk setiap operasi aritmatika yang dapat meluap (penjumlahan, pengurangan, penggandaan), dengan varian yang ditandatangani dan tidak ditandatangani, untuk ukuran int, ukuran panjang, dan ukuran panjang yang panjang. Sintaks untuk namanya adalah __builtin_[us](operation)(l?l?)_overflow:

  • uuntuk tidak ditandatangani atau suntuk ditandatangani ;
  • operasi adalah salah satu add, subatau mul;
  • no lsuffix berarti operan adalah ints; satu lberarti long; dua ls berarti long long.

Jadi untuk penambahan bilangan bulat panjang bertanda tangan yang dicentang, itu akan menjadi __builtin_saddl_overflow. Daftar lengkap dapat ditemukan di halaman dokumentasi Dentang .

GCC 5 + dan dentang 3.8+ juga menawarkan builtin generik yang bekerja tanpa menentukan jenis nilai-nilai: __builtin_add_overflow, __builtin_sub_overflowdan __builtin_mul_overflow. Ini juga bekerja pada jenis yang lebih kecil dari int.

Builtin lebih rendah ke apa yang terbaik untuk platform. Pada x86, mereka memeriksa carry, overflow, dan tanda bendera.

Cl.exe Visual Studio tidak memiliki padanan langsung. Untuk penambahan dan pengurangan yang tidak ditandatangani, termasuk <intrin.h>akan memungkinkan Anda untuk menggunakan addcarry_uNNdan subborrow_uNN(di mana NN adalah jumlah bit, suka addcarry_u8atau subborrow_u64). Tanda tangan mereka agak tumpul:

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_inadalah flag carry / meminjam pada input, dan nilai kembali adalah carry / pinjam pada output. Tampaknya tidak memiliki padanan untuk operasi atau multiplikasi yang ditandatangani.

Kalau tidak, Dentang untuk Windows sekarang sudah siap produksi (cukup baik untuk Chrome), sehingga bisa menjadi pilihan juga.

zneak
sumber
__builtin_sub_overflowpasti tidak dalam dentang 3.4.
Richard Cook
2
@RichardCook, butuh beberapa waktu tetapi Clang memiliki builtin generik pada versi 3.9.
zneak
@ Sabre, saya tidak berpikir ada.
zneak
4
Menurut dokumen , __builtin_add_overflowdan teman-teman harus sudah tersedia di Dentang 3.8.
Lekensteyn
2
Terima kasih. Ini sangat bagus. Adakah yang tahu apa fungsi yang sesuai untuk visual c ++? Tampaknya tidak dapat menemukan mereka.
Mudit Jain
53

Beberapa kompiler menyediakan akses ke flag integer overflow di CPU yang dapat Anda uji tetapi ini bukan standar.

Anda juga bisa menguji kemungkinan overflow sebelum Anda melakukan perkalian:

if ( b > ULONG_MAX / a ) // a * b would overflow
Robert Gamble
sumber
11
... atau gunakan numeric_limits <TYPE> :: maks ()
Jonas Gulle
20
Jangan lupa untuk menangani a = 0 - pembagian divisi kalau begitu.
Thelema
16
@Helema: "Jangan lupa untuk menangani a = 0" - dan INT_MIN / -1.
jww
1
Bagaimana jika b == ULONG_MAX / a? Maka masih bisa muat, mengingat yang amembelah ULONG_MAXtanpa residual.
babi
Lucu bahwa, menurut kinerja, perkalian agak cepat dibandingkan dengan divisi dan Anda menambahkan divisi untuk setiap perkalian. Ini tidak terdengar seperti itu solusi.
DrumM
40

Peringatan: GCC dapat mengoptimalkan jauh pemeriksaan luapan saat dikompilasi -O2. Opsi -Wallakan memberi Anda peringatan dalam beberapa kasus seperti

if (a + b < a) { /* Deal with overflow */ }

tetapi tidak dalam contoh ini:

b = abs(a);
if (b < 0) { /* Deal with overflow */ }

Satu-satunya cara yang aman adalah memeriksa luapan sebelum terjadi, seperti yang dijelaskan dalam makalah CERT , dan ini akan sangat membosankan untuk digunakan secara sistematis.

Kompilasi dengan -fwrapvmemecahkan masalah, tetapi menonaktifkan beberapa optimasi.

Kami sangat membutuhkan solusi yang lebih baik. Saya pikir kompiler harus mengeluarkan peringatan secara default ketika membuat optimasi yang mengandalkan overflow tidak terjadi. Situasi saat ini memungkinkan kompiler untuk mengoptimalkan cek overflow, yang menurut saya tidak dapat diterima.

A Fog
sumber
8
Perhatikan bahwa kompiler hanya dapat melakukan ini dengan tipe integer yang ditandatangani ; overflow sepenuhnya ditentukan untuk tipe integer yang tidak ditandai. Tetap saja, ya, ini jebakan yang cukup berbahaya!
SamB
1
"Saya pikir kompiler harus mengeluarkan peringatan secara default ketika membuat optimasi yang mengandalkan overflow tidak terjadi." - jadi for(int k = 0; k < 5; k++) {...}haruskah memunculkan peringatan?
user253751
2
@immibis: Kenapa harus begitu? Nilai-nilai kdapat dengan mudah ditentukan pada waktu kompilasi. Kompiler tidak harus membuat asumsi.
MikeMB
2
@immibis: Mengutip hal di atas: "Saya pikir kompiler harus mengeluarkan peringatan secara default ketika membuat optimasi yang bergantung pada overflow tidak terjadi."
MikeMB
1
@MikeMB Optimasi di mana kompiler tidak repot untuk memeriksa yang nkurang dari 32, sebelum memancarkan instruksi shift yang hanya menggunakan 5 bit yang lebih rendah n?
user253751
30

Dentang sekarang mendukung pemeriksaan overflow dinamis untuk bilangan bulat yang ditandatangani dan tidak ditandatangani. Lihat sakelar -fsanitize = integer . Untuk saat ini, ini adalah satu-satunya kompiler C ++ dengan dynamic overflow yang didukung sepenuhnya untuk keperluan debug.

ZAB
sumber
25

Saya melihat bahwa banyak orang menjawab pertanyaan tentang meluap, tetapi saya ingin mengatasi masalah aslinya. Dia mengatakan masalahnya adalah menemukan b = c sehingga semua digit digunakan tanpa mengulangi. Oke, bukan itu yang dia tanyakan dalam posting ini, tapi saya masih berpikir bahwa itu perlu untuk mempelajari batas atas masalah dan menyimpulkan bahwa dia tidak perlu menghitung atau mendeteksi luapan (catatan: Saya tidak cakap dalam matematika jadi saya melakukan langkah demi langkah ini, tetapi hasil akhirnya sangat sederhana sehingga ini mungkin memiliki rumus sederhana).

Poin utama adalah bahwa batas atas yang dibutuhkan oleh masalah untuk a, b atau c adalah 98.765.432. Pokoknya, mulailah dengan memecah masalah di bagian sepele dan non sepele:

  • x 0 == 1 (semua permutasi 9, 8, 7, 6, 5, 4, 3, 2 adalah solusi)
  • x 1 == x (tidak ada solusi yang mungkin)
  • 0 b == 0 (tidak ada solusi yang mungkin)
  • 1 b == 1 (tidak ada solusi yang mungkin)
  • a b , a> 1, b> 1 (tidak sepele)

Sekarang kita hanya perlu menunjukkan bahwa tidak ada solusi lain yang mungkin dan hanya permutasi yang valid (dan kemudian kode untuk mencetaknya adalah sepele). Kami kembali ke batas atas. Sebenarnya batas atas adalah c ≤ 98.765.432. Ini adalah batas atas karena ini adalah angka terbesar dengan 8 digit (total 10 digit minus 1 untuk masing-masing a dan b). Batas atas ini hanya untuk c karena batas untuk a dan b harus jauh lebih rendah karena pertumbuhan eksponensial, seperti yang dapat kita hitung, bervariasi b dari 2 hingga batas atas:

    9938.08^2 == 98765432
    462.241^3 == 98765432
    99.6899^4 == 98765432
    39.7119^5 == 98765432
    21.4998^6 == 98765432
    13.8703^7 == 98765432
    9.98448^8 == 98765432
    7.73196^9 == 98765432
    6.30174^10 == 98765432
    5.33068^11 == 98765432
    4.63679^12 == 98765432
    4.12069^13 == 98765432
    3.72429^14 == 98765432
    3.41172^15 == 98765432
    3.15982^16 == 98765432
    2.95305^17 == 98765432
    2.78064^18 == 98765432
    2.63493^19 == 98765432
    2.51033^20 == 98765432
    2.40268^21 == 98765432
    2.30883^22 == 98765432
    2.22634^23 == 98765432
    2.15332^24 == 98765432
    2.08826^25 == 98765432
    2.02995^26 == 98765432
    1.97741^27 == 98765432

Perhatikan, misalnya baris terakhir: dikatakan bahwa 1,97 ^ 27 ~ 98M. Jadi, misalnya, 1 ^ 27 == 1 dan 2 ^ 27 == 134.217.728 dan itu bukan solusi karena memiliki 9 digit (2> 1,97 sehingga sebenarnya lebih besar dari apa yang harus diuji). Seperti dapat dilihat, kombinasi yang tersedia untuk pengujian a dan b sangat kecil. Untuk b == 14, kita perlu mencoba 2 dan 3. Untuk b == 3, kita mulai dari 2 dan berhenti di 462. Semua hasil yang diberikan kurang dari ~ 98M.

Sekarang coba saja semua kombinasi di atas dan cari yang tidak mengulangi angka apa pun:

    ['0', '2', '4', '5', '6', '7', '8'] 84^2 = 7056
    ['1', '2', '3', '4', '5', '8', '9'] 59^2 = 3481
    ['0', '1', '2', '3', '4', '5', '8', '9'] 59^2 = 3481 (+leading zero)
    ['1', '2', '3', '5', '8'] 8^3 = 512
    ['0', '1', '2', '3', '5', '8'] 8^3 = 512 (+leading zero)
    ['1', '2', '4', '6'] 4^2 = 16
    ['0', '1', '2', '4', '6'] 4^2 = 16 (+leading zero)
    ['1', '2', '4', '6'] 2^4 = 16
    ['0', '1', '2', '4', '6'] 2^4 = 16 (+leading zero)
    ['1', '2', '8', '9'] 9^2 = 81
    ['0', '1', '2', '8', '9'] 9^2 = 81 (+leading zero)
    ['1', '3', '4', '8'] 3^4 = 81
    ['0', '1', '3', '4', '8'] 3^4 = 81 (+leading zero)
    ['2', '3', '6', '7', '9'] 3^6 = 729
    ['0', '2', '3', '6', '7', '9'] 3^6 = 729 (+leading zero)
    ['2', '3', '8'] 2^3 = 8
    ['0', '2', '3', '8'] 2^3 = 8 (+leading zero)
    ['2', '3', '9'] 3^2 = 9
    ['0', '2', '3', '9'] 3^2 = 9 (+leading zero)
    ['2', '4', '6', '8'] 8^2 = 64
    ['0', '2', '4', '6', '8'] 8^2 = 64 (+leading zero)
    ['2', '4', '7', '9'] 7^2 = 49
    ['0', '2', '4', '7', '9'] 7^2 = 49 (+leading zero)

Tak satu pun dari mereka cocok dengan masalah (yang juga bisa dilihat dengan tidak adanya '0', '1', ..., '9').

Contoh kode yang menyelesaikannya mengikuti. Perhatikan juga bahwa ini ditulis dengan Python, bukan karena membutuhkan bilangan bulat presisi yang sewenang-wenang (kode tidak menghitung apa pun yang lebih besar dari 98 juta), tetapi karena kami menemukan bahwa jumlah tes sangat kecil sehingga kami harus menggunakan bahasa tingkat tinggi untuk memanfaatkan wadah dan pustaka bawaannya (juga perhatikan: kode memiliki 28 baris).

    import math

    m = 98765432
    l = []
    for i in xrange(2, 98765432):
        inv = 1.0/i
        r = m**inv
        if (r < 2.0): break
        top = int(math.floor(r))
        assert(top <= m)

        for j in xrange(2, top+1):
            s = str(i) + str(j) + str(j**i)
            l.append((sorted(s), i, j, j**i))
            assert(j**i <= m)

    l.sort()
    for s, i, j, ji in l:
        assert(ji <= m)
        ss = sorted(set(s))
        if s == ss:
            print '%s %d^%d = %d' % (s, i, j, ji)

        # Try with non significant zero somewhere
        s = ['0'] + s
        ss = sorted(set(s))
        if s == ss:
            print '%s %d^%d = %d (+leading zero)' % (s, i, j, ji)
hdante
sumber
1
mengapa Anda tidak menggunakan 9.876.543.210 sebagai batas atas?
Tom Roggero
3
Karena 2 digit harus digunakan untuk sisi kiri persamaan.
hdante
2
Bukan berarti itu membuat perbedaan, tetapi batas atas sebenarnya dapat diambil sebagai 98765410 karena Anda telah menyatakan nilai pada LHS adalah> 1
Paul Childs
24

Berikut adalah solusi "non-portabel" untuk pertanyaan tersebut. Intel x86 dan x64 CPU memiliki apa yang disebut EFLAGS-register , yang diisi oleh prosesor setelah setiap operasi aritmatika integer. Saya akan melewatkan deskripsi terperinci di sini. Bendera yang relevan adalah Bendera "Overflow" (mask 0x800) dan "Carry" Flag (mask 0x1). Untuk menafsirkannya dengan benar, orang harus mempertimbangkan apakah operan bertipe ditandatangani atau tidak.

Berikut ini adalah cara praktis untuk memeriksa flag dari C / C ++. Kode berikut akan bekerja pada Visual Studio 2005 atau lebih baru (baik 32 dan 64 bit), serta pada GNU C / C ++ 64 bit.

#include <cstddef>
#if defined( _MSC_VER )
#include <intrin.h>
#endif

inline size_t query_intel_x86_eflags(const size_t query_bit_mask)
{
    #if defined( _MSC_VER )

        return __readeflags() & query_bit_mask;

    #elif defined( __GNUC__ )
        // This code will work only on 64-bit GNU-C machines.
        // Tested and does NOT work with Intel C++ 10.1!
        size_t eflags;
        __asm__ __volatile__(
            "pushfq \n\t"
            "pop %%rax\n\t"
            "movq %%rax, %0\n\t"
            :"=r"(eflags)
            :
            :"%rax"
            );
        return eflags & query_bit_mask;

    #else

        #pragma message("No inline assembly will work with this compiler!")
            return 0;
    #endif
}

int main(int argc, char **argv)
{
    int x = 1000000000;
    int y = 20000;
    int z = x * y;
    int f = query_intel_x86_eflags(0x801);
    printf("%X\n", f);
}

Jika operan dikalikan tanpa overflow, Anda akan mendapatkan nilai balik 0 dari query_intel_eflags(0x801), yaitu, carry atau flag overflow tidak diatur. Dalam kode contoh utama yang disediakan (), terjadi overflow dan kedua flag diset ke 1. Pemeriksaan ini tidak menyiratkan perhitungan lebih lanjut, jadi harus cukup cepat.

Malaikat Sinigersky
sumber
21

Jika Anda memiliki tipe data yang lebih besar daripada yang ingin Anda uji (mis. Anda menambahkan 32-bit dan Anda memiliki tipe 64-bit), maka ini akan mendeteksi jika terjadi overflow. Contoh saya adalah untuk menambahkan 8-bit. Tapi itu bisa ditingkatkan.

uint8_t x, y;    /* Give these values */
const uint16_t data16    = x + y;
const bool carry        = (data16 > 0xFF);
const bool overflow     = ((~(x ^ y)) & (x ^ data16) & 0x80);

Ini didasarkan pada konsep-konsep yang dijelaskan pada halaman ini: http://www.cs.umd.edu/class/spring2003/cmsc311/Notes/Comb/overflow.html

Sebagai contoh 32-bit, 0xFFmenjadi 0xFFFFFFFFdan 0x80menjadi 0x80000000dan akhirnya uint16_tmenjadi uint64_t.

CATATAN : ini menangkap penambahan / pengurangan bilangan bulat bilangan bulat, dan saya menyadari bahwa pertanyaan Anda melibatkan multiplikasi. Dalam hal ini, pembagian kemungkinan merupakan pendekatan terbaik. Ini biasanya merupakan cara callocimplementasi memastikan bahwa parameter tidak meluap ketika mereka dikalikan untuk mendapatkan ukuran akhir.

Evan Teran
sumber
Tautannya rusak: HTTP 403: Forbidden
Peter Mortensen
18

Cara paling sederhana adalah mengubah unsigned longs Anda menjadi unsigned long longs, lakukan penggandaan Anda, dan bandingkan hasilnya dengan 0x100000000LL.

Anda mungkin akan menemukan bahwa ini lebih efisien daripada melakukan pembagian seperti yang telah Anda lakukan dalam contoh Anda.

Oh, dan itu akan bekerja di C dan C ++ (karena Anda telah menandai pertanyaan dengan keduanya).


Baru saja melihat manual glibc . Ada yang menyebutkan jebakan integer overflow ( FPE_INTOVF_TRAP) sebagai bagian dari SIGFPE. Itu akan ideal, terlepas dari bagian-bagian buruk dalam manual:

FPE_INTOVF_TRAP Integer overflow (tidak mungkin dalam program C kecuali Anda mengaktifkan trapping overflow dengan cara yang spesifik perangkat keras).

Sedikit memalukan kok.

Andrew Edgecombe
sumber
4
Heh ... yang tidak saya katakan adalah saya mengajukan pertanyaan ini sebagai persiapan untuk menulis sebuah program untuk menyelesaikan masalah dengan angka yang lebih besar, di mana saya sudah menggunakan int panjang. Karena int panjang tidak (diduga) dalam standar C ++, saya terjebak dengan versi 32-bit untuk menghindari kebingungan.
Chris Johnson
Saya menyarankan menggunakan ULONG_MAXyang lebih mudah untuk mengetik dan lebih portabel daripada hard-coding 0x100000000.
jw013
24
Ini tidak berfungsi ketika longdan long longmemiliki ukuran yang sama (misalnya pada banyak kompiler 64-bit).
interjay
Mengandalkan sinyal untuk memberi tahu Anda tentang luapan akan sangat lambat.
SamB
@ Sam Hanya jika luapan diharapkan sering terjadi.
user253751
18

Berikut adalah cara yang sangat cepat untuk mendeteksi overflow untuk setidaknya penambahan, yang mungkin memberikan petunjuk untuk multiplikasi, pembagian, dan kekuatan.

Idenya adalah persis karena prosesor hanya akan membiarkan nilai membungkus kembali ke nol dan bahwa C / C ++ akan diabstraksi dari prosesor tertentu, Anda dapat:

uint32_t x, y;
uint32_t value = x + y;
bool overflow = value < (x | y);

Ini keduanya memastikan bahwa jika satu operan nol dan satu tidak, maka overflow tidak akan terdeteksi secara salah dan secara signifikan lebih cepat daripada banyak operasi BUKAN / XOR / DAN / seperti yang disarankan sebelumnya.

Seperti yang ditunjukkan, pendekatan ini, meskipun lebih baik daripada cara lain yang lebih rumit, masih dapat dioptimalkan. Berikut ini adalah revisi dari kode asli yang berisi optimisasi:

uint32_t x, y;
uint32_t value = x + y;
const bool overflow = value < x; // Alternatively "value < y" should also work

Cara yang lebih efisien dan murah untuk mendeteksi multiplication overflow adalah:

uint32_t x, y;
const bool overflow = (x >> 16U) * (y >> 16U);
uint32_t value = overflow ? UINT32_MAX : x * y;

Ini menghasilkan UINT32_MAX saat overflow, atau hasil perkalian. Ini adalah perilaku yang tidak ditentukan untuk memungkinkan perkalian untuk melanjutkan untuk bilangan bulat yang ditandatangani dalam kasus ini.

DX-MON
sumber
Saya tidak setuju karena teori perhitungan .. pertimbangkan yang berikut: y> x, nilai overflows, y hanya lebih besar dari x karena bit tanda sedang diset (1 + 255, misalnya, untuk karakter yang tidak ditandatangani) menguji nilai dan x akan menghasilkan di overflow = false - maka penggunaan logika atau untuk mencegah perilaku yang rusak ini.
DX-MON
Tes ini berfungsi untuk angka yang Anda berikan (x: = 1, y: = 255, size = uint8_t): nilainya akan 0 (1 + 255) dan 0 <1 benar. Ini memang bekerja untuk setiap pasangan nomor.
Gunther Piez
Hmm, Anda membuat poin yang bagus. Saya masih tetap di sisi keselamatan menggunakan atau trik meskipun sebagai kompiler yang baik akan mengoptimalkannya penyedia Anda memang benar untuk semua input, termasuk angka-angka yang tidak meluap seperti "0 + 4" di mana hasilnya tidak akan meluap.
DX-MON
4
Jika ada kelebihan, dari x+y>=256dan value=x+y-256. Karena y<256selalu berlaku, (y-256) adalah negatif dan value < xselalu benar. Buktinya untuk kasus non overflow sangat mirip.
Gunther Piez
2
@ DX-MON: Metode pertama Anda diperlukan jika Anda juga memiliki carry bit dari add sebelumnya. uint32_t x[N], y[N], z[N], carry=0; for (int i = 0; i < N; i++) { z[i] = x[i] + y[i] + carry; carry = z[i] < (x[i] | y[i]); }Jika Anda tidak ornilainya, Anda tidak akan dapat membedakan antara satu operan dan carry bit menjadi nol dan satu operand 0xffffffffdan carry bit menjadi satu.
Matt
14

Anda tidak dapat mengakses flag overflow dari C / C ++.

Beberapa kompiler memungkinkan Anda untuk memasukkan instruksi trap ke dalam kode. Pada GCC opsinya adalah -ftrapv.

Satu-satunya hal yang portabel dan kompiler yang dapat Anda lakukan adalah memeriksa sendiri kelebihannya. Sama seperti yang Anda lakukan dalam contoh Anda.

Namun, -ftrapvtampaknya tidak melakukan apa pun pada x86 menggunakan GCC terbaru. Saya kira itu adalah sisa dari versi lama atau khusus untuk beberapa arsitektur lain. Saya mengharapkan kompiler untuk memasukkan INTO opcode setelah setiap penambahan. Sayangnya tidak melakukan ini.

Nils Pipenbrinck
sumber
Mungkin bervariasi: -ftrapv tampaknya berfungsi dengan baik menggunakan GCC 4.3.4 pada kotak Cygwin. Ada contoh di stackoverflow.com/questions/5005379/…
Nate Kohl
3
Anda berdua benar. -ftrapv melakukan pekerjaan tetapi hanya untuk bilangan bulat yang ditandatangani
ZAB
14

Untuk bilangan bulat yang tidak ditandatangani, cukup periksa bahwa hasilnya lebih kecil dari salah satu argumen:

unsigned int r, a, b;
r = a + b;
if (r < a)
{
    // Overflow
}

Untuk bilangan bulat yang ditandatangani, Anda dapat memeriksa tanda-tanda argumen dan hasilnya.

Bilangan bulat dari tanda yang berbeda tidak dapat melimpah, dan bilangan bulat dari tanda yang sama hanya jika hasilnya berupa tanda yang berbeda:

signed int r, a, b, s;
r = a + b;
s = a>=0;
if (s == (b>=0) && s != (r>=0))
{
    // Overflow
}
Peter Mortensen
sumber
Nah metode pertama juga akan bekerja untuk bilangan bulat yang ditandatangani, bukan? char result = (char)127 + (char)3;akan menjadi -126; lebih kecil dari kedua operan.
primfaktor
1
Oh saya mengerti, masalahnya adalah fakta bahwa itu tidak ditentukan untuk tipe yang ditandatangani.
primfaktor
27
-1 overflow dari angka yang ditandatangani menghasilkan perilaku yang tidak terdefinisi (maka tes ini terlambat untuk benar-benar bermanfaat).
Voo
1
@primfaktor itu tidak berfungsi untuk masuk int: char ((- 127) + (-17)) = 112. Untuk masuk ditandatangani Anda harus memeriksa bit tanda dari argumen dan hasil
phuclv
3
Seperti yang telah dinyatakan, solusi untuk integer yang ditandatangani tidak berfungsi karena perilaku + b yang tidak terdefinisi dalam kasus overflow. Memeriksa overflow dengan integer yang ditandatangani harus dilakukan sebelum operasi.
Marwan Burelle
11

Saya perlu menjawab pertanyaan yang sama untuk angka floating point, di mana bit masking dan shifting tidak terlihat menjanjikan. Pendekatan yang saya tetapkan pada karya untuk angka-angka titik yang ditandatangani dan tidak ditandatangani, bilangan bulat dan mengambang. Ini berfungsi bahkan jika tidak ada tipe data yang lebih besar untuk dipromosikan ke untuk perhitungan menengah. Ini bukan yang paling efisien untuk semua jenis ini, tetapi karena itu bekerja untuk mereka semua, itu layak digunakan.

Tes Overflow, Penambahan, dan Pengurangan yang ditandatangani:

  1. Dapatkan konstanta yang mewakili nilai terbesar dan terkecil yang mungkin untuk tipe, MAXVALUE dan MINVALUE.

  2. Hitung dan bandingkan tanda-tanda operan.

    Sebuah. Jika salah satu nilai nol, maka penambahan atau pengurangan tidak bisa meluap. Lewati tes yang tersisa.

    b. Jika tanda-tandanya berlawanan, maka penambahan tidak bisa meluap. Lewati tes yang tersisa.

    c. Jika tanda-tandanya sama, maka pengurangan tidak bisa meluap. Lewati tes yang tersisa.

  3. Tes untuk aliran MAXVALUE positif.

    Sebuah. Jika kedua tanda positif dan MAXVALUE - A <B, maka penambahan akan melimpah.

    b. Jika tanda B negatif dan MAXVALUE - A <-B, maka pengurangan akan melimpah.

  4. Tes untuk luapan negatif MINVALUE.

    Sebuah. Jika kedua tanda negatif dan MINVALUE - A> B, maka penambahan akan melimpah.

    b. Jika tanda A negatif dan MINVALUE - A> B, maka pengurangan akan melimpah.

  5. Kalau tidak, tidak ada luapan.

Tes Overflow, Multiplikasi dan Divisi yang ditandatangani:

  1. Dapatkan konstanta yang mewakili nilai terbesar dan terkecil yang mungkin untuk tipe, MAXVALUE dan MINVALUE.

  2. Hitung dan bandingkan besaran (nilai absolut) dari operan dengan satu. (Di bawah ini, anggap A dan B adalah magnitudo ini, bukan yang asli yang ditandatangani.)

    Sebuah. Jika salah satu nilai adalah nol, perkalian tidak dapat meluap, dan pembagian akan menghasilkan nol atau tak terhingga.

    b. Jika salah satu nilai adalah satu, multiplikasi dan pembagian tidak dapat meluap.

    c. Jika besarnya satu operan di bawah satu dan yang lainnya lebih besar dari satu, multiplikasi tidak dapat meluap.

    d. Jika besarnya keduanya kurang dari satu, pembagian tidak dapat meluap.

  3. Tes untuk aliran MAXVALUE positif.

    Sebuah. Jika kedua operan lebih besar dari satu dan MAXVALUE / A <B, maka multiplikasi akan melimpah.

    b. Jika B kurang dari satu dan MAXVALUE * B <A, maka pembagian akan melimpah.

  4. Kalau tidak, tidak ada luapan.

Catatan: Overflow minimum MINVALUE ditangani oleh 3, karena kami mengambil nilai absolut. Namun, jika ABS (MINVALUE)> MAXVALUE, maka kami akan memiliki beberapa false positive yang langka.

Tes untuk aliran bawah serupa, tetapi melibatkan EPSILON (angka positif terkecil yang lebih besar dari nol).

Paul Chernoch
sumber
1
Setidaknya pada sistem POSIX, sinyal SIGFPE dapat diaktifkan untuk floating point di bawah / overflow.
Chris Johnson
Sementara mengkonversi ke floating point dan kembali berfungsi, itu (menurut pengujian saya pada mesin 32bit) jauh lebih lambat daripada solusi lain.
JanKanis
Peninjau mendeteksi kasus yang hilang untuk pengurangan bagian 2. Saya setuju bahwa 0 - MINVALUE akan meluap. Jadi pengujian untuk kasus ini harus ditambahkan.
Paul Chernoch
<pedantic> Integer tidak underflow (= menjadi terlalu dekat dengan nol untuk diwakili dengan akurasi apa pun). 1.0e-200 / 1.0e200akan menjadi contoh dari underflow yang sebenarnya, dengan asumsi IEEE berlipat ganda. Sebaliknya, istilah yang benar di sini adalah luapan negatif. </pedantic>
Arne Vogel
Lebih tepatnya, alasan mengapa bilangan bulat tidak dianggap sebagai underflow adalah karena perilaku pemotongan yang didefinisikan, misalnya 1/INT_MAXbisa juga dianggap underflow, tetapi bahasa hanya memerintahkan pemotongan ke nol.
Arne Vogel
8

CERT telah mengembangkan pendekatan baru untuk mendeteksi dan melaporkan limpasan bilangan bulat bertanda tangan, pembungkus bilangan bulat tak bertanda, dan pemotongan bilangan bulat menggunakan model bilangan bulat "seolah-olah" tak berhingga (AIR). CERT telah menerbitkan laporan teknis yang menggambarkan model dan menghasilkan prototipe kerja berdasarkan GCC 4.4.0 dan GCC 4.5.0.

Model integer AIR baik menghasilkan nilai yang setara dengan yang akan diperoleh dengan menggunakan bilangan bulat tak terhingga atau hasil dalam pelanggaran kendala runtime. Tidak seperti model integer sebelumnya, integer AIR tidak memerlukan jebakan yang tepat, dan akibatnya tidak merusak atau menghambat sebagian besar optimasi yang ada.

Robert C. Seacord
sumber
Saya tidak melihat sesuatu yang berguna di tautan, tetapi itu terdengar seperti model yang sudah lama saya sarankan. Ini mendukung sebagian besar optimasi yang bermanfaat, sementara juga mendukung jaminan semantik yang bermanfaat yang sebagian besar implementasi dapat berikan tanpa biaya. Jika kode tahu bahwa input ke suatu fungsi akan valid dalam semua kasus di mana output penting , tetapi tidak tahu sebelumnya apakah output itu penting, bisa membiarkan luapan terjadi dalam kasus di mana mereka tidak akan mempengaruhi apa pun mungkin lebih mudah dan lebih efisien daripada harus mencegahnya dengan segala cara.
supercat
8

Alat lain yang menarik adalah IOC: An Integer Overflow Checker untuk C / C ++ .

Ini adalah kompilasi Dentang tambalan , yang menambahkan pemeriksaan ke kode pada waktu kompilasi.

Anda mendapatkan hasil seperti ini:

CLANG ARITHMETIC UNDEFINED at <add.c, (9:11)> :
Op: +, Reason : Signed Addition Overflow,
BINARY OPERATION: left (int32): 2147483647 right (int32): 1
Willem Hengeveld
sumber
1
Tambalan ini sekarang digabungkan ke dentang basis kode di antara pembersih lainnya, lihat jawaban saya.
ZAB
7

Varian lain dari solusi, menggunakan bahasa assembly, adalah prosedur eksternal. Contoh ini untuk perkalian integer tak bertanda menggunakan g ++ dan fasm di Linux x64.

Prosedur ini mengalikan dua argumen integer yang tidak ditandatangani (32 bit) (sesuai dengan spesifikasi untuk amd64 (bagian 3.2.3 Parameter Passing ).

Jika kelas adalah INTEGER, register yang tersedia berikutnya dari urutan% rdi,% rsi,% rdx,% rcx,% r8, dan% r9 digunakan

(edi dan esi mendaftar dalam kode saya)) dan mengembalikan hasilnya atau 0 jika terjadi overflow.

format ELF64

section '.text' executable

public u_mul

u_mul:
  MOV eax, edi
  mul esi
  jnc u_mul_ret
  xor eax, eax
u_mul_ret:
ret

Uji:

extern "C" unsigned int u_mul(const unsigned int a, const unsigned int b);

int main() {
    printf("%u\n", u_mul(4000000000,2)); // 0
    printf("%u\n", u_mul(UINT_MAX/2,2)); // OK
    return 0;
}

Tautkan program dengan file objek asm. Dalam kasus saya, di Qt Creator , tambahkan ke LIBSdalam file .pro.

Bartolo-Otrit
sumber
5

Hitung hasilnya dengan ganda. Mereka memiliki 15 digit signifikan. Persyaratan Anda memiliki batas atas yang keras pada c dari 10 8  - ia dapat memiliki paling banyak 8 digit. Oleh karena itu, hasilnya akan tepat jika berada dalam jangkauan, dan tidak akan meluap sebaliknya.

MSalters
sumber
5

Coba makro ini untuk menguji bit overflow dari mesin 32-bit (mengadaptasi solusi Angel Sinigersky)

#define overflowflag(isOverflow){   \
size_t eflags;                      \
asm ("pushfl ;"                     \
     "pop %%eax"                    \
    : "=a" (eflags));               \
isOverflow = (eflags >> 11) & 1;}

Saya mendefinisikannya sebagai makro karena kalau tidak bit overflow akan ditimpa.

Berikutnya adalah aplikasi kecil dengan kode segmen di atas:

#include <cstddef>
#include <stdio.h>
#include <iostream>
#include <conio.h>
#if defined( _MSC_VER )
#include <intrin.h>
#include <oskit/x86>
#endif

using namespace std;

#define detectOverflow(isOverflow){     \
size_t eflags;                      \
asm ("pushfl ;"                     \
    "pop %%eax"                     \
    : "=a" (eflags));               \
isOverflow = (eflags >> 11) & 1;}

int main(int argc, char **argv) {

    bool endTest = false;
    bool isOverflow;

    do {
        cout << "Enter two intergers" << endl;
        int x = 0;
        int y = 0;
        cin.clear();
        cin >> x >> y;
        int z = x * y;
        detectOverflow(isOverflow)
        printf("\nThe result is: %d", z);
        if (!isOverflow) {
            std::cout << ": no overflow occured\n" << std::endl;
        } else {
            std::cout << ": overflow occured\n" << std::endl;
        }

        z = x * x * y;
        detectOverflow(isOverflow)
        printf("\nThe result is: %d", z);
        if (!isOverflow) {
            std::cout << ": no overflow ocurred\n" << std::endl;
        } else {
            std::cout << ": overflow occured\n" << std::endl;
        }

        cout << "Do you want to stop? (Enter \"y\" or \"Y)" << endl;

        char c = 0;

        do {
            c = getchar();
        } while ((c == '\n') && (c != EOF));

        if (c == 'y' || c == 'Y') {
            endTest = true;
        }

        do {
            c = getchar();
        } while ((c != '\n') && (c != EOF));

    } while (!endTest);
}
Markus Demarmels
sumber
4
Tidak semua mesin 32-bit yang kompatibel dengan Intel x86, dan tidak semua kompiler mendukung sintaks perakitan gnu (saya merasa lucu bahwa Anda memposting kode yang menguji _MSC_VERmeskipun kompilasi MS semua akan menolak kode).
Ben Voigt
2

Menangkap Integer Overflows di C menunjukkan solusi yang lebih umum daripada yang dibahas oleh CERT (ini lebih umum dalam hal jenis yang ditangani), bahkan jika itu memerlukan beberapa ekstensi GCC (saya tidak tahu seberapa luas mereka didukung).

Blaisorblade
sumber
2

Anda tidak dapat mengakses flag overflow dari C / C ++.

Saya tidak setuju dengan ini. Anda bisa menulis beberapa bahasa rakitan inline dan menggunakan joinstruksi (lompatan lompatan) dengan asumsi Anda berada di x86 untuk menjebak overflow. Tentu saja, kode Anda tidak lagi portabel untuk arsitektur lain.

Lihatlah info asdan info gcc.

Tarski
sumber
8
assembler inline bukanlah fitur C / C ++ dan platform independen. Pada x86 Anda dapat menggunakan instruksi ke dalam cabang btw.
Nils Pipenbrinck
0

Untuk memperluas jawaban Kepala Geek, ada cara yang lebih cepat untuk melakukan addition_is_safe;

bool addition_is_safe(unsigned int a, unsigned int b)
{
    unsigned int L_Mask = std::numeric_limits<unsigned int>::max();
    L_Mask >>= 1;
    L_Mask = ~L_Mask;

    a &= L_Mask;
    b &= L_Mask;

    return ( a == 0 || b == 0 );
}

Ini menggunakan arsitektur mesin aman, dalam bahwa integer 64-bit dan 32-bit masih akan bekerja dengan baik. Pada dasarnya, saya membuat topeng yang akan menutupi semua kecuali bagian yang paling signifikan. Kemudian, saya menutupi kedua bilangan bulat, dan jika salah satu dari mereka tidak memiliki set bit, maka penambahan aman.

Ini akan lebih cepat jika Anda melakukan inisialisasi topeng di beberapa konstruktor, karena itu tidak pernah berubah.

Steztric
sumber
5
Ini tidak benar. Carry mungkin membawa bit dari posisi lebih rendah yang akan menyebabkan overflow. Pertimbangkan menambah UINT_MAX + 1. Setelah masking, aakan memiliki bit set yang tinggi, tetapi 1akan menjadi nol dan karena itu fungsinya akan kembali true, penambahan aman - namun Anda langsung menuju overflow.
babi
0

mozilla::CheckedInt<T>menyediakan matematika integer berlebih yang diperiksa untuk tipe integer T(menggunakan intrinsik kompiler pada dentang dan gcc jika tersedia). Kode ini berada di bawah MPL 2.0 dan tergantung pada tiga ( IntegerTypeTraits.h, Attributes.hdan Compiler.h) header perpustakaan non-standar hanya header lainnya ditambah mesin pernyataan spesifik Mozilla . Anda mungkin ingin mengganti mesin asersi jika Anda mengimpor kode.

hsivonen
sumber
-1

Jawaban MSalter adalah ide yang bagus.

Jika perhitungan integer diperlukan (untuk presisi), tetapi floating point tersedia, Anda bisa melakukan sesuatu seperti:

uint64_t foo(uint64_t a, uint64_t b) {
    double dc;

    dc = pow(a, b);

    if (dc < UINT_MAX) {
       return (powu64(a, b));
    }
    else {
      // Overflow
    }
}
Frank Szczerba
sumber
Biasanya, saya akan mengatakan bahwa mengulang perhitungan dalam floating point adalah ide yang buruk, tetapi untuk kasus eksponensial khusus ini , mungkin lebih efisien. Tetapi tes harus (c * log(a) < max_log), di manaconst double max_log = log(UINT_MAX)
Toby Speight
-1

Set instruksi x86 termasuk instruksi multiplikasi yang tidak ditandatangani yang menyimpan hasilnya ke dua register. Untuk menggunakan instruksi dari C, seseorang dapat menulis kode berikut dalam program 64-bit (GCC):

unsigned long checked_imul(unsigned long a, unsigned long b) {
  unsigned __int128 res = (unsigned __int128)a * b;
  if ((unsigned long)(res >> 64))
    printf("overflow in integer multiply");
  return (unsigned long)res;
}

Untuk program 32-bit, kita perlu membuat hasilnya 64 bit dan parameter 32 bit.

Alternatifnya adalah menggunakan intrinsik yang bergantung pada kompiler untuk memeriksa register flag. Dokumentasi GCC untuk intrinsik overflow dapat ditemukan dari 6.56 Fungsi Bawaan untuk Melakukan Aritmatika dengan Memeriksa Overflow .

Pauli Nieminen
sumber
1
Anda harus menggunakan tipe 128-bit yang __uint128tidak ditandatangani untuk menghindari overflow yang ditandatangani dan menggeser nilai negatif ke kanan.
chqrlie
Apa itu "insting yang bergantung pada kompiler" dan "insting yang meluap" ? Apakah maksud Anda " fungsi intrinsik " ? Apakah Anda punya referensi? (Harap balas dengan mengedit jawaban Anda , bukan di sini dalam komentar (jika perlu).)
Peter Mortensen
-3
#include <stdio.h>
#include <stdlib.h>

#define MAX 100 

int mltovf(int a, int b)
{
    if (a && b) return abs(a) > MAX/abs(b);
    else return 0;
}

main()
{
    int a, b;

    for (a = 0; a <= MAX; a++)
        for (b = 0; b < MAX; b++) {

        if (mltovf(a, b) != (a*b > MAX)) 
            printf("Bad calculation: a: %d b: %d\n", a, b);

    }
}
Scott Franco
sumber
-3

Cara yang bersih untuk melakukannya adalah dengan menimpa semua operator (+ dan * khususnya) dan memeriksa overflow sebelum melakukan operasi.

Brian R. Bondy
sumber
6
Kecuali Anda tidak dapat menimpa operator untuk tipe bawaan. Anda harus menulis kelas untuk itu dan menulis ulang kode klien untuk menggunakannya.
Blaisorblade
-3

Tergantung untuk apa Anda menggunakannya. Melakukan penambahan atau perkalian unsigned long (DWORD), solusi terbaik adalah dengan menggunakan ULARGE_INTEGER.

ULARGE_INTEGER adalah struktur dua DWORD. Nilai penuh dapat diakses sebagai "QuadPart" sementara DWORD tinggi diakses sebagai "HighPart" dan DWORD rendah diakses sebagai "LowPart".

Sebagai contoh:

DWORD
My Addition(DWORD Value_A, DWORD Value_B)
{
    ULARGE_INTEGER a, b;

    b.LowPart = Value_A;  // A 32 bit value(up to 32 bit)
    b.HighPart = 0;
    a.LowPart = Value_B;  // A 32 bit value(up to 32 bit)
    a.HighPart = 0;

    a.QuadPart += b.QuadPart;

    // If  a.HighPart
    // Then a.HighPart contains the overflow (carry)

    return (a.LowPart + a.HighPart)

    // Any overflow is stored in a.HighPart (up to 32 bits)
Spyros Panaoussis
sumber
6
Sayangnya, ini adalah solusi khusus Windows. Platform lain tidak punya ULARGE_INTEGER.
Mysticial
-3

Untuk melakukan perkalian tanpa tanda tangan tanpa meluap dengan cara portabel berikut ini dapat digunakan:

... /* begin multiplication */
unsigned multiplicand, multiplier, product, productHalf;
int zeroesMultiplicand, zeroesMultiplier;
zeroesMultiplicand = number_of_leading_zeroes( multiplicand );
zeroesMultiplier   = number_of_leading_zeroes( multiplier );
if( zeroesMultiplicand + zeroesMultiplier <= 30 ) goto overflow;
productHalf = multiplicand * ( c >> 1 );
if( (int)productHalf < 0 ) goto overflow;
product = productHalf * 2;
if( multiplier & 1 ){
   product += multiplicand;
   if( product < multiplicand ) goto overflow;
}
..../* continue code here where "product" is the correct product */
....
overflow: /* put overflow handling code here */

int number_of_leading_zeroes( unsigned value ){
   int ctZeroes;
   if( value == 0 ) return 32;
   ctZeroes = 1;
   if( ( value >> 16 ) == 0 ){ ctZeroes += 16; value = value << 16; }
   if( ( value >> 24 ) == 0 ){ ctZeroes +=  8; value = value <<  8; }
   if( ( value >> 28 ) == 0 ){ ctZeroes +=  4; value = value <<  4; }
   if( ( value >> 30 ) == 0 ){ ctZeroes +=  2; value = value <<  2; }
   ctZeroes -= x >> 31;
   return ctZeroes;
}
Tyler Durden
sumber
-4

Cara sederhana untuk menguji overflow adalah dengan melakukan validasi dengan memeriksa apakah nilai saat ini kurang dari nilai sebelumnya. Misalnya, misalkan Anda memiliki loop untuk mencetak kekuatan 2:

long lng;
int n;
for (n = 0; n < 34; ++n)
{
   lng = pow (2, n);
   printf ("%li\n", lng);
}

Menambahkan limpahan memeriksa cara saya menggambarkan hasil dalam ini:

long signed lng, lng_prev = 0;
int n;
for (n = 0; n < 34; ++n)
{
    lng = pow (2, n);
    if (lng <= lng_prev)
    {
        printf ("Overflow: %i\n", n);
        /* Do whatever you do in the event of overflow.  */
    }
    printf ("%li\n", lng);
    lng_prev = lng;
}

Ini berfungsi untuk nilai yang tidak ditandatangani serta nilai yang ditandatangani positif dan negatif.

Tentu saja, jika Anda ingin melakukan sesuatu yang serupa untuk menurunkan nilai alih-alih meningkatkan nilai, Anda akan membalik <=tanda untuk membuatnya >=, dengan asumsi perilaku underflow sama dengan perilaku overflow. Dalam semua kejujuran, itu tentang portabel karena Anda akan mendapatkan tanpa akses ke flag melimpah CPU (dan itu akan membutuhkan kode perakitan inline, tetap membuat kode Anda non-portabel di seluruh implementasi).

Dustin
sumber
9
Jika nilai yang ditandatangani meluap, perilaku program Anda tidak ditentukan. Tidak dijamin untuk membungkus.
David Stone