Modulo con doppi in Java

Come gestisci il comportamento strano di Java con l’operatore modulo quando usi il doppio?

Ad esempio, ti aspetteresti che il risultato di 3.9 - (3.9 % 0.1) fosse 3.9 (e in effetti, Google dice che non sto impazzendo), ma quando lo 3.8000000000000003 in Java ottengo 3.8000000000000003 .

Capisco che questo è il risultato di come i negozi e i processi di Java raddoppiano, ma c’è un modo per aggirarlo?

Usa un tipo preciso se hai bisogno di un risultato preciso:

  double val = 3.9 - (3.9 % 0.1); System.out.println(val); // 3.8000000000000003 BigDecimal x = new BigDecimal( "3.9" ); BigDecimal bdVal = x.subtract( x.remainder( new BigDecimal( "0.1" ) ) ); System.out.println(bdVal); // 3.9 

Perché 3.8000 … 003? Perché Java utilizza FPU per calcolare il risultato. 3.9 è imansible memorizzare esattamente nella notazione a doppia precisione IEEE, quindi memorizza 3.89999 … invece. E 3.8999% 0.01 dà 0.09999 … quindi il risultato è un po ‘più grande di 3.8.

Dalla specifica del linguaggio Java :

Il risultato di un’operazione remainder a virgola mobile come calcolato dall’operatore% non è uguale a quello prodotto dall’operazione resto definita da IEEE 754. L’operazione IEEE 754 remainder calcola il resto da una divisione di arrotondamento, non una divisione troncante e quindi il suo comportamento non è analogo a quello del solito operatore di resto intero. Invece, il linguaggio di programmazione Java definisce% sulle operazioni in virgola mobile per comportarsi in modo analogo a quello dell’operatore di resto intero; questo può essere confrontato con la funzione di libreria C fmod. L’operazione di resto IEEE 754 può essere calcasting dalla routine di libreria Math.IEEEremainder.

In altre parole, ciò è dovuto al fatto che Java arrotonda il risultato della divisione coinvolta nel calcolo del resto, mentre IEEE754 specifica il troncamento della risposta della divisione. Questo caso particolare sembra esporre questa differenza molto chiaramente.

Puoi ottenere la risposta che ti aspetti utilizzando Math.IEEEremainder:

 System.out.println(3.9 - (3.9 % 0.1)); System.out.println(3.9 - Math.IEEEremainder(3.9, 0.1)); 

È ansible utilizzare java.math.BigDecimal e il suo metodo divideAndRemainder ().

Se conosci la quantità di decimali con cui hai a che fare, puoi provare prima a convertire in interi. Questo è solo un caso classico di inacuracia in virgola mobile. Invece di fare il 3.9 % 0.1 stai facendo qualcosa come 3.899 % 0.0999