İmzasız tam sayı çarpma taşmasını nasıl tespit edebilirim?


618

Bir b = c tüm çözümlerini bulmak için C ++ bir program yazıyordu , burada a , b ve c birlikte 0-9 arasındaki tüm basamakları tam olarak bir kez kullanır. Program değerlerini üzerinde ilmek bir ve b ve bir rakam sayma rutin her zaman koştu bir , b ve bir b basamak şart yerine yapılmadığını kontrol etmek.

Bununla birlikte, bir b tam sayı sınırını aştığında sahte çözümler üretilebilir . Bu gibi kodu kullanarak kontrol sona erdi:

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

Taşma testi yapmanın daha iyi bir yolu var mı? Bazı yongaların taşma meydana geldiğinde ayarlanan bir iç bayrağı olduğunu biliyorum, ancak C veya C ++ ile erişildiğini hiç görmedim.


İmzalı int taşmanın C ve C ++ 'da tanımlanmamış bir davranış olduğuna dikkat edin ve bu nedenle gerçekten buna neden olmadan tespit etmeniz gerekir. Eklemeden önce imzalı int taşması için bkz . C / C ++ 'da imzalı taşmayı algılama .


21
Bu konuda faydalı olabilecek bilgiler: Seacord tarafından "C ve C ++ 'da Güvenli Kodlama" Bölüm 5 - http://www.informit.com/content/images/0321335724/samplechapter/seacord_ch05.pdf C ++ için SafeInt sınıfları - http : //blogs.msdn.com/david_leblanc/archive/2008/09/30/safeint-3-on-codeplex.aspx - http://www.codeplex.com/SafeInt C için IntSafe kütüphanesi: - [ blogs.msdn .com / michael_howard / arşiv
Michael Burr

3
Seacord'un Güvenli Kodlaması harika bir kaynaktır, ancak IntegerLib kullanmayın. Bkz. Blog.regehr.org/archives/593 .
jww

32
Gcc derleyici seçeneği -ftrapv, (imzalı) tamsayı taşması üzerinde bir SIGABRT üretmesine neden olur. Buraya bakın .
nibot

1
Taşma sorusuna cevap vermez, ancak soruna gelmenin başka bir yolu, her zaman yeterli hassasiyete sahip olmanızı garanti etmek için GMP gibi bir BigNum kütüphanesi kullanmak olacaktır . Önde yeterli basamak tahsis ederseniz taşma konusunda endişelenmenize gerek kalmayacaktır.
wrdieter

1
@HeadGeek tarafından cevabında verilen bilgiler de söyleyeceğim şeydir. Ancak, bir ek ile. Şimdi bir çarpma için taşma tespit etme şekliniz muhtemelen en hızlısıdır. ARM'de HeadGeek'in cevabına yorum yaptığım gibi , sayının sırasını (en yüksek bitinin olduğu yer) belirlemek için clztalimatı veya __clz(unsigned)işlevi kullanabilirsiniz. Bunun x86 veya x64'te mevcut olup olmadığından emin olmadığım için olmadığını varsayacağım ve en önemli biti bulmanın en kötü log(sizeof(int)*8)talimatları alacağını söyleyeceğim .
nonsensickle

Yanıtlar:


229

İmzasız tamsayılar kullandığınızı görüyorum. Tanım olarak, C'de (C ++ hakkında bilmiyorum), imzasız aritmetik taşmaz ... yani, en azından C için, puanınız tartışılıyor :)

İşaretli tamsayılarla, taşma gerçekleştikten sonra, tanımlanmamış davranış (UB) oluştu ve programınız her şeyi yapabilir (örneğin: testleri sonuçsuz hale getirin). 

#include <limits.h>

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

Uygun bir program oluşturmak için, söz konusu taşmayı oluşturmadan önce taşmayı test etmeniz gerekir . Yöntem işaretsiz tamsayılarla da kullanılabilir:

// 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 */;

Bölünme için ( INT_MINve -1özel durum hariç ), INT_MINveya devam etme olasılığı yoktur INT_MAX.


97
İmzasız tamsayılar C ++ 'da da kesinlikle taşmaz (ISO / IEC 14882: 2003 3.9.1.4). Sorudaki 'taşma' kullanımım, imzasız türlerin iyi tanımlanmış paketlemesini içermesi amaçlanan, daha pozitif bir anlamdı. 64). Matematiksel sonsuz boyutlu tamsayı davranışından sapma olarak taşma ile dilde tanımlanmamış bir davranış olarak taşma arasındaki fark nadiren açık hale getirilmiş görünmektedir.
Chris Johnson

15
Bu testi olmak gerekmez x >= 0- x > 0yeterli olacaktır (eğer x == 0o zaman, x + adeğil taşması bilinen nedenlerle can).
caf

2
@pmg, standarttan destekleyici bir teklif var mı?
Pacerier

5
Bu yaklaşımı seviyorum ... Ancak, dikkatli olun: çarpma taşması tespiti, pozitif bir x olduğunu varsayar. X == 0 için sıfır algılama ile bölmeye yol açar ve negatif x için her zaman hatalı taşmayı algılar.
Franz

4
if ((a < INT_MIN / x))test çok geç. if (x == -1) Önce bir teste ihtiyaç vardır.
chux - Monica'yı eski durumuna döndür

