2013-06-09 11 views
10

Potrzebuję podzielić dwie liczby i zaokrąglić je w górę. Czy istnieje lepszy sposób na zrobienie tego?Dzielenie dwóch liczb całkowitych i zaokrąglanie wyniku bez użycia zmiennoprzecinkowej

int myValue = (int) ceil((float)myIntNumber/myOtherInt); 

Znajduję przesadę, aby rzucić dwa razy. (Extern int obsada jest po prostu wyłączyć ostrzeżenie)

Uwaga muszę rzucać wewnętrznie unosić inaczej

int a = ceil(256/11); //> Should be 24, but it is 23 
       ^example 
+2

wątpię ktoś ma zamiar przedstawić rozwiązanie, które jest mniej brzydki. To, co masz, jest najbardziej czytelnym sposobem na zrobienie tego. Zaczekaj, w rzeczywistości nie używasz literałów w swoim prawdziwym kodzie? Jeśli tak, to po prostu ustaw je jako literały zmiennoprzecinkowe. W przeciwnym razie, odlewanie jest drogą do zrobienia. –

+6

Powinieneś użyć 'double' zamiast' float', chyba że masz pewność, że nigdy nie będziesz mieć liczb większych niż 16777216. – celtschk

+2

@ yes123 nie chcę ci przeszkadzać, ale co masz na myśli przez elegancki, twój jest bardzo łatwy do przeczytania, ale te, które zamieściłem, mogą być bardziej mathy i być może szybsze. – aaronman

Odpowiedz

9

Z pomocą DYP, wpadł na poniższym branchless wzoru:

int idiv_ceil (int numerator, int denominator) 
{ 
    return numerator/denominator 
      + (((numerator < 0)^(denominator > 0)) && (numerator%denominator)); 
} 

Unika konwersje zmiennoprzecinkowe i przechodzi podstawowy zestaw testów jednostkowych, jak pokazano poniżej:


Oto kolejna wersja która unika operatora modulo.

int idiv_ceil (int numerator, int denominator) 
{ 
    int truncated = numerator/denominator; 
    return truncated + (((numerator < 0)^(denominator > 0)) && 
              (numerator - truncated*denominator)); 
} 

Pierwszy będzie szybsze procesory gdzie IDIV przywraca zarówno iloraz i pozostałość (i kompilator wystarczająco silny, aby korzystać z tego).

+0

Możesz pominąć 8 nawiasów, ale otrzymasz ostrzeżenie. – dyp

+0

@DyP: Tak, cecha "branchless" jest ważniejsza dla kompilatorów o gęstości mniejszej niż g ++ ... co również odpowiada małym procesorom, gdzie naprawdę zależy Ci na wydajności. –

+0

nareszcie to rozumiem, C++ działa na pozytywy i na negatywy, mógłbyś właśnie powiedzieć, że – aaronman

19

Zakładając, że zarówno myIntNumber i myOtherInt są pozytywne, można zrobić:

int myValue = (myIntNumber + myOtherInt - 1)/myOtherInt; 
+7

Jeśli podejrzewasz, że inni mają problemy z czytaniem, dołącz dobrze napisany komentarz. – chux

+0

Co na temat: 'myIntNumber <0? myIntNumber/myOtherInt: (myIntNumber + myOtherInt - 1)/myOtherInt'? – celtschk

+0

Cóż, wierzę, że pozostanę z moim koderem (ale może być bardziej czytelnym). – dynamic

-1

Zamiast używać funkcji ceil przed rzutowaniem na int, możesz dodać stałą, która jest bardzo bliska (ale nie całkiem) równa 1 - w ten sposób prawie wszystko (z wyjątkiem wartości, która jest dokładnie lub bardzo zbliżona do rzeczywistej liczba całkowita) będzie zwiększone o jeden, zanim zostanie obcięte.

Przykład:

#define EPSILON (0.9999) 

int myValue = (int)(((float)myIntNumber)/myOtherInt + EPSILON); 

EDIT: po obejrzeniu odpowiedź do innego wątku, chciałbym wyjaśnić, że to będzie zaokrąglić w górę, a nie od zera - liczby ujemne staną się mniej negatywny, a liczbami dodatnimi będzie stać się bardziej pozytywnym.

+0

to nadal 2 typecast – dynamic

+0

Jest również niepoprawny, zaokrągla "-3/2" do zera. –

1

Podział całkowity z zaokrągleniem.

tylko 1 podział wykonywany za połączenie, nie % lub * lub konwersji do/z zmiennoprzecinkowych, pracuje dla dodatniego i ujemnego int. Patrz uwaga (1).

n (numerator) = OPs myIntNumber; 
d (denominator) = OPs myOtherInt; 

Poniższe podejście jest proste. int runda dywizji w kierunku 0. Dla ujemnych kwot, jest to zaokrąglenie w górę, więc nic specjalnego nie jest potrzebne. Aby uzyskać dodatnie ilorazy, dodaj d-1, aby wykonać zaokrąglenie, a następnie wykonaj podział bez znaku.

Uwaga (1) Typowy podział według 0 wysadza w powietrze i MININT/-1 zawiedzie zgodnie z oczekiwaniami na maszynach drugiego stopnia.

int IntDivRoundUp(int n, int d) { 
    // If n and d are the same sign ... 
    if ((n < 0) == (d < 0)) { 
    // If n (and d) are negative ... 
    if (n < 0) { 
     n = -n; 
     d = -d; 
    } 
    // Unsigned division rounds down. Adding d-1 to n effects a round up. 
    return (((unsigned) n) + ((unsigned) d) - 1)/((unsigned) d); 
    } 
    else { 
    return n/d; 
    } 
} 

[Edycja: Kod testu usunięta, zobacz wcześniej rev jako potrzebne źródło]

1

Wystarczy użyć

int ceil_of_division = ((dividend-1)/divisor)+1; 

Na przykład:

for (int i=0;i<20;i++) 
    std::cout << i << "/8 = " << ((i-1)/8)+1 << std::endl; 
1

Być może jest to po prostu łatwiej zrobić:

int result = dividend/divisor; 
if(dividend % divisor != 0) 
    result++; 
+0

I niech kompilator zoptymalizuje to wszystko. To tylko najlepsza odpowiedź. – Salamandar

0

mały kilof to zrobić:

int divideUp(int a, int b) { 
    result = (a-1)/b + 1; 
} 
// Proof: 
a = b*N + k (always) 
if k == 0, then 
    (a-1)  == b*N - 1 
    (a-1)/b  == N - 1 
    (a-1)/b + 1 == N ---> Good ! 

if k > 0, then 
    (a-1)  == b*N + l 
    (a-1)/b  == N 
    (a-1)/b + 1 == N+1 ---> Good ! 
Powiązane problemy