Java arrotondamento per eccesso a un int utilizzando Math.ceil


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

Perché restituisce ancora 4? 157/32 = 4.90625, Ho bisogno di arrotondare, mi sono guardato intorno e questo sembra essere il metodo giusto.

Ho provato totalcome doubletipo, ma ottengo 4.0.

Che cosa sto facendo di sbagliato?

Risposte:


188

Stai facendo 157/32che sta dividendo due numeri interi tra loro, il che si traduce sempre in un numero intero arrotondato per difetto. Quindi (int) Math.ceil(...)non sta facendo nulla. Ci sono tre possibili soluzioni per ottenere ciò che desideri. Mi raccomando utilizzando l'opzione 1 o l'opzione 2 . Si prega di NON utilizzare l' opzione 0 .

## Opzione 0

Converti ae bin doppio e puoi usare la divisione e Math.ceilcome volevi che funzionasse. Tuttavia, sconsiglio vivamente l'uso di questo approccio, perché la doppia divisione può essere imprecisa. Per saperne di più sull'imprecisione dei doppi vedi questa domanda .

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

##Opzione 1

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

Fai a / bsempre floor se ae bsono entrambi numeri interi. Quindi hai un'istruzione if in linea che controlla se dovresti o meno soffermarti invece di floor. Quindi +1 o +0, se c'è un resto con la divisione hai bisogno di +1. a % b == 0controlla il resto.

##Opzione 2

Questa opzione è molto breve, ma forse per alcuni meno intuitiva. Penso che questo approccio meno intuitivo sarebbe più veloce dell'approccio a doppia divisione e confronto:
tieni presente che questo non funziona per b < 0.

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

Per ridurre la possibilità di overflow è possibile utilizzare quanto segue. Tuttavia, tieni presente che non funziona per a = 0e b < 1.

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

## Spiegazione dietro l '"approccio meno intuitivo"

Dal momento che la divisione di due interi in Java (e nella maggior parte degli altri linguaggi di programmazione) darà sempre il risultato finale. Così:

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

Ma non vogliamo floor(a/b), ma ceil(a/b), e usando le definizioni e le trame di Wikipedia :inserisci qui la descrizione dell'immagine

Con queste trame della funzione pavimento e soffitto puoi vedere la relazione.

Funzione pavimento Funzione Ceil

Lo puoi vedere floor(x) <= ceil(x). Abbiamo bisogno floor(x + s) = ceil(x). Quindi dobbiamo trovare s. Se lo prendiamo 1/2 <= s < 1sarà giusto (prova alcuni numeri e vedrai che lo fa, trovo difficile dimostrarlo). E 1/2 <= (b-1) / b < 1così

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

Questa non è una vera prova, ma spero che tu ne sia soddisfatto. Se qualcuno può spiegarlo meglio lo apprezzerei anch'io. Magari chiedilo su MathOverflow .


1
Sarebbe un enorme favore se potessi spiegare l'intuizione dietro l'approccio meno intuitivo? So che è corretto, voglio sapere come ci sei arrivato e come posso dimostrare matematicamente che è corretto. Ho provato a risolverlo matematicamente, non ero convinto.
Saad Rehman Shah