164

Orada olan bir operasyon işlenen en çok anlamlı bir bit pozisyonları ve biraz temel ikili matematik bilgisini kullanarak, taşma olasılığı olup olmadığını belirlemek için bir yol.

Ayrıca, herhangi iki işlenen en büyük işlenenin en yüksek bir bitinden (en fazla) bir bit daha fazla sonuç verecektir. Örneğin:

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);
}

Çarpma için, herhangi iki işlenen (en fazla) işlenenlerin bitlerinin toplamıyla sonuçlanır. Örneğin:

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);
}

Benzer şekilde, sonucun maksimum boyutunu aşağıdaki gibi bir agüçle tahmin edebilirsiniz b:

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

(Elbette hedef tamsayı için bit sayısını değiştirin.)

Bir sayıdaki en yüksek bir bitin konumunu belirlemenin en hızlı yolundan emin değilim, burada bir kaba kuvvet yöntemi:

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

Mükemmel değil, ancak işlemi yapmadan önce herhangi iki sayının taşıp taşmayacağı konusunda size iyi bir fikir verecektir. İşlevdeki döngü nedeniyle sonucu önerdiğiniz şekilde kontrol etmekten daha hızlı olup olmayacağını bilmiyorum highestOneBitPosition, ama olabilir (özellikle önceden işlenenlerde kaç bit olduğunu biliyorsan).


98
ve elbette en yüksekOneBitPosition'ı yeniden oturum açmak için yeniden adlandırabilirsiniz :)
Oliver Hallam

37
Evet, aynı işlemdir log2, ancak matematiksel bir geçmişi olmayan biri için bu kadar açık olmayabilir.
Kafa Geek

48
Bu algoritma güvenli cevapları hafife almıyor mu? 2 ^ 31 + 0, en yüksekOneBitPosition (2 ^ 31) = 32 olduğu için güvensiz olarak algılardı. > 32.
clahey

19
senin göre multiplication_is_safe 0x8000 * 0x10000taşacak durumdaki (bit pozisyonları 16 + 17 = 33 olan > 32 öyle değil de,) çünkü 0x8000 * 0x10000 = 0x80000000bir imzasız 32 bitlik int içine açıkçası hala hangi uyuyor. Bu, bu kodların çalışmadığı örneklerden sadece bir tanesidir. 0x8000 * 0x10001, ...
Michi

13
@GT_mh: Ne demek istiyorsun? Dediğim gibi, mükemmel değil; bu bir kural Başparmak şey olduğunda kesin diyecekler var olan güvenli ama her hesaplama tam hesaplama yapmadan iyi olacağını belirlemek için bir yolu yoktur. 0x8000 * 0x10000iyi olduğu ortaya çıksa bile, bu tanım gereği "güvenli" değildir.
Kafa Geek

147

Clang 3.4+ ve GCC 5+ kontrol edilmiş aritmetik yapıları sunar. Özellikle bit testi güvenlik kontrollerine kıyasla bu soruna çok hızlı bir çözüm sunuyorlar.

OP'nin sorusundaki örnek için şu şekilde çalışır:

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
}

Clang belgeleri, c_testbir taşma meydana geldiğinde taşan sonucu içerip içermediğini belirtmez , ancak GCC belgeleri bunun gerçekleştiğini belirtir. Bu ikisinin __builtinuyumlu olmayı sevdiği göz önüne alındığında, muhtemelen Clang'ın bu şekilde çalıştığını varsaymak güvenlidir.

Orada bir __builtinint boyutları, uzun boyutlarda ve uzun uzun boyutları için imzalı ve imzasız varyantları ile taşabilir her aritmetik işlem (toplama, çıkarma, çarpma), için. Adın sözdizimi __builtin_[us](operation)(l?l?)_overflow:

  • uiçin imzasız veya siçin imzalanmış ;
  • işlem biridir add, subya da mul;
  • hiçbir lsonek, işlenenlerin ints olduğu anlamına gelir ; bir lanlamı long; iki ls ortalama long long.

Bu yüzden işaretli bir uzun tamsayı ilavesi için olurdu __builtin_saddl_overflow. Tam listeyi Clang dokümantasyon sayfasında bulabilirsiniz .

GCC 5+ ve Clang 3.8+ ayrıca değerlerin türünü belirtmeden işin o jenerik yerleşiklerinden sunuyoruz: __builtin_add_overflow, __builtin_sub_overflowve __builtin_mul_overflow. Bunlar daha küçük tipler üzerinde de çalışır int.

Yerleşikler platform için en iyi olandan daha düşüktür. X86'da taşıma, taşma ve işaret bayraklarını kontrol ederler.

Visual Studio'nun cl.exe dosyasının doğrudan eşdeğerleri yoktur. İmzasız toplama ve çıkarma için, dahil <intrin.h>kullanmanıza izin verir addcarry_uNNve subborrow_uNN(burada NN, addcarry_u8veya gibi bit sayısıdır subborrow_u64). İmzaları biraz geniş:

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_ingirdi üzerindeki taşıma / ödünç bayraktır ve dönüş değeri çıktıdaki taşıma / borçtur. İmzalı işlemler veya çarpmalar için eşdeğerleri yok gibi görünüyor.

