C ++: Zaokrąglanie w górę do najbliższej wielokrotności liczby


168

OK - jestem prawie zawstydzony, publikując to tutaj (i skasuję, jeśli ktoś zagłosuje za zamknięciem), ponieważ wydaje się, że jest to podstawowe pytanie.

Czy jest to właściwy sposób zaokrąglania w górę do wielokrotności liczby w C ++?

Wiem, że są inne pytania z tym związane, ale szczególnie interesuje mnie, jak najlepiej to zrobić w C ++:

int roundUp(int numToRound, int multiple)
{
 if(multiple == 0)
 {
  return numToRound;
 }

 int roundDown = ( (int) (numToRound) / multiple) * multiple;
 int roundUp = roundDown + multiple; 
 int roundCalc = roundUp;
 return (roundCalc);
}

Aktualizacja: Przepraszam, że prawdopodobnie nie wyjaśniłem tego zamiaru. Oto kilka przykładów:

roundUp(7, 100)
//return 100

roundUp(117, 100)
//return 200

roundUp(477, 100)
//return 500

roundUp(1077, 100)
//return 1100

roundUp(52, 20)
//return 60

roundUp(74, 30)
//return 90

3
Masz błąd w logice - powiedzmy, że chcę zaokrąglić 4 w górę do najbliższej wielokrotności 2. roundDown = (4/2) * 2 = 4; roundUp = 4 + 2; więc roundCalc = 6. Zakładam, że w takim przypadku chciałbyś zwrócić 4.
Niki Yoshiuchi

nie działa to w przypadku roundUp (30,30). Daje 60 jako odpowiedź, nadal powinno dać 30 jako odpowiedź ..
bsobaid

@bsobaid: Sprawdź moją odpowiedź na dole. Jest to nieco prostsze niż inne rozwiązania tutaj, chociaż te też powinny działać
Niklas B.

3
W twoich przypadkach testowych wyraźnie brakuje przykładów obejmujących liczby ujemne, przypadków, w których podział jest dokładny, przypadków, w których dzielenie jest prawie dokładne, oraz przypadków, w których liczby są bardzo blisko granic zakresu int.

1
Robben_Ford_Fan_boy, Zmiana z odpowiedzią, na którą wybrałeś, powinna zostać usunięta. Jeśli różni się od udzielonych odpowiedzi, możesz zamieścić własną odpowiedź. W obecnej sytuacji ta odpowiedź ma problemy, które należy omówić w sekcji odpowiedzi.
chux - Przywróć Monikę

Odpowiedzi:


161

Działa to dla liczb dodatnich, ale nie mam pewności co do ujemnych. Używa tylko matematyki całkowitej.

int roundUp(int numToRound, int multiple)
{
    if (multiple == 0)
        return numToRound;

    int remainder = numToRound % multiple;
    if (remainder == 0)
        return numToRound;

    return numToRound + multiple - remainder;
}

Edycja: Oto wersja, która działa z liczbami ujemnymi, jeśli "w górę" oznacza wynik, który jest zawsze> = dane wejściowe.

int roundUp(int numToRound, int multiple)
{
    if (multiple == 0)
        return numToRound;

    int remainder = abs(numToRound) % multiple;
    if (remainder == 0)
        return numToRound;

    if (numToRound < 0)
        return -(abs(numToRound) - remainder);
    else
        return numToRound + multiple - remainder;
}

+1 Moim zdaniem zdecydowanie najładniejsze i najbardziej czytelne rozwiązanie.
Robben_Ford_Fan_boy,

1
Dodaj if(number<0){ multiple = multiple*(-1); }na początku, aby zaokrąglić liczby ujemne we właściwym kierunku
Josh

4
@Josh: Po co używać mnożenia? if(number<0) multiple = -multiplejest łatwiej.
md5

nie działa to w przypadku roundUp (30,30). Daje 60 jako odpowiedź, nadal powinno dać 30 jako odpowiedź.
bsobaid

@bsobaid niemożliwe. if (remainder == 0)Test powinien dbać o tej sprawie. U mnie to działa: ideone.com/Waol7B
Mark Ransom

112

Bez warunków:

int roundUp(int numToRound, int multiple) 
{
    assert(multiple);
    return ((numToRound + multiple - 1) / multiple) * multiple;
}

Działa to jak zaokrąglanie od zera dla liczb ujemnych

EDYCJA: Wersja, która działa również dla liczb ujemnych

int roundUp(int numToRound, int multiple) 
{
    assert(multiple);
    int isPositive = (int)(numToRound >= 0);
    return ((numToRound + isPositive * (multiple - 1)) / multiple) * multiple;
}

Testy


Jeśli multipleto potęga 2 (szybciej ~ 3,7 razy http://quick-bench.com/sgPEZV9AUDqtx2uujRSa3-eTE80 )

int roundUp(int numToRound, int multiple) 
{
    assert(multiple && ((multiple & (multiple - 1)) == 0));
    return (numToRound + multiple - 1) & -multiple;
}

Testy


24
+1 za moc wersji 2. Bardzo przydatne, ponieważ całkowicie pozwala uniknąć kosztów mnożenia, dzielenia lub modulo.
Nikos C.

Czy na pewno te algorytmy nie mają żadnych warunków wstępnych? A co z liczbami ujemnymi? Zachowanie wydaje się być niezdefiniowane w wersjach poprzedzających C ++ 11 .
cubuspl42

> A co z liczbami ujemnymi? Jak opisano, działa to w przypadku liczb ujemnych, takich jak zaokrąglanie od zera.
KindDragon

Odczytuję „zaokrąglanie w górę” jako zaokrąglanie w kierunku dodatniej nieskończoności, a nie zaokrąglanie od zera.

8
Zauważ, że & ~(x - 1)jest to to samo, co & -xdla arytmetyki uzupełnień do dwóch.
Todd Lehman,

39

Działa to, gdy współczynnik zawsze będzie dodatni:

int round_up(int num, int factor)
{
    return num + factor - 1 - (num - 1) % factor;
}

Edycja: to powraca round_up(0,100)=100. Zapoznaj się z komentarzem Paula poniżej, aby zapoznać się z rozwiązaniem, które powraca round_up(0,100)=0.


1
Wygląda na to, że jest to najkrótszy przypadek, który obsługuje przypadek „już-wielokrotny”.
harningt

1
Najlepsze rozwiązanie pod względem ilości kosztownych operacji. Używa tylko jednej dywizji i nie ma mnożenia
Niklas B.

3
round_up (0, 100) == 100 zamiast 0 jak w zaakceptowanej odpowiedzi
Gregory

7
Nie powinno być num + factor - 1 - (num + factor - 1) % factor?
Paul

6
num - 1 - (num - 1) % factor + factorwykonuje te same obliczenia bez ryzyka przepełnienia liczby całkowitej.

24

Jest to uogólnienie problemu „jak sprawdzić, ile bajtów zajmie n bitów? (A: (n bitów + 7) / 8).

int RoundUp(int n, int roundTo)
{
    // fails on negative?  What does that mean?
    if (roundTo == 0) return 0;
    return ((n + roundTo - 1) / roundTo) * roundTo; // edit - fixed error
}

1
Nie zaokrągla to do następnej wielokrotności liczby.
aaaa bbbb

7
Podoba mi się to rozwiązanie, ponieważ jeśli roundTo będzie potęgą 2, możesz wyeliminować / i * i skończyć z tylko tanimi operacjami (x = roundTo - 1; return (n + x) & ~ x;)
Trejkaz

@Trejkaz nope. Powinno być (x = roundTo - 1; return (n+x)&~roundTo;)tak, jak w mojej odpowiedzi
KindDragon

@KindDragon, który daje zły wynik dla mnie, ale jeśli poprawię to, mówiąc ~ x zamiast ~ roundTo, otrzymam oczekiwany wynik. W każdym razie na Javie 8.
Trejkaz

@KindDragon: Maska AND musi być 0xFFF...000, nie 0xFFF7FFFlub coś takiego, więc chcesz albo negację dopełniacza 2 ( -: minus) przy potędze 2, albo odwracanie bitu na potędze 2 (dopełniacz odwrotny ~,: tylda nie minus). Więc (n+x) & ~xlub (n-roundTo+1) & -roundTo.
Peter Cordes,

14
int roundUp(int numToRound, int multiple)
{
 if(multiple == 0)
 {
  return 0;
 }
 return ((numToRound - 1) / multiple + 1) * multiple;  
}

I nie ma potrzeby bawić się warunkami


11

Dla każdego, kto szuka krótkiej i słodkiej odpowiedzi. To jest to, czego użyłem. Bez uwzględniania negatywów.

n - (n % r)

To zwróci poprzedni czynnik.

(n + r) - (n % r)

Wróci następny. Mam nadzieję, że to komuś pomoże. :)