Spero che tu sia soddisfatto della mia modifica, non posso fare di meglio, penso :(
martijnn2008

Presumo che Math.floor e ceil siano corretti solo per la divisione intera e non per la divisione lunga quando i valori vengono convertiti in doppi. Esempi di contatori sono 4611686018427386880/4611686018427387137 non riesce al piano e 4611686018427386881/4611686018427386880 fallisce su ceil
Wouter

2
Un punto di chiarimento: i risultati delle due opzioni secondarie dell'opzione 2 non sono identici in tutti i casi. Un valore di zero per a fornirà 0 nel primo e 1 nel secondo (che non è la risposta corretta per la maggior parte delle applicazioni).
Sushisource

1
Sei sicuro di non voler dire "Tuttavia, tieni presente che non funziona per a = 0 eb < 1"
dantiston

60

157/32 è int/int, il che si traduce in un file int.

Provare a utilizzare il doppio letterale - 157/32d, che è int/double, che si traduce in una double.


Sei sicuro che int / int risulterà sempre in un int ?! puoi per favore dare una fonte a questo ?!


34

157/32è una divisione intera perché tutti i letterali numerici sono numeri interi se non diversamente specificato con un suffisso ( dper double lper long)

la divisione viene arrotondata per difetto (a 4) prima di essere convertita in un doppio (4.0) che viene quindi arrotondato per eccesso (a 4.0)

se usi una variabile puoi evitarlo

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


7

Nessuno ha menzionato il più intuitivo:

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

Questa soluzione corregge l' imprecisione della doppia divisione.


1
Math.round ritorna lungo
Zulqurnain Jutt

Grazie @ZulqurnainJutt, ha aggiunto un cast
IG Pascual

4

In Java l'aggiunta di uno .0 lo renderà un doppio ...

int total = (int) Math.ceil(157.0 / 32.0);

3

Quando si dividono due numeri interi, ad es.

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

il risultato è un int, il cui valore è adiviso per b, arrotondato verso zero. Poiché il risultato è già arrotondato, ceil()non fa nulla. Nota che questo arrotondamento non è lo stesso di floor(), che arrotonda verso l'infinito negativo. Quindi, è 3/2uguale 1(ed è floor(1.5)uguale 1.0, ma è (-3)/2uguale -1(ma è floor(-1.5)uguale -2.0).

Questo è importante perché, se a/bfosse sempre la stessa floor(a / (double) b), allora si può solo implementare ceil()di a/bcome -( (-a) / b).

Il suggerimento di andare ceil(a/b)da

int n = (a + b - 1) / b;, che è equivalente a a / b + (b - 1) / b, o(a - 1) / b + 1

funziona perché ceil(a/b)è sempre maggiore di uno floor(a/b), tranne quando a/bè un numero intero. Quindi, vuoi spostarlo al (o oltre) il numero intero successivo, a meno che non a/bsia un numero intero. L'aggiunta 1 - 1 / bfarà questo. Per i numeri interi, non li spingerà fino al numero intero successivo. Per tutto il resto, lo farà.

Yikes. Si spera che abbia senso. Sono sicuro che ci sia un modo matematicamente più elegante per spiegarlo.


2

Inoltre per convertire un numero da intero a numero reale puoi aggiungere un punto:

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

E anche il risultato di (157/32.) Sarà reale. ;)



1

Controlla la soluzione di seguito per la tua domanda:

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

Qui dovresti moltiplicare Numerator per 1.0, quindi darà la tua risposta.

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

0

Usa il doppio per lanciare like

Math.ceil((double)value) o simili

Math.ceil((double)value1/(double)value2);

0

Java fornisce solo la divisione dei piani /per impostazione predefinita. Ma possiamo scrivere soffitto in termini di pavimento . Vediamo:

Qualsiasi numero intero ypuò essere scritto con il modulo y == q*k+r. Secondo la definizione di divisione del pavimento (qui floor) che completa r,

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

e della divisione del soffitto (qui ceil) che arrotonda r₁,

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

dove possiamo sostituire r+1per r₁:

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


Quindi sostituiamo la prima equazione con la terza per qottenere

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

Infine, data qualsiasi numero intero yin cui y = q*k+r+1per alcuni q, k, r, abbiamo

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

E abbiamo finito. Spero che questo ti aiuti.


Sono sicuro che sia corretto, ma poiché il punto di questo è chiarire, non mi ceilè chiaro il motivo per cui è definito come tale dalla definizione intuitiva, in particolare dove stiamo prendendo il limite di un intero, cioè r1 = k. Poiché i casi limite sono ciò che è complicato in questo, penso che debba essere spiegato un po 'di più.
Luigi Plinge

@LuigiPlinge Per me la derivazione non può essere più semplice a causa della differenza intrinseca tra pavimento e soffitto nel contesto dell'operazione di divisione. Penso che non sia necessario concentrarsi sul caso limite: è un fatto naturale quando si tenta di unificare le definizioni di pavimento e soffitto scomponendo un numero intero. Di conseguenza, la dimostrazione è solo di tre passaggi e la conclusione può essere ricordata approssimativamente come "un passo indietro ammortizzato, quindi un passo avanti assoluto".
ShellayLee

0

Ci sono due metodi con cui puoi arrotondare il tuo doppio valore.

  1. Math.ceil
  2. Math.floor

Se vuoi la tua risposta 4.90625 come 4 allora dovresti usare Math.floor e se vuoi la tua risposta 4.90625 come 5 allora puoi usare Math.ceil

Puoi fare riferimento al seguente codice per questo.

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);

    }

}

-3
int total = (157-1)/32 + 1

o più generale

(a-1)/b +1 

Penso che funzioni, ma non hai davvero spiegato perché la versione originale non funzionava.
Teepeemm

" Tuttavia, tieni presente che non funziona per a = 0 eb <1 "
IG Pascual
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.