Aksi takdirde, Windows için Clang artık üretime hazırdır (Chrome için yeterince iyi), bu da bir seçenek olabilir.


__builtin_sub_overflowkesinlikle Clang 3.4'te değil.
Richard Cook

2
@RichardCook, biraz zaman aldı ama Clang 3.9 sürümünden itibaren genel yerleşiklere sahip.
zneak

@tambre, olduğunu sanmıyorum.
Mart'ta zneak

4
Göre docs , __builtin_add_overflowve arkadaşlar zaten Clang 3.8 geçerli olmalıdır.
Lekensteyn

2
Teşekkürler. Harika çalışıyor. Herhangi bir fikir görsel c ++ için karşılık gelen işlevi nedir? Onları bulamıyorum.
Mudit Jain

53

Bazı derleyiciler, daha sonra test edebileceğiniz CPU'daki tamsayı taşma bayrağına erişim sağlar, ancak bu standart değildir.

Ayrıca, çarpma işlemini gerçekleştirmeden önce taşma olasılığını test edebilirsiniz:

if ( b > ULONG_MAX / a ) // a * b would overflow

11
... veya numeric_limits <TYPE> :: max () kullanın
Jonas Gulle

20
O zaman bir = 0 - bölünme aralarını işlemeyi unutmayın.
Thelema

16
@Thelema: "a = 0 ile işlemeyi unutmayın" - ve INT_MIN / -1.
JWW

1
Ya eğer b == ULONG_MAX / a? Sonra kalıntı olmadan abölünür göz önüne alındığında, yine de sığabilir ULONG_MAX.
domuz

Performans açısından akıllıca bir çarpmanın bir bölmeye kıyasla oldukça hızlı olması ve her çarpma için bir bölme eklemeniz. Bu gibi gelmiyor çözümü.
DrumM

40

Uyarı: GCC, derleme sırasında taşma kontrolünü optimize edebilir -O2. Bu seçenek -Wall, bazı durumlarda size bir uyarı verir.

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

ancak bu örnekte değil:

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

Tek güvenli yol, CERT kağıdında açıklandığı gibi taşmayı kontrol etmeden kontrol etmektir ve bu sistematik olarak kullanmak son derece sıkıcı olacaktır.

İle derlemek -fwrapvsorunu çözer, ancak bazı optimizasyonları devre dışı bırakır.

Umutsuzca daha iyi bir çözüme ihtiyacımız var. Ben derleyici taşma gerçekleşmeyen bir optimizasyon yaparken varsayılan olarak bir uyarı vermek gerektiğini düşünüyorum. Mevcut durum, derleyicinin bir taşma kontrolünü optimize etmesine izin veriyor, ki bu benim görüşüme göre kabul edilemez.


8
Derleyicilerin bunu yalnızca işaretli tamsayı türleriyle yapabileceğini unutmayın ; taşma, imzasız tamsayı türleri için tamamen tanımlanmıştır. Yine de, evet, oldukça tehlikeli bir tuzak!
SamB

1
"Bence derleyici taşmaya dayanmayan bir optimizasyon yaparken varsayılan olarak bir uyarı vermelidir." - for(int k = 0; k < 5; k++) {...}bir uyarı mı yapmalıyım?
user253751

2
@immibis: Neden olsun ki? Değerleri kderleme zamanında kolayca belirlenebilir. Derleyicinin varsayımlarda bulunması gerekmez.
MikeMB

2
@immibis: Yukarıdakileri alıntılamak için: "Bence derleyici
MikeMB

1
@MikeMB Derleyicinin n32'den az olup olmadığını kontrol etmek için uğraşmadığı optimizasyon , yalnızca daha düşük 5 bit kullanan bir kaydırma talimatı yayınlamadan önce n?
user253751

30

Clang artık hem imzalı hem de imzasız tamsayılar için dinamik taşma kontrollerini destekliyor. Bkz -fsanitize = tam sayı anahtarı. Şimdilik, hata ayıklama amaçları için tam olarak desteklenen dinamik taşma denetimine sahip tek C ++ derleyicisidir.


25

Birçok insanın taşma hakkındaki soruyu yanıtladığını görüyorum, ancak orijinal sorununu ele almak istedim. Sorunun, tüm basamaklar tekrarlanmadan kullanılacak şekilde bir b = c bulmak olduğunu söyledi. Tamam, bu yazıda sorduğu şey bu değildi, ancak yine de sorunun üst sınırını incelemek ve asla bir taşmayı hesaplamak veya tespit etmek zorunda olmayacağı sonucuna varmak gerektiğini düşünüyorum (not: Yetkin değilim) matematikte adım adım yaptım, ama sonuç o kadar basitti ki basit bir formüle sahip olabilir).