9
float roundUp(float number, float fixedBase) {
    if (fixedBase != 0 && number != 0) {
        float sign = number > 0 ? 1 : -1;
        number *= sign;
        number /= fixedBase;
        int fixedPoint = (int) ceil(number);
        number = fixedPoint * fixedBase;
        number *= sign;
    }
    return number;
}

Działa to dla dowolnej liczby zmiennoprzecinkowej lub podstawy (np. Możesz zaokrąglić -4 do najbliższej 6,75). W istocie jest to konwersja do stałego punktu, zaokrąglenie w tym miejscu, a następnie konwersja z powrotem. Obsługuje wartości ujemne, zaokrąglając wartość AWAY od 0. Obsługuje również zaokrąglanie ujemne do wartości, zasadniczo zmieniając funkcję w roundDown.

Konkretna wersja int wygląda następująco:

int roundUp(int number, int fixedBase) {
    if (fixedBase != 0 && number != 0) {
        int sign = number > 0 ? 1 : -1;
        int baseSign = fixedBase > 0 ? 1 : 0;
        number *= sign;
        int fixedPoint = (number + baseSign * (fixedBase - 1)) / fixedBase;
        number = fixedPoint * fixedBase;
        number *= sign;
    }
    return number;
}

Co jest mniej więcej odpowiedzią cokołu, z dodatkowym wsparciem ujemnego wejścia.


Przetestowałem kod float roundUp z double, to działa dla mnie. Naprawdę rozwiązuje mój problem.
Ashif,

1
A co z double round(double value, double multiple) { double sign = value; multiple = std::copysign(multiple, 1.0); value = std::copysign(value, 1.0); return std::copysign(multiple * std::ceil(value / multiple), sign); }Lub zamień pułap na rundę, aby uzyskać zaokrąglenie.
Troyseph

8

Jest to nowoczesne podejście C ++ wykorzystujące funkcję szablonu, która działa dla float, double, long, int i short (ale nie dla long long i long double ze względu na używane wartości double).

#include <cmath>
#include <iostream>

template<typename T>
T roundMultiple( T value, T multiple )
{
    if (multiple == 0) return value;
    return static_cast<T>(std::round(static_cast<double>(value)/static_cast<double>(multiple))*static_cast<double>(multiple));
}

int main()
{
    std::cout << roundMultiple(39298.0, 100.0) << std::endl;
    std::cout << roundMultiple(20930.0f, 1000.0f) << std::endl;
    std::cout << roundMultiple(287399, 10) << std::endl;
}

Ale możesz łatwo dodać obsługę specjalizacji szablonów long longi long doubleze specjalizacją, jak pokazano poniżej:

template<>
long double roundMultiple<long double>( long double value, long double multiple)
{
    if (multiple == 0.0l) return value;
    return std::round(value/multiple)*multiple;
}

template<>
long long roundMultiple<long long>( long long value, long long multiple)
{
    if (multiple == 0.0l) return value;
    return static_cast<long long>(std::round(static_cast<long double>(value)/static_cast<long double>(multiple))*static_cast<long double>(multiple));
}

Aby utworzyć funkcje do zaokrąglania w górę, użyj std::ceili, aby zawsze zaokrąglać w dół std::floor. Mój przykład z góry to zaokrąglanie za pomocą std::round.

Utwórz funkcję szablonu „zaokrąglenie w górę” lub lepiej znaną jako „okrągły sufit”, jak pokazano poniżej:

