2010-07-12 5 views
11

Come si gestisce il comportamento strano di Java con l'operatore modulo quando si utilizza il doppio?Modulo con doppi in Java

Per esempio, ci si aspetta il risultato di 3.9 - (3.9 % 0.1) di essere 3.9 (e in effetti, Google dice che non sto impazzendo), ma quando l'eseguo in Java ottengo 3.8000000000000003.

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

+1

Cosa stai cercando di fare? Ho la sensazione che tu non abbia davvero bisogno di '%'. – polygenelubricants

risposta

15

utilizzare un tipo preciso se avete 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 è impossibile 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.

+0

+1 Odio lavorare con i numeri in virgola mobile IEEE a mano nella mia classe di Ingegneria Elettrica, ma ne è valsa la pena. –

+0

Grazie, lo farò bene. – Andy

+0

Bella risposta. Quindi, perché la FPU è in grado di capire che '((3.9D/0.1D)% 1D) = 0.0' (fondamentalmente lo stesso problema leggermente riorganizzato) quando a causa di un errore di arrotondamento pensa '(3.9D% 0.1D) = 0.0999999999999997'? –

1

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

+1

E Google affermerà che il 3.9% 0.1 = -4.4408921 × 10-16 che non si adatta perfettamente al modulo. – Stroboskop

2

Da The Java Language Specification:

Il risultato di una virgola mobile funzionamento rimanente calcolata dall'operatore % non è lo stesso di quello prodotta dall'operazione resto definito da IEEE 754. L'IEEE L'operazione residua 754 calcola il resto da una divisione di arrotondamento, non una divisione troncante, e quindi il suo comportamento non è analogo a quello di il solito operatore di resto intero. Invece, il linguaggio di programmazione Java definisce% sulle operazioni in virgola mobile comportarsi in modo analogo a quello dell'interprete restante intero ; questo può essere confrontato con la funzione di libreria C fmod. L'operazione di resto 751 IEEE può essere calcolata 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.

È possibile ottenere la risposta vi aspettate utilizzando Math.IEEEremainder:

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