Ana nokta, sorunun a, b veya c için gerektirdiği üst sınırın 98.765.432 olmasıdır. Her neyse, sorunu önemsiz ve önemsiz kısımlara bölerek başlayarak:

  • x 0 == 1 (9, 8, 7, 6, 5, 4, 3, 2'nin tüm permütasyonları çözümdür)
  • x 1 == x (çözüm mümkün değil)
  • 0 b == 0 (çözüm mümkün değil)
  • 1 b == 1 (çözüm mümkün değil)
  • a b , a> 1, b> 1 (önemsiz değil)

Şimdi sadece başka bir çözümün mümkün olmadığını ve sadece permütasyonların geçerli olduğunu göstermeliyiz (ve sonra bunları yazdırmak için kod önemsizdir). Üst sınırlara geri dönüyoruz. Aslında üst sınır c = 98.765.432'dir. Üst sınır, çünkü 8 basamaklı en büyük sayıdır (her a ve b için toplam 10 basamak eksi 1). Bu üst sınır sadece c içindir, çünkü a ve b için sınırlar üstel büyüme nedeniyle çok daha düşük olmalıdır, çünkü hesaplayabildiğimiz gibi, b'yi 2'den üst sınıra değiştirir:

    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

Dikkat edin, örneğin son satır: 1.97 ^ 27 ~ 98M diyor. Yani, örneğin, 1 ^ 27 == 1 ve 2 ^ 27 == 134.217.728 ve bu bir çözüm değil, çünkü 9 haneye sahip (2> 1.97, bu yüzden gerçekten test edilmesi gerekenden daha büyük). Görüldüğü gibi, a ve b'yi test etmek için mevcut kombinasyonlar gerçekten küçüktür. B == 14 için, 2 ve 3'ü denememiz gerekir. B == 3 için, 2'den başlayıp 462'de dururuz. Tüm sonuçlar ~ 98M'den azdır.

Şimdi yukarıdaki tüm kombinasyonları test edin ve herhangi bir rakam tekrarlamayanları arayın:

    ['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)

Hiçbiri problemle uyuşmuyor ('0', '1', ..., '9' yokluğunda da görülebilir).

Bunu çözen örnek kod aşağıdadır. Ayrıca, Python'da yazıldığını unutmayın, keyfi hassas tamsayılara ihtiyaç duyduğu için değil (kod 98 milyondan daha büyük bir şey hesaplamıyor), ancak test miktarının çok küçük olduğunu öğrendiğimiz için, yüksek seviyeli bir dil kullanmalıyız. yerleşik kapsayıcılarından ve kitaplıklarından yararlanın (ayrıca not: kodun 28 satırı vardır).

    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)

1
neden üst sınır olarak 9.876.543.210 kullanmıyorsunuz?
Tom Roggero

3
Çünkü denklemin sol tarafı için 2 hane kullanılmalıdır.
Mart'ta hdante

2
Bir fark yarattığı için değil, ancak LHS'deki değerlerin 5:02'de> 1
Paul Childs

24

İşte soru için "taşınabilir olmayan" bir çözüm. Intel x86 ve x64 işlemcilerin her tamsayı aritmetik işleminden sonra işlemci tarafından doldurulan EFLAGS kaydı adı verilir . Burada ayrıntılı bir açıklamayı atlayacağım. İlgili bayraklar "Taşma" Bayrağı (maske 0x800) ve "Taşıma" Bayrağıdır (maske 0x1). Bunları doğru bir şekilde yorumlamak için, işlenenlerin imzalı veya imzasız türde olup olmadığına dikkat edilmelidir.

İşte bayrakları C / C ++ 'dan kontrol etmenin pratik bir yolu. Aşağıdaki kod, Visual Studio 2005 veya daha yeni (32 ve 64 bit) ve GNU C / C ++ 64 bit üzerinde çalışacaktır .

#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);
}

Eğer işlenenler taşma olmadan çarpılırsa, 0 değerinden bir dönüş değeri alırsınız query_intel_eflags(0x801), yani ne taşıma ne de taşma bayrakları ayarlanmaz. Verilen main () örnek kodunda bir taşma meydana gelir ve her iki bayrak da 1 olarak ayarlanır. Bu kontrol daha fazla hesaplama anlamına gelmez, bu yüzden oldukça hızlı olmalıdır.


21

Test etmek istediğinizden daha büyük bir veri türünüz varsa (32 bitlik bir eklenti yaptığınızı ve 64 bitlik bir türünüz olduğunu varsayalım), taşma olup olmadığını tespit eder. Örneğim 8 bitlik ekleme içindir. Ancak ölçeklendirilebilir.

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

Bu sayfada açıklanan kavramlara dayanmaktadır: http://www.cs.umd.edu/class/spring2003/cmsc311/Notes/Comb/overflow.html

32 bitlik bir örnek için, 0xFFolur 0xFFFFFFFFve 0x80olur 0x80000000ve sonunda a uint16_tolur uint64_t.

NOT : Bu, tamsayı toplama / çıkarma taşmalarını yakalar ve sorunuzun çarpım içerdiğini fark ettim. Bu durumda, bölünme muhtemelen en iyi yaklaşımdır. Bu genellikle callocuygulamaların, son boyutu elde etmek için çarpıldıkça parametrelerin taşmadığından emin olmalarının bir yoludur .


Bağlantı koptu: HTTP 403: Yasak
Peter Mortensen

18

En basit yol, s'lerinizi unsigned longs'ye dönüştürmek unsigned long long, çarpma işleminizi yapmak ve sonucu 0x100000000LL ile karşılaştırmaktır.

Muhtemelen bunun, örneğinizde yaptığınız gibi bölme işlemini yapmaktan daha etkili olduğunu göreceksiniz.

