Podane wartości liczb całkowitych x
oraz y
C i C ++ zwracają jako iloraz q = x/y
podłogi ekwiwalentu zmiennoprzecinkowego. Zamiast tego interesuje mnie metoda zwrotu sufitu. Na przykład ceil(10/5)=2
i ceil(11/5)=3
.
Oczywiste podejście obejmuje coś takiego:
q = x / y;
if (q * y < x) ++q;
Wymaga to dodatkowego porównania i pomnożenia; a inne metody, które widziałem (w rzeczywistości stosowały), polegały na rzucaniu jako a float
lub double
. Czy istnieje bardziej bezpośrednia metoda, która pozwala uniknąć dodatkowego mnożenia (lub drugiego podziału) i rozgałęzienia, a także pozwala uniknąć rzutowania jako liczby zmiennoprzecinkowej?
q = x/y + (x % y != 0);
wystarczyOdpowiedzi:
Dla liczb dodatnich
Aby zaokrąglić w górę ...
lub (unikając przepełnienia x + y)
źródło
x/y
jak pułap podziału. C90 nie określił, jak zaokrąglać, i nie sądzę, aby obecny standard C ++ również.q = x / y;
Dla liczb dodatnich:
źródło
Odpowiedź Sparky jest jednym ze standardowych sposobów rozwiązania tego problemu, ale jak napisałem również w moim komentarzu, istnieje ryzyko przepełnienia. Można to rozwiązać za pomocą szerszego typu, ale co jeśli chcesz podzielić
long long
s?Odpowiedź Nathana Ernsta zapewnia jedno rozwiązanie, ale wymaga wywołania funkcji, deklaracji zmiennej i warunku, co czyni go nie mniejszym niż kod OP i prawdopodobnie nawet wolniejszym, ponieważ trudniej go zoptymalizować.
Moje rozwiązanie jest następujące:
Będzie to nieco szybsze niż kod OPs, ponieważ modulo i podział są wykonywane przy użyciu tej samej instrukcji procesora, ponieważ kompilator widzi, że są one równoważne. Przynajmniej gcc 4.4.1 wykonuje tę optymalizację z flagą -O2 na x86.
Teoretycznie kompilator może wstawiać wywołanie funkcji w kodzie Nathana Ernsta i emitować to samo, ale gcc tego nie zrobił, kiedy go testowałem. Może tak być, ponieważ wiązałoby skompilowany kod z jedną wersją standardowej biblioteki.
Na koniec, żadna z tych kwestii nie ma znaczenia na nowoczesnym komputerze, z wyjątkiem sytuacji, gdy jesteś w bardzo ciasnej pętli, a wszystkie twoje dane znajdują się w rejestrach lub pamięci podręcznej L1. W przeciwnym razie wszystkie te rozwiązania będą równie szybkie, z wyjątkiem ewentualnie Nathana Ernsta, które mogą być znacznie wolniejsze, jeśli funkcja musi zostać pobrana z pamięci głównej.
źródło
q = (x > 0)? 1 + (x - 1)/y: (x / y);
q = x / y + (x % y > 0);
to jest łatwiejsze niż? :
wyrażenie?Możesz użyć
div
funkcji w cstdlib, aby uzyskać iloraz i resztę w jednym wywołaniu, a następnie osobno obsłużyć sufit, jak w poniższymźródło
return res.quot + !!res.rem;
:)Co powiesz na to? (wymaga y nieujemnego, więc nie używaj tego w rzadkim przypadku, gdy y jest zmienną bez gwarancji nieujemności)
Zmniejszyłem
y/y
do jednego, eliminując termin,x + y - 1
a wraz z nim wszelkie szanse na przepełnienie.Unikam
x - 1
zawijania, gdyx
jest to typ bez znaku i zawiera zero.W przypadku znaku
x
, ujemne i zero nadal łączą się w jeden przypadek.Prawdopodobnie nie jest to ogromna zaleta nowoczesnego procesora ogólnego zastosowania, ale byłoby to znacznie szybsze w systemie wbudowanym niż jakakolwiek inna poprawna odpowiedź.
źródło
Istnieje rozwiązanie zarówno pozytywne, jak i negatywne,
x
ale tylko pozytywney
z tylko 1 dywizją i bez rozgałęzień:Zauważ, że jeśli
x
jest dodatni, wówczas podział jest w kierunku zera i powinniśmy dodać 1, jeśli przypomnienie nie jest zerem.Jeśli
x
jest ujemne, wówczas podział jest w kierunku zera, to jest to, czego potrzebujemy i nie dodamy niczego, ponieważx % y
nie jest dodatnieźródło
Działa to dla liczb dodatnich lub ujemnych:
Jeśli pozostała część, sprawdź, czy
x
iy
są tego samego znaku, i1
odpowiednio dodaje .źródło
Wolałbym skomentować, ale nie mam wystarczająco wysokiego przedstawiciela.
O ile mi wiadomo, dla pozytywnych argumentów i dzielnika, który jest potęgą 2, jest to najszybszy sposób (testowany w CUDA):
Tylko w przypadku ogólnych argumentów pozytywnych zwykle robię to tak:
źródło
q = x/y + !!(x % y);
radzi sobieq = x/y + (x % y == 0);
i jakie sąq = (x + y - 1) / y;
rozwiązania pod względem wydajności we współczesnej CUDA.uproszczona forma ogólna,
Aby uzyskać bardziej ogólną odpowiedź, funkcje C ++ do dzielenia liczb całkowitych z dobrze zdefiniowaną strategią zaokrąglania
źródło
Kompiluj z O3, kompilator dobrze wykonuje optymalizację.
źródło