Java zaokrągla w górę do int przy użyciu Math.ceil

101
int total = (int) Math.ceil(157/32);

Dlaczego nadal zwraca 4? 157/32 = 4.90625, Muszę zaokrąglić, rozejrzałem się i wydaje mi się, że to właściwa metoda.

Próbowałem totaljako doubletyp, ale otrzymałem 4.0.

Co ja robię źle?

Tomek
źródło

Odpowiedzi:

188

Robisz, 157/32co polega na dzieleniu między sobą dwóch liczb całkowitych, co zawsze skutkuje zaokrągleniem w dół. Dlatego (int) Math.ceil(...)nic nie robi. Istnieją trzy możliwe rozwiązania, aby osiągnąć to, czego chcesz. I zalecają przy użyciu opcji 1 lub opcji 2 . Proszę nie NIE używać opcji 0 .

## Opcja 0

Zamień ai bna podwójną, a możesz użyć dzielenia i Math.ceiltak, jak chciałeś, aby działał. Jednak zdecydowanie odradzam stosowanie tego podejścia, ponieważ podwójny podział może być nieprecyzyjny. Aby dowiedzieć się więcej na temat nieprecyzyjności podwójnych, zobacz to pytanie .

int n = (int) Math.ceil((double) a / b));

##Opcja 1

int n = a / b + ((a % b == 0) ? 0 : 1); 

Robisz a / bz zawsze wartością floor, jeśli ai bobie są liczbami całkowitymi. Następnie masz wbudowane oświadczenie `` jeśli '', które sprawdza, czy zamiast podłogi należy ustawić sufit. Więc +1 lub +0, jeśli jest reszta z dzielenia, potrzebujesz +1. a % b == 0sprawdza pozostałą część.

##Opcja 2

Ta opcja jest bardzo krótka, ale może dla niektórych mniej intuicyjna. Myślę, że to mniej intuicyjne podejście byłoby szybsze niż podejście z podwójnym dzieleniem i porównaniem:
proszę zauważyć, że to nie działa b < 0.

int n = (a + b - 1) / b;

Aby zmniejszyć ryzyko przepełnienia, możesz użyć następującego. Należy jednak pamiętać, że nie działa w przypadku a = 0i b < 1.

int n = (a - 1) / b + 1;

## Wyjaśnienie „mniej intuicyjnego podejścia”

Ponieważ dzielenie dwóch liczb całkowitych w Javie (i większości innych języków programowania) zawsze będzie skutkowało wynikiem. Więc:

int a, b;
int result = a/b (is the same as floor(a/b) )

Ale nie chcemy floor(a/b), ale ceil(a/b)i używając definicji i wykresów z Wikipedii :wprowadź opis obrazu tutaj

Dzięki tym działkom funkcji podłogi i sufitu można zobaczyć związek.

Funkcja podłogowa Funkcja sufitu

Możesz to zobaczyć floor(x) <= ceil(x). Potrzebujemy floor(x + s) = ceil(x). Więc musimy znaleźć s. Jeśli weźmiemy pod uwagę 1/2 <= s < 1, będzie dobrze (spróbuj kilku liczb, a zobaczysz, że tak, trudno mi to udowodnić). I 1/2 <= (b-1) / b < 1tak

ceil(a/b) = floor(a/b + s)
          = floor(a/b + (b-1)/b)
          = floor( (a+b-1)/b) )

To nie jest prawdziwy dowód, ale mam nadzieję, że jesteś z niego zadowolony. Gdyby ktoś mógł to lepiej wytłumaczyć, też bym to docenił. Może zapytaj o to w MathOverflow .