Oh, ve hem C hem de C ++ ile çalışacak (soruyu her ikisiyle de etiketlediğiniz gibi).


Sadece glibc el kitabına bir göz atın . FPE_INTOVF_TRAPParçası olarak bir tamsayı taşma tuzağından ( ) bahsedilmiştir SIGFPE. Bu, kılavuzdaki kötü bitler dışında ideal olacaktır:

FPE_INTOVF_TRAP Tamsayı taşması (donanıma özgü bir şekilde taşmayı bindirmeyi etkinleştirmediğiniz sürece bir C programında imkansız).

Gerçekten biraz utanç verici.


4
Heh ... Söylemediğim şey, zaten uzun uzun int kullanıyorum, daha büyük sayılarla bir sorunu çözmek için bir program yazmaya hazırlanırken bu soruyu sormamdı. Uzun uzun int (iddia edildiği gibi) C ++ standardında olmadığından, karışıklığı önlemek için 32 bit sürümle sıkıştım.
Chris Johnson

ULONG_MAXHangisinin yazılması daha kolay ve sabit kodlamadan daha taşınabilir olmasını öneririm 0x100000000.
jw013

24
Bu aynı boyutta olduğunda longve long longaynı boyutta çalışmaz (örneğin birçok 64 bit derleyicide).
interjay

Taşmalardan bahsetmek için sinyallere güvenmek zaten çok yavaş olurdu.
SamB

@ SamB Yalnızca taşmaların sık olması bekleniyorsa.
user253751

17

İşte en azından eklemeler için taşmayı tespit etmenin gerçekten hızlı bir yolu, bu da çarpma, bölme ve güç için bir yol açabilir.

Fikir, işlemcinin değerin sıfıra sarılmasına izin vereceği ve C / C ++ 'ın herhangi bir belirli işlemciden soyutlanması gerektiği için:

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

Bu, bir işlenen sıfırsa ve bir işlenmemişse, taşmanın yanlış algılanmamasını ve daha önce önerildiği gibi birçok NOT / XOR / AND / test işleminden önemli ölçüde daha hızlı olmasını sağlar.

Belirtildiği gibi, bu yaklaşım, diğer daha ayrıntılı yollardan daha iyi olmasına rağmen, hala optimize edilebilir. Optimizasyon içeren orijinal kodun bir düzeltmesi aşağıdadır:

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

Çarpma taşmasını tespit etmenin daha etkili ve ucuz bir yolu:

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

Bu, taşma durumunda UINT32_MAX veya çarpmanın sonucu ile sonuçlanır. Bu durumda çarpmanın imzalı tamsayılar için ilerlemesine izin vermek kesinlikle tanımlanmamış bir davranıştır.


Hesaplama teorisi nedeniyle katılmıyorum .. aşağıdakileri göz önünde bulundurun: y> x, değer taşmaları, y ayarlanmış işaret biti nedeniyle (yalnızca 1 + 255, örneğin imzasız karakterlerde) test değeri nedeniyle x'den büyüktür ve x sonuçlanır taşma = yanlış - bu nedenle mantıksal kullanımı veya bu kırık davranışı önlemek için ..
DX-MON

Test, verdiğiniz sayılar için çalışır (x: = 1, y: = 255, size = uint8_t): değer 0 (1 + 255) olur ve 0 <1 doğrudur. Gerçekten her sayı çifti için çalışır.
Gunther Piez

Hmm, iyi bir noktaya değindin. Herhangi bir iyi derleyici bunu optimize sağlayıcı olsa da yine de güvenliğin yanına sadık kalıyorum, sonuçta taşma olmayacak "0 + 4" gibi taşan sayılar da dahil olmak üzere tüm girişler için gerçekten doğru.
DX-MON

4
Taşma varsa, x+y>=256ve value=x+y-256. Çünkü y<256her zaman doğrudur, (y-256) negatiftir ve value < xher zaman doğrudur. Taşmayan kasanın kanıtı oldukça benzerdir.
Gunther Piez

2
@ DX-MON: Daha önceki bir eklentiden taşıma bitiniz varsa ilk yönteminiz gereklidir. 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]); }Bunu yapmazsanız ordeğerleri, bir tanesinde işlenen ayırt etmek mümkün olmayacaktır ve taşıma sıfır ve biri olma işlenen varlık bit 0xffffffffve taşıma biri olma ısırdı.
Matt

14

Taşma bayrağına C / C ++ 'dan erişemezsiniz.

Bazı derleyiciler koda tuzak talimatları eklemenize izin verir. GCC'de seçenek -ftrapv.

Yapabileceğiniz tek taşınabilir ve derleyici bağımsız şey, taşmaları kendi başınıza kontrol etmektir. Tıpkı sizin örneğinizde yaptığınız gibi.

Ancak, -ftrapven son GCC'yi kullanarak x86'da hiçbir şey yapmıyor gibi görünüyor. Sanırım eski bir versiyondan kalan veya başka bir mimariye özgü. Her derlemeden sonra derleyicinin INTO opcode eklemesini bekliyordum. Ne yazık ki bunu yapmıyor.


Belki de değişir: -ftrapv, bir Cygwin kutusunda GCC 4.3.4 kullanarak iyi çalışıyor gibi görünüyor. Stackoverflow.com/questions/5005379/…
Nate Kohl