template<typename T>
T roundCeilMultiple( T value, T multiple )
{
    if (multiple == 0) return value;
    return static_cast<T>(std::ceil(static_cast<double>(value)/static_cast<double>(multiple))*static_cast<double>(multiple));
}

Utwórz funkcję szablonu „zaokrąglenie w dół” lub lepiej znaną jako „zaokrąglona podłoga”, jak pokazano poniżej:

template<typename T>
T roundFloorMultiple( T value, T multiple )
{
    if (multiple == 0) return value;
    return static_cast<T>(std::floor(static_cast<double>(value)/static_cast<double>(multiple))*static_cast<double>(multiple));
}

1
plus 1, chociaż niektórzy ludzie mogą uznać za bardziej rozsądne zwrócenie 0, gdy mulitple == 0
stijn

3
Uważaj, ponieważ konwersja int64_t na double może być stratna, więc nie jest tak ogólna, jak mogłoby się wydawać.
Adrian McCarthy,

@AdrianMcCarthy Tak, musisz stworzyć odpowiednie specjalizacje szablonów, jak pokazano powyżej. Jak widać, implementuję dwie dodatkowe funkcje dla long longi long double. To samo należy oczywiście zrobić dla pozostałych dwóch funkcji.
Flovdis

Myślę, że jest to zdecydowanie najwolniejsze ze wszystkich, ale nie musiałoby tak być. Wszystko, co musisz zrobić, to std :: enable_if_t i zrobić dwie gałęzie dla liczb całkowitych i zmiennoprzecinkowych. Możesz również lepiej wykorzystać wartość numeric_limits i sprawdzić, czy mantysa jest wystarczająco duża, aby faktycznie pasowała do wartości. To zwiększyłoby bezpieczeństwo.
świnia

5

Po pierwsze, twój warunek błędu (wielokrotność == 0) powinien prawdopodobnie mieć wartość zwracaną. Co? Nie wiem Może chcesz zgłosić wyjątek, to zależy od Ciebie. Ale zwracanie niczego nie jest niebezpieczne.

Po drugie, powinieneś sprawdzić, czy numToRound nie jest już wielokrotnością. W przeciwnym razie, gdy dodasz multipledo roundDown, otrzymasz złą odpowiedź.

Po trzecie, twoje rzuty są złe. Rzucasz numToRoundna liczbę całkowitą, ale to już jest liczba całkowita. Musisz rzucić do, aby podwoić się przed dzieleniem i wrócić do int po pomnożeniu.

Na koniec, czego chcesz dla liczb ujemnych? Zaokrąglanie „w górę” może oznaczać zaokrąglanie do zera (zaokrąglanie w tym samym kierunku co liczby dodatnie) lub od zera („większa” liczba ujemna). A może cię to nie obchodzi.

Oto wersja z trzema pierwszymi poprawkami, ale nie radzę sobie z negatywnym problemem:

int roundUp(int numToRound, int multiple)
{
 if(multiple == 0)
 {
  return 0;
 }
 else if(numToRound % multiple == 0)
 {
  return numToRound
 }

 int roundDown = (int) (( (double) numToRound / multiple ) * multiple);
 int roundUp = roundDown + multiple; 
 int roundCalc = roundUp;
 return (roundCalc);
}

@Peter Czy to jest? Zakładałem, int / intże zwróci int, co nie jest tym, czego chcieliśmy.
Mike Caron

int / int rzeczywiście zwraca int, ale właśnie tego chcesz. Na przykład numToRound = 7, multiple = 3. 7/3 = 2.
Peter Ruderman

4

W rundzie do potęgi dwóch:

Na wszelki wypadek, gdyby ktoś potrzebował rozwiązania dla liczb dodatnich zaokrąglonych do najbliższej wielokrotności potęgi dwóch (bo tak właśnie tutaj skończyłem):

// number: the number to be rounded (ex: 5, 123, 98345, etc.)
// pow2:   the power to be rounded to (ex: to round to 16, use '4')
int roundPow2 (int number, int pow2) {
    pow2--;                     // because (2 exp x) == (1 << (x -1))
    pow2 = 0x01 << pow2;

    pow2--;                     // because for any
                                //
                                // (x = 2 exp x)
                                //
                                // subtracting one will
                                // yield a field of ones
                                // which we can use in a
                                // bitwise OR

    number--;                   // yield a similar field for
                                // bitwise OR
    number = number | pow2;
    number++;                   // restore value by adding one back

    return number;
}

