Pisałem program w C ++, aby znaleźć wszystkie rozwiązania z b = c , gdzie , b i c razem wykorzystać wszystkie cyfry 0-9 dokładnie raz. Program zwracane nad wartości i B , i pobiegł rutynowej cyfra liczenia za każdym razem na o , b i a , b , aby sprawdzić, czy warunek ten został spełniony, cyfry.
Jednakże, uboczne rozwiązania mogą być generowane podczas b przelewa limit całkowitej. Skończyło się na sprawdzeniu tego za pomocą kodu, takiego jak:
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
Czy istnieje lepszy sposób testowania przepełnienia? Wiem, że niektóre układy mają wewnętrzną flagę, która jest ustawiana, gdy nastąpi przepełnienie, ale nigdy nie widziałem, aby można było uzyskać do niej dostęp za pośrednictwem C lub C ++.
Uważaj, że podpisane int
przepełnienie jest niezdefiniowanym zachowaniem w C i C ++ , a zatem musisz je wykryć bez powodowania go. Aby zapoznać się z przepełnieniem podpisu int przed dodaniem, zobacz Wykrywanie przepełnienia podpisu w C / C ++ .
źródło
-ftrapv
spowoduje, że wygeneruje SIGABRT na (podpisanym) przepełnieniu liczb całkowitych. Zobacz tutaj .clz
instrukcji lub__clz(unsigned)
funkcji do ustalenia rangi liczby (gdzie jest jej najwyższy bit). Ponieważ nie jestem pewien, czy jest on dostępny na x86 lub x64, założę się, że tak nie jest i powiem, że znalezienie najbardziej znaczącego bitu zajmie najgorszelog(sizeof(int)*8)
instrukcje.Odpowiedzi:
Widzę, że używasz liczb całkowitych bez znaku. Z definicji w C (nie wiem o C ++) arytmetyka bez znaku nie przepełnia się ... więc, przynajmniej w przypadku C, masz rację:
W przypadku liczb całkowitych ze znakiem, gdy wystąpi przepełnienie, wystąpiło niezdefiniowane zachowanie (UB), a program może zrobić wszystko (na przykład: renderować testy niejednoznaczne).
Aby utworzyć zgodny program, musisz przetestować przepełnienie przed wygenerowaniem wspomnianego przepełnienia. Metodę można również stosować z liczbami całkowitymi bez znaku:
Do podziału (z wyjątkiem
INT_MIN
i-1
szczególny przypadek), nie ma żadnej możliwości podchodzącINT_MIN
lubINT_MAX
.źródło
x >= 0
-x > 0
wystarczy (jeślix == 0
, tox + a
z oczywistych powodów nie może się przepełnić).if ((a < INT_MIN / x))
test jest za późno.if (x == -1)
Badanie jest pierwszym potrzebne.Nie jest to sposób, aby określić, czy operacja jest zlecenie przelewu, wykorzystując pozycje najbardziej znaczących bitów w jednym z argumentów i trochę podstawowej wiedzy binarnie matematyki.
Dodatkowo dwa dowolne operandy spowodują (najwyżej) jeden bit więcej niż najwyższy jeden największy argument. Na przykład:
W przypadku mnożenia dowolne dwa operandy będą skutkowały (co najwyżej) sumą bitów operandów. Na przykład:
Podobnie możesz oszacować maksymalny rozmiar wyniku
a
do potęgib
podobnej:(Oczywiście zamień liczbę bitów na docelową liczbę całkowitą).
Nie jestem pewien najszybszego sposobu określenia pozycji najwyższego bitu w liczbie, oto metoda brute-force:
To nie jest idealne, ale da ci to dobry pomysł, czy jakieś dwie liczby mogą się przepełnić przed wykonaniem operacji. Nie wiem, czy byłoby to szybsze niż zwykłe sprawdzenie wyniku w sposób sugerowany przez ciebie z powodu pętli w
highestOneBitPosition
funkcji, ale może (zwłaszcza jeśli wcześniej wiedziałeś, ile bitów było w operandach).źródło
log2
, ale niekoniecznie byłoby to tak oczywiste dla kogoś, kto nie miał matematycznego zaplecza.multiplication_is_safe
0x8000 * 0x10000
przepełnieniem (pozycje bitów to 16 + 17 = 33, co jest > 32 ), chociaż tak nie jest, ponieważ0x8000 * 0x10000 = 0x80000000
które oczywiście nadal pasują do 32-bitowej liczby całkowitej bez znaku. To tylko jeden z wielu przykładów, dla których ten kod nie działa.0x8000 * 0x10001
, ...0x8000 * 0x10000
według tej definicji nie jest „bezpieczny”, nawet jeśli okazuje się być w porządku.Clang 3.4+ i GCC 5+ oferują sprawdzone wbudowane arytmetyki. Oferują one bardzo szybkie rozwiązanie tego problemu, szczególnie w porównaniu z testami bezpieczeństwa do testów bitowych.
Na przykład w pytaniu OP działałoby to tak:
Dokumentacja Clanga nie określa, czy
c_test
zawiera przepełniony wynik w przypadku przepełnienia, ale dokumentacja GCC mówi, że tak. Biorąc pod uwagę, że ci dwaj lubią być__builtin
kompatybilni, prawdopodobnie bezpiecznie jest założyć, że tak właśnie działa Clang.__builtin
Dla każdej operacji arytmetycznej istnieje przepełnienie (dodawanie, odejmowanie, mnożenie) z podpisanymi i niepodpisanymi wariantami dla liczb całkowitych, długich i długich. Składnia nazwy to__builtin_[us](operation)(l?l?)_overflow
:u
dla niepodpisane lubs
za podpisane ;add
,sub
lubmul
;l
przyrostka oznacza, że operandy sąint
s; jedenl
oznaczalong
; dwal
s oznaczająlong long
.Tak byłoby w przypadku sprawdzonego dodawania długiej liczby całkowitej
__builtin_saddl_overflow
. Pełna lista znajduje się na stronie dokumentacji Clanga .GCC 5+ i Clang 3.8+ dodatkowo oferują rodzajowe pomocy poleceń wbudowanych, że praca bez określania typu wartości:
__builtin_add_overflow
,__builtin_sub_overflow
i__builtin_mul_overflow
. Działają również na typach mniejszych niżint
.Wbudowane są niższe niż najlepsze dla platformy. Na x86 sprawdzają flagi przenoszenia, przepełnienia i podpisują flagi.
Plik cl.exe programu Visual Studio nie ma bezpośrednich odpowiedników. W przypadku niepodpisanych dodatków i odejmowań, w tym
<intrin.h>
pozwoli ci użyćaddcarry_uNN
isubborrow_uNN
(gdzie NN jest liczbą bitów, jakaddcarry_u8
lubsubborrow_u64
). Ich podpis jest nieco tępy:c_in
/b_in
oznacza flagę carry / borrow na wejściu, a wartością zwracaną jest carry / borrow na wyjściu. Wygląda na to, że nie ma odpowiedników dla podpisanych operacji lub mnożenia.W przeciwnym razie Clang dla Windows jest teraz gotowy do produkcji (wystarczająco dobry dla Chrome), więc może to być również opcja.
źródło
__builtin_sub_overflow
zdecydowanie nie jest w Clang 3.4.__builtin_add_overflow
i przyjaciół powinno być już dostępne w Clang 3.8.Niektóre kompilatory zapewniają dostęp do flagi przepełnienia liczb całkowitych w CPU, którą można następnie przetestować, ale nie jest to standard.
Możesz także przetestować możliwość przepełnienia przed wykonaniem mnożenia:
źródło
b == ULONG_MAX / a
? Wtedy może nadal pasować, biorąc pod uwagę, żea
dzieli sięULONG_MAX
bez resztek.Ostrzeżenie: GCC może zoptymalizować kontrolę przepełnienia podczas kompilacji
-O2
. Ta opcja-Wall
wyświetli ostrzeżenie w niektórych przypadkach, takich jakale nie w tym przykładzie:
Jedynym bezpiecznym sposobem jest sprawdzenie przepełnienia przed jego wystąpieniem, jak opisano w dokumencie CERT , a jego systematyczne stosowanie byłoby niezwykle uciążliwe.
Kompilacja z
-fwrapv
rozwiązuje problem, ale wyłącza niektóre optymalizacje.Desperacko potrzebujemy lepszego rozwiązania. Myślę, że kompilator powinien domyślnie wyświetlać ostrzeżenie podczas przeprowadzania optymalizacji, która opiera się na braku przepełnienia. Obecna sytuacja pozwala kompilatorowi zoptymalizować kontrolę przepełnienia, co moim zdaniem jest niedopuszczalne.
źródło
for(int k = 0; k < 5; k++) {...}
powinno się ostrzec?k
można łatwo ustalić w czasie kompilacji. Kompilator nie musi przyjmować żadnych założeń.n
jest mniejsza niż 32, przed wysłaniem instrukcji shift, która wykorzystuje tylko 5 niższych bitówn
?Clang obsługuje teraz dynamiczne kontrole przepełnienia zarówno dla liczb całkowitych ze znakiem, jak i bez znaku. Zobacz przełącznik -fsanitize = liczba całkowita . Na razie jest to jedyny kompilator C ++ z całkowicie obsługiwanym dynamicznym sprawdzaniem przepełnienia do celów debugowania.
źródło
Widzę, że wiele osób odpowiedziało na pytanie o przepełnienie, ale chciałem rozwiązać jego pierwotny problem. Powiedział, że problemem jest znalezienie b = c, tak aby wszystkie cyfry były używane bez powtarzania. Ok, nie o to pytał w tym poście, ale nadal uważam, że konieczne było przestudiowanie górnej granicy problemu i wyciągnięcie wniosku, że nigdy nie będzie musiał obliczać ani wykrywać przepełnienia (uwaga: nie jestem biegły z matematyki, więc zrobiłem to krok po kroku, ale wynik końcowy był tak prosty, że może mieć prostą formułę).
Głównym punktem jest to, że górna granica, której wymaga problem dla a, b lub c, wynosi 98.765.432. W każdym razie, zaczynając od podzielenia problemu na trywialne i nietrywialne części:
Teraz musimy tylko pokazać, że żadne inne rozwiązanie nie jest możliwe, a poprawne są tylko permutacje (a następnie kod do ich wydrukowania jest trywialny). Wracamy do górnej granicy. W rzeczywistości górna granica wynosi c ≤ 98,765,432. Jest to górna granica, ponieważ jest to największa liczba z 8 cyframi (łącznie 10 cyfr minus 1 dla każdego a i b). Ta górna granica jest tylko dla c, ponieważ granice aib muszą być znacznie niższe z powodu wykładniczego wzrostu, jak możemy obliczyć, zmieniając b od 2 do górnej granicy:
Zauważ, na przykład ostatnia linia: mówi, że 1,97 ^ 27 ~ 98M. Na przykład 1 ^ 27 == 1 i 2 ^ 27 == 134.217.728 i to nie jest rozwiązanie, ponieważ ma 9 cyfr (2> 1,97, więc jest faktycznie większe niż to, co powinno być przetestowane). Jak widać, kombinacje dostępne do testowania aib są naprawdę małe. Dla b == 14 musimy spróbować 2 i 3. Dla b == 3 zaczynamy od 2 i zatrzymujemy się na 462. Wszystkie wyniki są mniejsze niż ~ 98 mln.
Teraz wystarczy przetestować wszystkie powyższe kombinacje i poszukać tych, które nie powtarzają żadnych cyfr:
Żadne z nich nie pasuje do problemu (co można również zobaczyć przez brak „0”, „1”, ..., „9”).
Przykładowy kod, który go rozwiązuje, jest następujący. Zauważ też, że jest napisane w Pythonie, nie dlatego, że wymaga liczb całkowitych o dowolnej precyzji (kod nie oblicza niczego większego niż 98 milionów), ale ponieważ odkryliśmy, że liczba testów jest tak mała, że powinniśmy używać języka wysokiego poziomu, aby korzystaj z wbudowanych kontenerów i bibliotek (uwaga: kod ma 28 wierszy).
źródło
Oto „nieprzenośne” rozwiązanie tego pytania. Procesory Intel x86 i x64 mają tak zwany rejestr EFLAGS , który jest wypełniany przez procesor po każdej operacji arytmetycznej na liczbach całkowitych. Pomiń tutaj szczegółowy opis. Odpowiednimi flagami są flaga „Przepełnienie” (maska 0x800) i flaga „Przenoszenie” (maska 0x1). Aby interpretować je poprawnie, należy rozważyć, czy operandy są typu podpisanego czy niepodpisanego.
Oto praktyczny sposób sprawdzenia flag z C / C ++. Poniższy kod będzie działał na Visual Studio 2005 lub nowszym (zarówno 32-bitowym, jak i 64-bitowym), a także na GNU C / C ++ 64-bitowym.
Gdyby argumenty zostały zwielokrotnione bez przepełnienia, otrzymalibyśmy wartość 0 od
query_intel_eflags(0x801)
, tzn. Nie ustawiono flag przenoszenia ani przepełnienia. W podanym przykładowym kodzie main () występuje przepełnienie, a obie flagi są ustawione na 1. To sprawdzenie nie oznacza żadnych dalszych obliczeń, więc powinno być dość szybkie.źródło
Jeśli masz typ danych większy niż ten, który chcesz przetestować (powiedzmy, że dodajesz 32-bit i masz typ 64-bitowy), to wykryje, czy nastąpiło przepełnienie. Mój przykład dotyczy dodawania 8-bitowego. Ale można go zwiększyć.
Opiera się na pojęciach wyjaśnionych na tej stronie: http://www.cs.umd.edu/class/spring2003/cmsc311/Notes/Comb/overflow.html
Dla przykładu 32-bitowego
0xFF
staje się0xFFFFFFFF
i0x80
staje się,0x80000000
a w końcuuint16_t
staje sięuint64_t
.UWAGA : wychwytuje to przepełnienie dodawania / odejmowania liczb całkowitych i zdałem sobie sprawę, że twoje pytanie obejmuje mnożenie. W takim przypadku podział jest prawdopodobnie najlepszym podejściem. Jest to zwykle sposób, w jaki
calloc
implementacje upewniają się, że parametry nie przepełniają się, ponieważ są mnożone w celu uzyskania ostatecznego rozmiaru.źródło
Najprostszym sposobem jest konwersja
unsigned long
s naunsigned long long
s, pomnożenie i porównanie wyniku do 0x100000000LL.Prawdopodobnie przekonasz się, że jest to bardziej wydajne niż dzielenie, jak w przykładzie.
Aha, i będzie działać zarówno w C, jak i C ++ (jak oznaczyłeś pytanie w obu).
Właśnie oglądałem instrukcję glibc . Jest tam wzmianka o pułapce przepełnienia liczb całkowitych (
FPE_INTOVF_TRAP
) jako częściSIGFPE
. Byłoby idealnie, oprócz nieprzyjemnych fragmentów instrukcji:Trochę wstydu.
źródło
ULONG_MAX
który jest łatwiejszy do pisania i bardziej przenośny niż kodowanie na stałe0x100000000
.long
ilong long
są tej samej wielkości (np na wielu 64-bitowych kompilatorów).Oto naprawdę szybki sposób na wykrycie przepełnienia przynajmniej dla dodatków, co może dać przewagę w mnożeniu, dzieleniu i sile.
Chodzi o to, że właśnie dlatego, że procesor po prostu pozwoli wartości zawinąć się do zera, a C / C ++ jest wyodrębniany z dowolnego konkretnego procesora, możesz:
To zapewnia, że jeśli jeden argument jest równy zero, a drugi nie, to przepełnienie nie zostanie fałszywie wykryte i jest znacznie szybsze niż wiele operacji NOT / XOR / AND / testowych, jak wcześniej sugerowano.
Jak wskazano, takie podejście, choć lepsze niż inne bardziej skomplikowane sposoby, jest nadal możliwe do zoptymalizowania. Poniżej znajduje się wersja oryginalnego kodu zawierającego optymalizację:
Bardziej wydajnym i tanim sposobem na wykrycie przepełnienia jest:
Powoduje to albo UINT32_MAX przy przepełnieniu, albo wynik mnożenia. W tym przypadku jest to ściśle niezdefiniowane zachowanie, aby umożliwić kontynuowanie mnożenia dla podpisanych liczb całkowitych.
źródło
x+y>=256
ivalue=x+y-256
. Ponieważy<256
zawsze jest prawdziwe, (y-256) jest ujemne, a więcvalue < x
zawsze jest prawdziwe. Dowód na nie przepełnioną sprawę jest dość podobny.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]); }
Jeśli nie podaszor
wartości, nie będziesz w stanie odróżnić jednego argumentu operacji od bitu przenoszenia będącego zerem i jednego argumentu operacji0xffffffff
od bitu przenoszenia będącego jednym.Nie możesz uzyskać dostępu do flagi przepełnienia z C / C ++.
Niektóre kompilatory pozwalają wstawiać instrukcje pułapek do kodu. Na GCC jest to opcja
-ftrapv
.Jedyną przenośną i niezależną od kompilatora rzeczą, którą możesz zrobić, jest samodzielne sprawdzenie przepełnienia. Tak jak w twoim przykładzie.
-ftrapv
Wydaje się jednak, że nic nie robi na x86 przy użyciu najnowszego GCC. Sądzę, że jest to pozostałość po starej wersji lub specyficzna dla innej architektury. Spodziewałem się, że kompilator wstawi kod operacji INTO po każdym dodaniu. Niestety tego nie robi.źródło
W przypadku liczb całkowitych bez znaku sprawdź, czy wynik jest mniejszy niż jeden z argumentów:
W przypadku liczb całkowitych ze znakiem można sprawdzić znaki argumentów i wyniku.
Liczby całkowite różnych znaków nie mogą się przepełnić, a liczby całkowite tego samego znaku są przepełnione tylko wtedy, gdy wynik jest innego znaku:
źródło
char result = (char)127 + (char)3;
byłoby -126; mniejsze niż oba operandy.Musiałem odpowiedzieć na to samo pytanie dla liczb zmiennoprzecinkowych, w których maskowanie i przesuwanie bitów nie wygląda obiecująco. Podejście, na którym się zdecydowałem, dotyczy prac dla liczb podpisanych i niepodpisanych, liczb całkowitych i zmiennoprzecinkowych. Działa nawet wtedy, gdy nie ma większego typu danych do promowania w obliczeniach pośrednich. Nie jest najbardziej wydajny dla wszystkich tych typów, ale ponieważ działa na wszystkie, warto go użyć.
Podpisany test przepełnienia, dodawanie i odejmowanie:
Uzyskaj stałe reprezentujące największe i najmniejsze możliwe wartości dla typu, MAXVALUE i MINVALUE.
Oblicz i porównaj znaki operandów.
za. Jeśli którakolwiek wartość jest równa zero, to ani dodawanie, ani odejmowanie nie może się przelać. Pomiń pozostałe testy.
b. Jeśli znaki są przeciwne, dodanie nie może się przelać. Pomiń pozostałe testy.
do. Jeśli znaki są takie same, odejmowanie nie może się przelać. Pomiń pozostałe testy.
Test na dodatnie przepełnienie wartości MAXVALUE.
za. Jeśli oba znaki są dodatnie i MAKSYMALNA - A <B, wówczas dodawanie przepełni się.
b. Jeśli znak B jest ujemny, a MAXVALUE - A <-B, wówczas odejdzie przepełnienie.
Test pod kątem ujemnego przepełnienia MINVALUE.
za. Jeśli oba znaki są ujemne, a WARTOŚĆ MINIMALNA - A> B, wówczas dodawanie przepełni się.
b. Jeśli znak A jest ujemny, a WARTOŚĆ MINOWA - A> B, wówczas odejdzie przepełnienie.
W przeciwnym razie nie ma przelewu.
Podpisany test przepełnienia, mnożenie i dzielenie:
Uzyskaj stałe reprezentujące największe i najmniejsze możliwe wartości dla typu, MAXVALUE i MINVALUE.
Oblicz i porównaj wielkości (wartości bezwzględne) operandów z jednym. (Poniżej załóżmy, że A i B to te wielkości, a nie podpisane oryginały).
za. Jeśli jedna z wartości wynosi zero, mnożenie nie może się przepełnić, a dzielenie da zero lub nieskończoność.
b. Jeśli jedna z wartości jest równa jeden, mnożenie i dzielenie nie mogą się przepełnić.
do. Jeśli wielkość jednego operandu jest mniejsza niż jeden, a drugiego jest większa niż jeden, mnożenie nie może się przepełnić.
re. Jeśli zarówno wielkości są mniejsze niż jeden, podział nie może się przepełnić.
Test na dodatnie przepełnienie wartości MAXVALUE.
za. Jeśli oba argumenty są większe niż jeden i MAXVALUE / A <B, mnożenie się przepełni.
b. Jeśli B jest mniejsze niż jeden, a MAXVALUE * B <A, podział zostanie przepełniony.
W przeciwnym razie nie ma przelewu.
Uwaga: Minimalne przepełnienie MINVALUE jest obsługiwane przez 3, ponieważ przyjęliśmy wartości bezwzględne. Jeśli jednak ABS (MINWARTOŚĆ)> MAXWARTOŚĆ, będziemy mieli rzadkie fałszywe wyniki dodatnie.
Testy niedomiaru są podobne, ale obejmują EPSILON (najmniejsza liczba dodatnia większa od zera).
źródło
1.0e-200 / 1.0e200
byłby przykładem faktycznego niedomiaru, zakładając, że IEEE podwaja się. Zamiast tego poprawnym terminem jest przepełnienie ujemne. </pedantic>1/INT_MAX
Można je uznać za niedopełnienie, ale język po prostu nakazuje obcięcie do zera.Firma CERT opracowała nowe podejście do wykrywania i zgłaszania przekroczenia liczby podpisanych liczb całkowitych, owijania liczb całkowitych bez znaku i obcinania liczb całkowitych, wykorzystując model liczb całkowitych „jak gdyby” z nieograniczonym zasięgiem (AIR). CERT opublikował raport techniczny opisujący model i opracował działający prototyp oparty na GCC 4.4.0 i GCC 4.5.0.
Model liczb całkowitych AIR generuje wartość równoważną wartości, która zostałaby uzyskana przy użyciu liczb całkowitych o nieograniczonym zakresie, lub powoduje naruszenie ograniczenia środowiska wykonawczego. W przeciwieństwie do poprzednich modeli liczb całkowitych, liczby całkowite AIR nie wymagają precyzyjnych pułapek, w związku z czym nie psują ani nie hamują większości istniejących optymalizacji.
źródło
Innym interesującym narzędziem jest IOC: Integer Overflow Checker for C / C ++ .
Jest to poprawiony kompilator Clanga , który dodaje kontrole do kodu w czasie kompilacji.
Otrzymasz wynik wyglądający tak:
źródło
Innym wariantem rozwiązania wykorzystującego język asemblera jest procedura zewnętrzna. Ten przykład mnożenia liczb całkowitych bez znaku za pomocą g ++ i fasm pod Linux x64.
Ta procedura zwielokrotnia dwa argumenty liczb całkowitych bez znaku (32 bity) (zgodnie ze specyfikacją dla amd64 (sekcja 3.2.3 Przekazywanie parametrów ).
(edi i esi rejestrują się w moim kodzie)) i zwraca wynik lub 0, jeśli wystąpiło przepełnienie.
Test:
Połącz program z plikiem obiektu asm. W moim przypadku w Qt Creator dodaj go do
LIBS
pliku .pro.źródło
Oblicz wyniki z podwójnymi. Mają 15 cyfr znaczących. Twoje wymaganie ma twardą górną granicę c na 10 8 - może mieć maksymalnie 8 cyfr. W związku z tym wynik będzie dokładny, jeśli znajdzie się w zakresie, i w przeciwnym razie nie przepełni się.
źródło
Wypróbuj to makro, aby przetestować bit przepełnienia 32-bitowych maszyn (dostosował rozwiązanie Angel Sinigersky)
Zdefiniowałem go jako makro, ponieważ w przeciwnym razie bit przepełnienia zostałby nadpisany.
Kolejna jest mała aplikacja z powyższym segmentem kodu:
źródło
_MSC_VER
chociaż wszystkie kompilacje MS odrzucą kod).Przechwytywanie przepełnień liczb całkowitych w C wskazuje na rozwiązanie bardziej ogólne niż to omówione przez CERT (jest bardziej ogólne pod względem obsługiwanych typów), nawet jeśli wymaga pewnych rozszerzeń GCC (nie wiem, jak szeroko są obsługiwane).
źródło
Nie zgadzam się z tym. Możesz napisać wbudowany język asemblera i użyć
jo
instrukcji (przepełnienie skoku), zakładając, że jesteś na x86, aby przechwycić przepełnienie. Oczywiście twój kod nie byłby już przenośny dla innych architektur.Spójrz na
info as
iinfo gcc
.źródło
Aby rozwinąć odpowiedź Head Geek, istnieje szybszy sposób na zrobienie tego
addition_is_safe
;Korzysta z bezpiecznej architektury maszyn, ponieważ 64-bitowe i 32-bitowe liczby całkowite bez znaku będą nadal działać poprawnie. Zasadniczo tworzę maskę, która zamaskuje wszystko oprócz najbardziej znaczącego fragmentu. Następnie maskuję obie liczby całkowite, a jeśli któryś z nich nie ma ustawionego tego bitu, dodawanie jest bezpieczne.
Byłoby to jeszcze szybsze, jeśli wstępnie zainicjujesz maskę w jakimś konstruktorze, ponieważ nigdy się nie zmienia.
źródło
UINT_MAX + 1
. Po maskowaniua
zostanie ustawiony wysoki bit, ale1
zmieni się na zero, a zatem funkcja powrócitrue
, dodawanie jest bezpieczne - ale jesteś skierowany bezpośrednio do przepełnienia.mozilla::CheckedInt<T>
zapewnia sprawdzanie przepełnienia matematyki liczb całkowitych dla typu liczb całkowitychT
(przy użyciu wbudowanych kompilatorów w clang i gcc, jeśli są dostępne). Kod jest pod MPL 2.0 i zależy od trzech (IntegerTypeTraits.h
,Attributes.h
iCompiler.h
) inne tylko nagłówek niestandardowe nagłówki biblioteki plus Mozilla specyficzne maszyny twierdzenie . Prawdopodobnie chcesz wymienić maszynę do potwierdzania, jeśli zaimportujesz kod.źródło
Odpowiedź MSaltera to dobry pomysł.
Jeśli wymagane jest obliczanie liczb całkowitych (dla precyzji), ale dostępny jest zmiennoprzecinkowy, możesz zrobić coś takiego:
źródło
(c * log(a) < max_log)
, gdzieconst double max_log = log(UINT_MAX)
Zestaw instrukcji x86 zawiera niepodpisaną instrukcję mnożenia, która przechowuje wynik w dwóch rejestrach. Aby skorzystać z tej instrukcji z C, można napisać następujący kod w 64-bitowym programie (GCC):
W przypadku programu 32-bitowego wynik musi być 64-bitowy, a parametry 32-bitowe.
Alternatywą jest użycie funkcji wewnętrznej zależnej od kompilatora do sprawdzenia rejestru flag. Dokumentację GCC wewnętrzną przepełnienia można znaleźć w 6.56 Wbudowanych funkcji do wykonywania arytmetyki z kontrolą przepełnienia .
źródło
__uint128
aby uniknąć przepełnienia ze znakiem i przesunięcia w prawo wartości ujemnej.źródło
Czystym sposobem na to byłoby zastąpienie wszystkich operatorów (w szczególności + i *) i sprawdzenie, czy nie występuje przepełnienie przed wykonaniem operacji.
źródło
To zależy do czego go używasz. Dokonując dodawania lub mnożenia bez znaku długiego (DWORD), najlepszym rozwiązaniem jest użycie ULARGE_INTEGER.
ULARGE_INTEGER jest strukturą dwóch DWORD. Dostęp do pełnej wartości można uzyskać jako „QuadPart”, podczas gdy wysoki DWORD jest dostępny jako „HighPart”, a niski DWORD jest dostępny jako „LowPart”.
Na przykład:
źródło
ULARGE_INTEGER
.Aby wykonać mnożenie bez znaku bez przepełnienia w przenośny sposób, można użyć:
źródło
Prostym sposobem na sprawdzenie przepełnienia jest sprawdzenie poprawności poprzez sprawdzenie, czy bieżąca wartość jest mniejsza niż poprzednia. Załóżmy na przykład, że masz pętlę do wydrukowania potęg 2:
Dodanie przelewu sprawdzającego sposób, w jaki opisałem, powoduje:
Działa z wartościami bez znaku, a także z wartościami dodatnimi i ujemnymi.
Oczywiście, jeśli chciałbyś zrobić coś podobnego dla zmniejszania wartości zamiast zwiększania wartości, odwróciłbyś
<=
znak, aby to zrobić>=
, zakładając, że zachowanie niedomiaru jest takie samo, jak zachowanie przelania. Szczerze mówiąc, jest to tak przenośne, jak to możliwe, bez dostępu do flagi przepełnienia procesora (i to wymagałoby wbudowanego kodu asemblowania, co sprawiłoby, że kod nie byłby przenośny między implementacjami).źródło