3
İkiniz de haklısınız. -ftrapv işi sadece işaretli tamsayılar için yapmak
ZAB

14

İmzasız tamsayılar için, sonucun bağımsız değişkenlerden birinden küçük olduğundan emin olun:

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

İşaretli tamsayılar için argümanların ve sonucun işaretlerini kontrol edebilirsiniz.

Farklı işaretlerin tam sayıları taşamaz ve aynı işaretin tam sayıları yalnızca sonuç farklı bir işarete sahipse taşar:

signed int r, a, b, s;
r = a + b;
s = a>=0;
if (s == (b>=0) && s != (r>=0))
{
    // Overflow
}

İlk yöntem imzalı tamsayılar için de işe yarar, değil mi? char result = (char)127 + (char)3;-126; her iki işlenenden daha küçük.
primfaktor

1
Görüyorum ki sorun, imzalı türler için tanımsız olması.
primfaktor

27
-1 imzalı sayıların taşması tanımsız davranışa neden olur (bu nedenle test gerçekten yararlı olmak için çok geç).
Voo

1
@primfaktor işaretli int için çalışmaz: char ((- 127) + (-17)) = 112. İmzalı int için argümanların işaret sonucunu ve sonucunu kontrol etmelisiniz
phuclv

3
Daha önce de belirtildiği gibi, işaretli tam sayı çözümü, taşma durumunda a + b'nin tanımlanmamış davranışı nedeniyle çalışmaz. İmzalı tamsayı ile taşma denetleniyor gerekir ameliyattan önce yapılması.
Marwan Burelle

11

Aynı soruyu, bit maskeleme ve kaymanın umut verici görünmediği kayan nokta sayıları için cevaplamam gerekiyordu. Yerleştirdiğim yaklaşım, imzalı ve imzasız, tamsayı ve kayan nokta sayıları için çalışmaktadır. Ara hesaplamalar için teşvik edilecek daha büyük bir veri türü olmasa bile çalışır. Tüm bu türler için en verimli değildir, ancak hepsi için çalıştığı için kullanmaya değer.

İmzalı Taşma testi, Toplama ve Çıkarma:

  1. MAXVALUE ve MINVALUE türü için mümkün olan en büyük ve en küçük değerleri temsil eden sabitleri edinin.

  2. İşlenenlerin işaretlerini hesaplayın ve karşılaştırın.

    a. Her iki değer de sıfırsa, ne toplama ne de çıkarma taşamaz. Kalan testleri atlayın.

    b. İşaretler ters ise, ekleme taşamaz. Kalan testleri atlayın.

    c. İşaretler aynı ise, çıkarma taşamaz. Kalan testleri atlayın.

  3. MAXVALUE değerinde pozitif taşma testi yapın.

    a. Her iki işaret de pozitif ve MAXVALUE - A <B ise, ekleme taşacaktır.

    b. B işareti negatifse ve MAXVALUE - A <-B ise, çıkarma taşar.

  4. MINVALUE değerinde negatif taşma testi yapın.

    a. Her iki işaret de negatif ve MINVALUE - A> B ise, ekleme taşacaktır.

    b. A işareti negatif ve MINVALUE - A> B ise, çıkarma taşar.

  5. Aksi takdirde taşma olmaz.

