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
int
.Odpowiedzi:
Działa to dla liczb dodatnich, ale nie mam pewności co do ujemnych. Używa tylko matematyki całkowitej.
Edycja: Oto wersja, która działa z liczbami ujemnymi, jeśli "w górę" oznacza wynik, który jest zawsze> = dane wejściowe.
źródło
if(number<0){ multiple = multiple*(-1); }
na początku, aby zaokrąglić liczby ujemne we właściwym kierunkuif(number<0) multiple = -multiple
jest łatwiej.if (remainder == 0)
Test powinien dbać o tej sprawie. U mnie to działa: ideone.com/Waol7BBez warunków:
Działa to jak zaokrąglanie od zera dla liczb ujemnych
EDYCJA: Wersja, która działa również dla liczb ujemnych
Testy
Jeśli
multiple
to potęga 2 (szybciej ~ 3,7 razy http://quick-bench.com/sgPEZV9AUDqtx2uujRSa3-eTE80 )Testy
źródło
& ~(x - 1)
jest to to samo, co& -x
dla arytmetyki uzupełnień do dwóch.Działa to, gdy współczynnik zawsze będzie dodatni:
Edycja: to powraca
round_up(0,100)=100
. Zapoznaj się z komentarzem Paula poniżej, aby zapoznać się z rozwiązaniem, które powracaround_up(0,100)=0
.źródło
num + factor - 1 - (num + factor - 1) % factor
?num - 1 - (num - 1) % factor + factor
wykonuje te same obliczenia bez ryzyka przepełnienia liczby całkowitej.Jest to uogólnienie problemu „jak sprawdzić, ile bajtów zajmie n bitów? (A: (n bitów + 7) / 8).
źródło
(x = roundTo - 1; return (n+x)&~roundTo;)
tak, jak w mojej odpowiedzi0xFFF...000
, nie0xFFF7FFF
lub 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) & ~x
lub(n-roundTo+1) & -roundTo
.I nie ma potrzeby bawić się warunkami
źródło
Dla każdego, kto szuka krótkiej i słodkiej odpowiedzi. To jest to, czego użyłem. Bez uwzględniania negatywów.
To zwróci poprzedni czynnik.
Wróci następny. Mam nadzieję, że to komuś pomoże. :)
źródło
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:
Co jest mniej więcej odpowiedzią cokołu, z dodatkowym wsparciem ujemnego wejścia.
źródło
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.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).
Ale możesz łatwo dodać obsługę specjalizacji szablonów
long long
ilong double
ze specjalizacją, jak pokazano poniżej:Aby utworzyć funkcje do zaokrąglania w górę, użyj
std::ceil
i, 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:
Utwórz funkcję szablonu „zaokrąglenie w dół” lub lepiej znaną jako „zaokrąglona podłoga”, jak pokazano poniżej:
źródło
long long
ilong double
. To samo należy oczywiście zrobić dla pozostałych dwóch funkcji.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
multiple
doroundDown
, otrzymasz złą odpowiedź.Po trzecie, twoje rzuty są złe. Rzucasz
numToRound
na 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:
źródło
int / int
że zwróci int, co nie jest tym, czego chcieliśmy.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):
Wprowadzona liczba pozostanie taka sama, jeśli jest już wielokrotnością.
Oto wyjście x86_64, które daje GCC z
-O2
lub-Os
(kompilacja 9 września 2013 - godbolt GCC online):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
inline
funkcji podczas jej używania.Kredyt:
źródło
Używam:
a dla potęgi dwojga:
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:
źródło
n_Align_Up_POT
odką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.Prawdopodobnie bezpieczniej jest rzutować na zmiennoprzecinkowe i używać metody ceil () - chyba że wiesz, że dzielenie int da poprawny wynik.
źródło
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
źródło
po pierwsze, ponieważ tak naprawdę nie rozumiem, co chcesz zrobić, linie
zdecydowanie można skrócić do
źródło
może to pomoże:
źródło
Zawsze zaokrąglać
alwaysRoundUp (1, 10) -> 10
alwaysRoundUp (5, 10) -> 10
alwaysRoundUp (10, 10) -> 10
Zawsze zaokrąglać w dół
alwaysRoundDown (1, 10) -> 0
alwaysRoundDown (5, 10) -> 0
alwaysRoundDown (10, 10) -> 10
Aby zaokrąglić normalną drogę
normalRound (1, 10) -> 0
normalRound (5, 10) -> 10
normalRound (10, 10) -> 10
źródło
Zaokrąglij do najbliższej wielokrotności, która jest potęgą 2
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
gcc
korpusie tej funkcji generuje 8 instrukcji montażu bez podziałów i rozgałęzień.źródło
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.
źródło
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.
źródło
Oto, co bym zrobił:
Kod może nie być optymalny, ale wolę czysty kod niż suchą wydajność.
źródło
int
abyfloat
łatwo traci precyzję i pozwala na błędnych odpowiedzi.mimo że:
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.
źródło
do:
a dla twojego ~ / .bashrc:
źródło
Używam kombinacji modułu, aby anulować dodanie reszty, jeśli
x
jest już wielokrotnością: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
.źródło
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.
źródło
RoundUp(INT_MIN, -1)
, jakn / multiple
toint
przepełnienia.Myślę, że to powinno ci pomóc. Poniższy program napisałem w języku C.
źródło
źródło
Oto wyniki, których szukasz dla dodatnich liczb całkowitych:
A oto wyniki:
źródło
Myślę, że to działa:
źródło
To działa dla mnie, ale nie próbowałem radzić sobie z negatywami
źródło
Oto super proste rozwiązanie, aby pokazać pojęcie elegancji. Zasadniczo służy do przyciągania siatki.
(pseudo kod)
źródło