Wprowadzona liczba pozostanie taka sama, jeśli jest już wielokrotnością.

Oto wyjście x86_64, które daje GCC z -O2lub -Os(kompilacja 9 września 2013 - godbolt GCC online):

roundPow2(int, int):
    lea ecx, [rsi-1]
    mov eax, 1
    sub edi, 1
    sal eax, cl
    sub eax, 1
    or  eax, edi
    add eax, 1
    ret

Każda linia kodu w C doskonale odpowiada swojej linii w zestawie: http://goo.gl/DZigfX

Każda z tych instrukcji jest niezwykle szybka , więc funkcja jest również niezwykle szybka. Ponieważ kod jest tak mały i szybki, może być przydatny dla inlinefunkcji podczas jej używania.


Kredyt:


1
Dokładnie to, czego szukałem. Dzięki!
kiyo

1
int roundUpPow2 (int num, int pow2) {return num + (pow2 - 1) & ~ (pow2 - 1); } jest około 30% szybsze i łatwiejsze w użyciu (przechodzisz 16 zamiast 4, aby zaokrąglić w górę do następnej wielokrotności 16.
Axel Rietschin

3

Używam:

template <class _Ty>
inline _Ty n_Align_Up(_Ty n_x, _Ty n_alignment)
{
    assert(n_alignment > 0);
    //n_x += (n_x >= 0)? n_alignment - 1 : 1 - n_alignment; // causes to round away from zero (greatest absolute value)
    n_x += (n_x >= 0)? n_alignment - 1 : -1; // causes to round up (towards positive infinity)
    //n_x += (_Ty(-(n_x >= 0)) & n_alignment) - 1; // the same as above, avoids branch and integer multiplication
    //n_x += n_alignment - 1; // only works for positive numbers (fastest)
    return n_x - n_x % n_alignment; // rounds negative towards zero
}

a dla potęgi dwojga:

template <class _Ty>
bool b_Is_POT(_Ty n_x)
{
    return !(n_x & (n_x - 1));
}

template <class _Ty>
inline _Ty n_Align_Up_POT(_Ty n_x, _Ty n_pot_alignment)
{
    assert(n_pot_alignment > 0);
    assert(b_Is_POT(n_pot_alignment)); // alignment must be power of two
    -- n_pot_alignment;
    return (n_x + n_pot_alignment) & ~n_pot_alignment; // rounds towards positive infinity (i.e. negative towards zero)
}

Zauważ, że obie te zaokrąglają ujemne wartości do zera (co oznacza zaokrąglenie do dodatniej nieskończoności dla wszystkich wartości), żadna z nich nie opiera się na przepełnieniu ze znakiem (co jest niezdefiniowane w C / C ++).

To daje:

n_Align_Up(10, 100) = 100
n_Align_Up(110, 100) = 200
n_Align_Up(0, 100) = 0
n_Align_Up(-10, 100) = 0
n_Align_Up(-110, 100) = -100
n_Align_Up(-210, 100) = -200
n_Align_Up_POT(10, 128) = 128
n_Align_Up_POT(130, 128) = 256
n_Align_Up_POT(0, 128) = 0
n_Align_Up_POT(-10, 128) = 0
n_Align_Up_POT(-130, 128) = -128
n_Align_Up_POT(-260, 128) = -256

Używam twojego n_Align_Up_POTodkąd zobaczyłem go w klasie TList Delphi. Ma swoje ograniczenia, takie jak wyrównanie (wielokrotne) będące potęgą 2, ale rzadko jest to problem, ponieważ głównie używam go do uzyskania / sprawdzenia prawidłowego wyrównania dla SMID. To jest niesamowite i wydaje się, że niewiele osób o tym wie.
user1593842

2

Prawdopodobnie bezpieczniej jest rzutować na zmiennoprzecinkowe i używać metody ceil () - chyba że wiesz, że dzielenie int da poprawny wynik.


1
Zauważ, że double może pomieścić 54 bity znacznika na maszynach z procesorem x86. Jeśli masz 64-bitowe liczby int, ostatecznie się to nie powiedzie.
świnia

Standard IEEE754 double nie może, ale procesory x64 mają 80-bitowy wewnętrzny zmiennoprzecinkowy, więc operacje na pojedynczej liczbie są niezawodne
Martin Beckett

1
Chociaż to prawda, masz bardzo małą kontrolę nad tym zaokrąglaniem z C / C ++. Zależy to od ustawień słowa kontrolnego i może w rzeczywistości zaokrąglić do mniej niż 80 bitów. Masz również zestawy instrukcji SSE i inne zestawy SIMD, które nie mają takiego rozszerzonego pośrednika (kompilator wektoryzacji mógłby z łatwością ich użyć).
świnia

2
int noOfMultiples = int((numToRound / multiple)+0.5);
return noOfMultiples*multiple

C ++ zaokrągla każdą liczbę w dół, więc jeśli dodasz 0,5 (jeśli jego 1,5 będzie to 2), ale 1,49 będzie równe 1,99, więc 1.

EDYCJA - Przepraszamy, nie widziałem, że chcesz zaokrąglić w górę, sugerowałbym użycie metody ceil () zamiast +0,5


2

po pierwsze, ponieważ tak naprawdę nie rozumiem, co chcesz zrobić, linie

int roundUp = roundDown + multiple;
int roundCalc = roundUp;
return (roundCalc); 

zdecydowanie można skrócić do

int roundUp = roundDown + multiple;
return roundUp;

2

może to pomoże:

int RoundUpToNearestMultOfNumber(int val, int num)
{
  assert(0 != num);
  return (floor((val + num) / num) * num);
}

Po co używać dzielenia dolnego i całkowitego? Nie ma nic do podłogi. Gdyby była podwójna, można by przynajmniej odziedziczyć obsługę wartości ujemnych.
świnia

2

Zawsze zaokrąglać

int alwaysRoundUp(int n, int multiple)
{
    if (n % multiple != 0) {
        n = ((n + multiple) / multiple) * multiple;

        // Another way
        //n = n - n % multiple + multiple;
    }

    return n;
}

alwaysRoundUp (1, 10) -> 10

alwaysRoundUp (5, 10) -> 10

alwaysRoundUp (10, 10) -> 10


Zawsze zaokrąglać w dół

int alwaysRoundDown(int n, int multiple)
{
    n = (n / multiple) * multiple;

    return n;
}

alwaysRoundDown (1, 10) -> 0

alwaysRoundDown (5, 10) -> 0

alwaysRoundDown (10, 10) -> 10


Aby zaokrąglić normalną drogę

int normalRound(int n, int multiple)
{
    n = ((n + multiple/2)/multiple) * multiple;

    return n;
}

normalRound (1, 10) -> 0

normalRound (5, 10) -> 10

normalRound (10, 10) -> 10


2

Zaokrąglij do najbliższej wielokrotności, która jest potęgą 2

unsigned int round(unsigned int value, unsigned int multiple){
    return ((value-1u) & ~(multiple-1u)) + multiple;
}

Może to być przydatne podczas przydzielania wzdłuż linii pamięci podręcznej, gdzie żądany przyrost zaokrąglenia jest potęgą dwóch, ale wynikowa wartość musi być tylko wielokrotnością tego. Na gcckorpusie tej funkcji generuje 8 instrukcji montażu bez podziałów i rozgałęzień.

round(  0,  16) ->   0
round(  1,  16) ->  16
round( 16,  16) ->  16
round(257, 128) -> 384 (128 * 3)
round(333,   2) -> 334

1

Znalazłem algorytm, który jest nieco podobny do powyższego:

int [(| x | + n-1) / n] * [(nx) / | x |], gdzie x to wartość wprowadzona przez użytkownika, a n to używana wielokrotność.

Działa dla wszystkich wartości x, gdzie x jest liczbą całkowitą (dodatnią lub ujemną, w tym zerem). Napisałem go specjalnie dla programu C ++, ale można to w zasadzie zaimplementować w dowolnym języku.


1

Dla ujemnego numToRound:

Powinno to być naprawdę łatwe, ale standardowy operator modulo% nie obsługuje liczb ujemnych, jak można by się spodziewać. Na przykład -14% 12 = -2 a nie 10. Pierwszą rzeczą do zrobienia jest uzyskanie operatora modulo, który nigdy nie zwraca liczb ujemnych. Wtedy roundUp jest naprawdę proste.

public static int mod(int x, int n) 
{
    return ((x % n) + n) % n;
}

public static int roundUp(int numToRound, int multiple) 
{
    return numRound + mod(-numToRound, multiple);
}

1

Oto, co bym zrobił:

#include <cmath>

int roundUp(int numToRound, int multiple)
{
    // if our number is zero, return immediately
   if (numToRound == 0)
        return multiple;

    // if multiplier is zero, return immediately
    if (multiple == 0)
        return numToRound;

    // how many times are number greater than multiple
    float rounds = static_cast<float>(numToRound) / static_cast<float>(multiple);

    // determine, whether if number is multiplier of multiple
    int floorRounds = static_cast<int>(floor(rounds));

    if (rounds - floorRounds > 0)
        // multiple is not multiplier of number -> advance to the next multiplier
        return (floorRounds+1) * multiple;
    else
        // multiple is multiplier of number -> return actual multiplier
        return (floorRounds) * multiple;
}

Kod może nie być optymalny, ale wolę czysty kod niż suchą wydajność.


Odlewania intaby floatłatwo traci precyzję i pozwala na błędnych odpowiedzi.
chux - Przywróć Monikę

1
int roundUp (int numToRound, int multiple)
{
  return multiple * ((numToRound + multiple - 1) / multiple);
}

mimo że:

  • nie zadziała dla liczb ujemnych
  • nie zadziała, jeśli numRound + wielokrotne przepełnienia

sugerowałoby użycie zamiast tego liczb całkowitych bez znaku, które mają zdefiniowane zachowanie związane z przepełnieniem.

Otrzymasz wyjątek to wielokrotność == 0, ale w tym przypadku nie jest to dobrze zdefiniowany problem.


1

do:

int roundUp(int numToRound, int multiple)
{
  return (multiple ? (((numToRound+multiple-1) / multiple) * multiple) : numToRound);
}

a dla twojego ~ / .bashrc:

roundup()
{
  echo $(( ${2} ? ((${1}+${2}-1)/${2})*${2} : ${1} ))
}

1

Używam kombinacji modułu, aby anulować dodanie reszty, jeśli xjest już wielokrotnością:

int round_up(int x, int div)
{
    return x + (div - x % div) % div;
}

Znajdujemy odwrotność reszty, a następnie moduł, który z dzielnikiem ponownie, aby go unieważnić, jeśli jest to sam dzielnik, a następnie dodaj x.

round_up(19, 3) = 21

1

Oto moje rozwiązanie oparte na sugestii OP i przykładach podanych przez wszystkich innych. Ponieważ prawie każdy szukał obsługi liczb ujemnych, to rozwiązanie właśnie to robi, bez użycia funkcji specjalnych, np. Abs i tym podobnych.

Unikając modułu i zamiast tego stosując dzielenie, liczba ujemna jest wynikiem naturalnym, chociaż jest zaokrąglana w dół. Po obliczeniu wersji zaokrąglonej w dół wykonuje wymagane obliczenia matematyczne w celu zaokrąglenia w górę, w kierunku ujemnym lub dodatnim.

Należy również pamiętać, że żadne funkcje specjalne nie są używane do obliczania czegokolwiek, więc występuje tam niewielkie zwiększenie prędkości.

int RoundUp(int n, int multiple)
{
    // prevent divide by 0 by returning n
    if (multiple == 0) return n;

    // calculate the rounded down version
    int roundedDown = n / multiple * multiple;

    // if the rounded version and original are the same, then return the original
    if (roundedDown == n) return n;

    // handle negative number and round up according to the sign
    // NOTE: if n is < 0 then subtract the multiple, otherwise add it
    return (n < 0) ? roundedDown - multiple : roundedDown + multiple;
}

Nie powiedzie się RoundUp(INT_MIN, -1), jak n / multipleto intprzepełnienia.
chux - Przywróć Monikę

1

Myślę, że to powinno ci pomóc. Poniższy program napisałem w języku C.

# include <stdio.h>
int main()
{
  int i, j;
  printf("\nEnter Two Integers i and j...");
  scanf("%d %d", &i, &j);
  int Round_Off=i+j-i%j;
  printf("The Rounded Off Integer Is...%d\n", Round_Off);
  return 0;
}

0
/// Rounding up 'n' to the nearest multiple of number 'b'.
/// - Not tested for negative numbers.
/// \see http://stackoverflow.com/questions/3407012/
#define roundUp(n,b) ( (b)==0 ? (n) : ( ((n)+(b)-1) - (((n)-1)%(b)) ) )

/// \c test->roundUp().
void test_roundUp() {   
    // yes_roundUp(n,b) ( (b)==0 ? (n) : ( (n)%(b)==0 ? n : (n)+(b)-(n)%(b) ) )
    // yes_roundUp(n,b) ( (b)==0 ? (n) : ( ((n + b - 1) / b) * b ) )

    // no_roundUp(n,b) ( (n)%(b)==0 ? n : (b)*( (n)/(b) )+(b) )
    // no_roundUp(n,b) ( (n)+(b) - (n)%(b) )

if (true) // couldn't make it work without (?:)
{{  // test::roundUp()
    unsigned m;
                { m = roundUp(17,8); } ++m;
    assertTrue( 24 == roundUp(17,8) );
                { m = roundUp(24,8); }
    assertTrue( 24 == roundUp(24,8) );

    assertTrue( 24 == roundUp(24,4) );
    assertTrue( 24 == roundUp(23,4) );
                { m = roundUp(23,4); }
    assertTrue( 24 == roundUp(21,4) );

    assertTrue( 20 == roundUp(20,4) );
    assertTrue( 20 == roundUp(19,4) );
    assertTrue( 20 == roundUp(18,4) );
    assertTrue( 20 == roundUp(17,4) );

    assertTrue( 17 == roundUp(17,0) );
    assertTrue( 20 == roundUp(20,0) );
}}
}

0

Oto wyniki, których szukasz dla dodatnich liczb całkowitych:

#include <iostream>
using namespace std;

int roundUp(int numToRound, int multiple);

int main() {
    cout << "answer is: " << roundUp(7, 100) << endl;
    cout << "answer is: " << roundUp(117, 100) << endl;
    cout << "answer is: " << roundUp(477, 100) << endl;
    cout << "answer is: " << roundUp(1077, 100) << endl;
    cout << "answer is: " << roundUp(52,20) << endl;
    cout << "answer is: " << roundUp(74,30) << endl;
    return 0;
}

int roundUp(int numToRound, int multiple) {
    if (multiple == 0) {
        return 0;
    }
    int result = (int) (numToRound / multiple) * multiple;
    if (numToRound % multiple) {
        result += multiple;
    } 
    return result;
}

A oto wyniki:

answer is: 100
answer is: 200
answer is: 500
answer is: 1100
answer is: 60
answer is: 90

0

Myślę, że to działa:

int roundUp(int numToRound, int multiple) {
    return multiple? !(numToRound%multiple)? numToRound : ((numToRound/multiple)+1)*multiple: numToRound;
}

-1

To działa dla mnie, ale nie próbowałem radzić sobie z negatywami

public static int roundUp(int numToRound, int multiple) {
    if (multiple == 0) {
        return 0;
    } else if (numToRound % multiple == 0) {
    return numToRound;
    }

    int mod = numToRound % multiple;
    int diff = multiple - mod;
    return numToRound + diff;
}

-2

Oto super proste rozwiązanie, aby pokazać pojęcie elegancji. Zasadniczo służy do przyciągania siatki.

(pseudo kod)

nearestPos = Math.Ceil( numberToRound / multiple ) * multiple;

czy sprawdziłeś swój pomysł przed przesłaniem? nie dawaj poprawnej odpowiedzi
yaodav

To nawet nie jest prawidłowy kod.
user13783520
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.