martijnn2008
źródło
1
To byłaby wielka przysługa, gdybyś mógł wyjaśnić intuicję stojącą za mniej intuicyjnym podejściem? Wiem, że to prawda, chcę wiedzieć, jak do tego doszedłeś i jak mogę matematycznie wykazać, że jest poprawna. Próbowałem rozwiązać to matematycznie, nie byłem przekonany.
Saad Rehman Shah
Mam nadzieję, że jesteś zadowolony z mojej edycji, nie mogę zrobić nic lepszego myślę :(
martijnn2008
Zakładam, że Math.floor i ceil są poprawne tylko dla dzielenia liczb całkowitych, a nie dla długiego dzielenia, gdy wartości są rzutowane na podwójne. Przykładami liczników są 4611686018427386880/4611686018427387137 awaria na podłodze i 4611686018427386881/4611686018427386880 awaria na suficie
Wouter
2
Uwaga: wyniki dwóch podwariantów opcji 2 nie są identyczne we wszystkich przypadkach. Wartość zero dla a da 0 w pierwszej, a 1 w drugiej (co nie jest poprawną odpowiedzią dla większości aplikacji).
Sushisource,
1
Czy na pewno nie chodziło Ci o „Jednak pamiętaj, że to nie działa dla a = 0 i b < 1”
dantiston
60

157/32 jest int/int, co powoduje, że plik int.

Spróbuj użyć podwójnego dosłowny - 157/32d, która jest int/double, co skutkuje double.

Bozho
źródło
Czy na pewno int / int zawsze spowoduje int ?! czy możesz podać źródło tego ?!
34

157/32jest dzieleniem całkowitoliczbowym, ponieważ wszystkie literały numeryczne są liczbami całkowitymi, chyba że podano inaczej z sufiksem ( ddla double lna długo)

podział jest zaokrąglany w dół (do 4) przed zamianą na podwójny (4,0), który jest następnie zaokrąglany w górę (do 4,0)

jeśli używasz zmiennych, możesz tego uniknąć

double a1=157;
double a2=32;
int total = (int) Math.ceil(a1/a2);
maniak grzechotki
źródło
26
int total = (int) Math.ceil((double)157/32);
Phoebus
źródło
7

Nikt nie wspomniał o najbardziej intuicyjnym:

int x = (int) Math.round(Math.ceil((double) 157 / 32));

To rozwiązanie naprawia niedokładność podwójnego podziału.

IG Pascual
źródło
1
Math.round zwraca long
Zulqurnain Jutt
Dzięki @ZulqurnainJutt, dodał obsadę
IG Pascual
4

W Javie dodanie .0 sprawi, że będzie to podwójne ...

int total = (int) Math.ceil(157.0 / 32.0);
Scott Higgins
źródło
3

Dzieląc dwie liczby całkowite, np.

int c = (int) a / (int) b;

wynikiem jest an int, którego wartość jest apodzielona przez bzaokrąglenie do zera. Ponieważ wynik jest już zaokrąglony, ceil()nic nie robi. Zauważ, że to zaokrąglenie nie jest tym samym floor(), co zaokrąglenie w kierunku ujemnej nieskończoności. Więc 3/2jest równe 1(i floor(1.5)równe 1.0, ale (-3)/2równe -1(ale floor(-1.5)równe -2.0).

Jest to istotne, ponieważ jeśli a/bbyły zawsze takie same, jak floor(a / (double) b), to może po prostu wdrożyć ceil()od a/bjak -( (-a) / b).

Sugestia wyjścia ceil(a/b)z

int n = (a + b - 1) / b;, co jest równoważne z a / b + (b - 1) / blub(a - 1) / b + 1

działa, ponieważ ceil(a/b)jest zawsze o jeden większy niż floor(a/b), z wyjątkiem sytuacji, gdy a/bjest liczbą całkowitą. Więc chcesz podbić ją do (lub poza) następną liczbę całkowitą, chyba że a/bjest to liczba całkowita. Dodanie 1 - 1 / bto zrobi. W przypadku liczb całkowitych nie przesunie ich do następnej liczby całkowitej. W przypadku wszystkiego innego tak będzie.

Yikes. Mam nadzieję, że to ma sens. Jestem pewien, że istnieje bardziej matematycznie elegancki sposób, aby to wyjaśnić.

jorgenman
źródło
2

Aby przekonwertować liczbę z liczby całkowitej na liczbę rzeczywistą, możesz dodać kropkę:

int total = (int) Math.ceil(157/32.);

Wynik (157/32.) Też będzie prawdziwy. ;)

Vitaliy Borisok
źródło
2
int total = (int) Math.ceil( (double)157/ (double) 32);
kamienie333
źródło
1

Sprawdź poniższe rozwiązanie dla swojego pytania:

int total = (int) Math.ceil(157/32);

Tutaj powinieneś pomnożyć Licznik przez 1.0, wtedy poda twoją odpowiedź.

int total = (int) Math.ceil(157*1.0/32);
Nieznany
źródło
0

Użyj podwójnego, aby rzucać jak

Math.ceil((double)value) lub jak

Math.ceil((double)value1/(double)value2);
Bhupinder
źródło
0

Java /domyślnie udostępnia tylko podział pięter . Ale możemy zapisać sufit w kategoriach podłogi . Zobaczmy:

Za ypomocą formularza można zapisać dowolną liczbę całkowitą y == q*k+r. Zgodnie z definicją podziału podłogi (tutaj floor), która zaokrągla r,

floor(q*k+r, k) == q  , where 0  r  k-1

i podziału sufitu (tutaj ceil), który zaokrągla w górę r₁,

ceil(q*k+r₁, k) == q+1  , where 1  r  k

gdzie możemy zastąpić r+1przez r₁:

ceil(q*k+r+1, k) == q+1  , where 0  r  k-1


Następnie podstawiamy pierwsze równanie do trzeciego, aby quzyskać

ceil(q*k+r+1, k) == floor(q*k+r, k) + 1  , where 0  r  k-1

Wreszcie, biorąc pod uwagę dowolną liczbę całkowitą y, gdzie y = q*k+r+1dla niektórych q, k, rmamy

ceil(y, k) == floor(y-1, k) + 1

Gotowe. Mam nadzieję że to pomoże.

ShellayLee
źródło
Jestem pewien, że jest to poprawne, ale ponieważ chodzi o to, aby wyjaśnić, nie jest dla mnie jasne, dlaczego ceiljest definiowany jako taki na podstawie definicji intiuywnej, w szczególności, gdy bierzemy górną granicę liczby całkowitej, tj. R1 = k. Ponieważ przypadki skrajne są tym, co jest w tym trudne, myślę, że należy to nieco dokładniej opisać.
Luigi Plinge
@LuigiPlinge Dla mnie wyprowadzenie nie może być prostsze ze względu na wewnętrzną różnicę między podłogą a sufitem w kontekście operacji podziału. Myślę, że nie musisz skupiać się na przypadku krawędzi - jest to naturalny fakt, gdy próbujesz ujednolicić definicje podłogi i sufitu poprzez rozbicie liczby całkowitej. W rezultacie dowód to zaledwie trzy kroki, a wniosek można z grubsza zapamiętać jako „jeden zamortyzowany krok wstecz, a następnie jeden bezwzględny krok naprzód”.
ShellayLee
0

Są dwie metody zaokrąglania podwójnej wartości.

  1. Math.ceil
  2. Math.floor

Jeśli chcesz uzyskać odpowiedź 4.90625 jako 4, powinieneś użyć Math.floor, a jeśli chcesz uzyskać odpowiedź 4.90625 jako 5, możesz użyć Math.ceil

Możesz skorzystać z następującego kodu.

public class TestClass {

    public static void main(String[] args) {
        int floorValue = (int) Math.floor((double)157 / 32);
        int ceilValue = (int) Math.ceil((double)157 / 32);
        System.out.println("Floor: "+floorValue);
        System.out.println("Ceil: "+ceilValue);

    }

}
Deepak Kumbhar
źródło
-3
int total = (157-1)/32 + 1

lub bardziej ogólne

(a-1)/b +1 
oldcolder
źródło
Myślę, że to działa, ale tak naprawdę nie wyjaśniłeś, dlaczego oryginalna wersja nie działa.
Teepeemm
Należy jednak pamiętać, że to nie działa dla a = 0 i b <1
IG Pascual