İmzalı Taşma testi, Çarpma ve Bölme:

  1. MAXVALUE ve MINVALUE türü için mümkün olan en büyük ve en küçük değerleri temsil eden sabitleri edinin.

  2. İşlenenlerin büyüklüklerini (mutlak değerler) hesaplayın ve biriyle karşılaştırın. (Aşağıda, A ve B'nin imzalı orijinaller değil, bu büyüklükler olduğunu varsayalım.)

    a. İki değerden biri sıfırsa, çarpma taşamaz ve bölme sıfır veya sonsuz verir.

    b. Her iki değer de bir ise, çarpma ve bölme taşamaz.

    c. Bir işlenenin büyüklüğü birinin altında ve diğerinin büyüklüğü birden büyükse, çarpma taşamaz.

    d. Büyüklüklerin her ikisi birden birden küçükse, bölme taşamaz.

  3. MAXVALUE değerinde pozitif taşma testi yapın.

    a. Her iki işlenen de birden büyükse ve MAXVALUE / A <B ise, çarpma taşacaktır.

    b. B birden az ve MAXVALUE * B <A ise, bölme taşacaktır.

  4. Aksi takdirde taşma olmaz.

Not: MINVALUE minimum taşması 3 ile işlenir, çünkü mutlak değerler aldık. Ancak, ABS (MINVALUE)> MAXVALUE ise, bazı nadir yanlış pozitiflerimiz olacaktır.

Taşma testleri benzerdir, ancak EPSILON'u (sıfırdan büyük en küçük pozitif sayı) içerir.


1
POSIX sistemlerinde, en azından SIGFPE sinyali kayan nokta altı / taşmaları için etkinleştirilebilir.
Chris Johnson

Kayan nokta ve arka çalışmalara dönüştürülürken, (32 bitlik bir makinedeki testime göre) diğer çözümlerden çok daha yavaş.
JanKanis

Bir yorumcu çıkarma bölümü 2 için eksik bir durum tespit etti. 0 - MINVALUE değerinin taşacağını kabul ediyorum. Bu nedenle, bu vaka için test eklenmelidir.
Paul Chernoch

<pedantic> Tamsayılar taşmaz (= herhangi bir doğrulukla temsil edilemeyecek sıfıra çok yakın olur). 1.0e-200 / 1.0e200IEEE'nin iki katına çıktığı varsayılarak, gerçek bir yetersizliğin örneği olabilir. Buradaki doğru terim, bunun yerine, negatif taşmadır. </pedantic>
Arne Vogel

Kesin olmak gerekirse, tamsayıların taşma olarak değerlendirilmemesinin nedeni, tanımlanmış kesme davranışından kaynaklanır, örneğin 1/INT_MAXiyi taşma olarak düşünülebilir, ancak dil kesmeyi sıfıra zorlar.
Arne Vogel

8

CERT, "as-if" sonsuz aralıklı (AIR) tamsayı modelini kullanarak imzalı tamsayı taşması, imzasız tamsayı sarma ve tamsayı kesme algılaması ve raporlaması için yeni bir yaklaşım geliştirmiştir. CERT, modeli tanımlayan teknik bir rapor yayınladı ve GCC 4.4.0 ve GCC 4.5.0'a dayanan bir çalışma prototipi üretti.

AIR tamsayı modeli, sonsuz aralıklı tamsayılar kullanılarak elde edilen değerle eşdeğer bir değer üretir veya çalışma zamanı kısıtlaması ihlaline neden olur. Önceki tamsayı modellerinden farklı olarak, AIR tamsayıları kesin tuzaklar gerektirmez ve sonuç olarak mevcut optimizasyonların çoğunu bozmaz veya engellemez.


Bağlantıda yararlı bir şey görmedim, ama bu uzun zamandır savunduğum bir model gibi görünüyor. Yararlı optimizasyonların büyük çoğunluğunu desteklerken, çoğu uygulamanın temelde ücretsiz olarak sağlayabileceği faydalı anlam garantilerini de destekler. Kod, bir işleve girişlerin çıktının önemli olduğu tüm durumlarda geçerli olacağını biliyorsa, ancak çıktının önemli olup olmadığını önceden bilmiyorsa, hiçbir şeyi etkilemeyecekleri durumlarda taşmalara izin verilebilir ne pahasına olursa olsun onları önlemek zorunda daha kolay ve verimli.
supercat

8

Başka bir ilginç araç IOC: C / C ++ için bir Tamsayı Taşması Denetleyicisi .

Bu, derleme zamanında koda denetimler ekleyen yamalı bir Clang derleyicisidir.

Çıkışı şöyle görünür:

CLANG ARITHMETIC UNDEFINED at <add.c, (9:11)> :
Op: +, Reason : Signed Addition Overflow,
BINARY OPERATION: left (int32): 2147483647 right (int32): 1

1
Bu yama şimdi diğer dezenfektanlar arasında clang kod tabanına birleştirildi, cevabımı görün.
ZAB

7

Montaj dilini kullanan bir çözümün başka bir varyantı harici bir prosedürdür. Linux x64 altında g ++ ve fasm kullanarak imzasız tamsayı çarpımı için bu örnek.

Bu prosedür iki imzasız tamsayı bağımsız değişkenini (32 bit) çarpar ( amd64 belirtimine göre (bölüm 3.2.3 Parametre Geçişi ).

Sınıf INTEGER ise,% rdi,% rsi,% rdx,% rcx,% r8 ve% r9 dizisinin bir sonraki kullanılabilir kaydı kullanılır.

(edi ve esi koduma kaydeder)) ve bir taşma meydana geldiğinde sonucu veya 0 değerini döndürür.

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

Ölçek:

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;
}

Programı asm nesne dosyasıyla ilişkilendirin. Benim durumumda, Qt Creator'da , bunu LIBSbir .pro dosyasına ekleyin .


5

Sonuçları iki katına hesaplayın. 15 önemli basamakları var. İhtiyacınızın c 8 10 üzerinde sert bir üst sınırı vardır  - en fazla 8 basamak içerebilir. Bu nedenle, sonuç menzil içinde ise kesin olur ve aksi takdirde taşmaz.


5

32 bitlik makinelerin taşma bitini test etmek için bu makroyu deneyin (Angel Sinigersky'nin çözümünü uyarladı)

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

Bunu makro olarak tanımladım çünkü taşma bitinin üzerine yazılacaktı.

Sonraki kod bölümü yukarıdaki küçük bir uygulama:

#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);
}

4
Tüm 32-bit makineler Intel x86 uyumlu değildir ve tüm derleyiciler gnu assembly sözdizimini desteklemez ( _MSC_VERMS derlemelerinin tüm kodları reddedmesine rağmen hangi kodları test ettiğinizi komik buluyorum ).
Ben Voigt


2

Taşma bayrağına C / C ++ 'dan erişemezsiniz.

Buna katılmıyorum. Bazı satır içi montaj dili yazabilir ve jotaşmayı yakalamak için x86'da olduğunuzu varsayarak (atlama taşması) komutunu kullanabilirsiniz . Elbette, kodunuz artık diğer mimariler için taşınabilir olmayacaktır.

Bakın info asve info gcc.


8
satır içi montajcı C / C ++ özelliği ve platformdan bağımsız değildir. X86 üzerinde btw şubelerinin istead komutunu kullanabilirsiniz.
Nils Pipenbrinck

0

Head Geek'in cevabını genişletmek için bunu yapmanın daha hızlı bir yolu var 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 );
}

Bu, 64 bit ve 32 bit işaretsiz tam sayıların hala iyi çalışacağı için makine mimarisi kasası kullanır. Temel olarak, en önemli bit dışında tümünü maskeleyecek bir maske oluşturuyorum. Sonra, her iki tamsayıyı da maskelerim ve her ikisinde de bu bit seti yoksa, ekleme güvenlidir.

Asla değişmediği için maskeyi bazı kurucularda önceden başlatırsanız bu daha da hızlı olur.


5
Bu doğru değil. Taşıma, taşmaya neden olacak şekilde alt konumlardan uçlar getirebilir. Eklemeyi düşünün UINT_MAX + 1. Maskelemeden sonra a, yüksek bit ayarlanmış 1olacak , ancak sıfır olacak ve bu nedenle işlev geri dönecek true, ekleme güvenlidir - yine de doğrudan taşmaya yönelirsiniz.
domuz


-1

MSalter'in cevabı iyi bir fikir.

Tamsayı hesaplaması gerekiyorsa (kesinlik için), ancak kayan nokta varsa, şöyle bir şey yapabilirsiniz:

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
    }
}

Genellikle, hesaplamayı kayan noktada tekrarlamanın kötü bir fikir olduğunu söyleyebilirim, ancak bu özel üs alma durumu için ^ c, daha verimli olabilir. Ama test (c * log(a) < max_log)nerede olmalıconst double max_log = log(UINT_MAX)
Toby Speight

-1

X86 komut kümesi sonucu iki kayıt defterinde saklayan imzasız bir çarpma komutu içerir. Bu talimatı C'den kullanmak için 64 bit programa (GCC) aşağıdaki kod yazılabilir:

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;
}

32 bitlik bir program için, sonuç 64 bit ve parametreler 32 bit olmalıdır.

Alternatif olarak, bayrak kaydını kontrol etmek için derleyiciye bağlı içsel kullanmaktır. Taşma içsel için GCC belgeleri Taşma Kontrolü ile Aritmetik Gerçekleştirmek için 6.56 Yerleşik İşlevler'den bulunabilir .


1
__uint128İmzalı taşmayı ve sağa negatif bir değeri kaydırmayı önlemek için işaretsiz 128 bit türünü kullanmalısınız.
chqrlie

Nelerdir "derleyici bağımlı içgüdüleri" ve "taşma içgüdüleri" ? Şunu mu demek istediniz: " intrinsic function " ? Referansınız var mı? (Lütfen cevabınızı düzenleyerek yanıtlayın , burada yorumlarda değil (uygun şekilde).)
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);

    }
}

-3

Bunu yapmanın temiz bir yolu, tüm operatörleri (özellikle + ve *) geçersiz kılmak ve işlemleri gerçekleştirmeden önce taşma olup olmadığını kontrol etmektir.


6
Bunun dışında yerleşik türler için işleçleri geçersiz kılamazsınız. Bunun için bir sınıf yazmanız ve kullanmak için istemci kodunu yeniden yazmanız gerekir.
Blaisorblade

-3

Ne için kullandığınıza bağlıdır. İmzasız uzun (DWORD) toplama veya çarpma işlemi gerçekleştirirken en iyi çözüm ULARGE_INTEGER kullanmaktır.

ULARGE_INTEGER iki DWORD'den oluşan bir yapıdır. Yüksek DWORD'ye "HighPart" ve düşük DWORD'ye "LowPart" olarak erişilirken tam değere "QuadPart" olarak erişilebilir.

Örneğin:

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)

6
Ne yazık ki, bu yalnızca Windows için bir çözümdür. Diğer platformlarda yoktur ULARGE_INTEGER.
Gizemli

-3

Taşınabilir bir şekilde taşmadan imzalanmamış bir çarpma gerçekleştirmek için aşağıdakiler kullanılabilir:

... /* 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;
}

-4

Taşmayı test etmenin basit yolu, geçerli değerin önceki değerden düşük olup olmadığını kontrol ederek doğrulama yapmaktır. Örneğin, 2'nin gücünü yazdırmak için bir döngünüz olduğunu varsayalım:

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

Ben tarif ettiğim şekilde taşma denetimi ekleyerek bu sonuç:

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;
}

Hem pozitif hem de negatif işaretli değerlerin yanı sıra imzasız değerler için de çalışır.

Tabii ki, değerleri artırmak yerine değerleri azaltmak için benzer bir şey <=yapmak istiyorsanız >=, taşma davranışının taşma davranışı ile aynı olduğunu varsayarak işareti yapmak için işareti çevirirsiniz . Dürüst olmak gerekirse, bu, bir CPU'nun taşma bayrağına erişmeden elde edebileceğiniz kadar taşınabilir (ve satır içi montaj kodu gerektirecek ve kodunuzu yine de uygulamalar arasında taşınabilir olmayacak).


9
İmzalı bir değer aşılırsa, programınızın davranışı tanımsızdır. Etrafı sarması garanti edilmez.
David Stone
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.