2014-09-29 4 views
6

Ci sono alcune operazioni aritmetiche o bit a bit che possono controllare se un doppio si adatta a un float senza perdita di precisione .Con quale velocità controllare se il doppio si adatta al galleggiante? (Java)

Dovrebbe non solo verificare che il campo doppio sia nell'intervallo float, ma anche che non si perda alcun bit di mantissa .

Bye

P.S .: Questo risponde il problema a metà strada per C#: How to check if a double can fit into a float without conversion to infinity ma ho bisogno di una soluzione che funziona per Java.

+1

Perché questa sarebbe una buona idea? Sicuramente la memoria non può essere la tua preoccupazione. Qual è la giustificazione tecnica per questo? – duffymo

+2

Ad esempio, durante la serializzazione di milioni di doppi. Se riesci a risparmiare i byte usando i float, potresti guadagnare molta velocità o riduzione dello spazio. Particolarmente interessante su reti lente o supporti lenti – RobAu

risposta

5

ne dite di questo:

double d = ...; 
if ((double)(float)d == d) { 
    System.out.println(d + " fits into float!"); 
} 

L'idea è abbastanza semplice: Abbiamo appena fusi a float e poi di nuovo a double e controllare se il risultato è sempre lo stesso. Se d non si adatta a float, allora una certa precisione sarebbe stata persa con il cast (float)d e quindi il risultato sarebbe stato diverso.

In senso stretto, il cast di nuovo a double non è necessario in quanto l'operatore di confronto eseguirà questo cast implicitamente, quindi (float)d == d va bene.

Se si è preoccupati per le prestazioni di questo, perché molte operazioni float sono molto più lente rispetto alle operazioni int paragonabili: questo è praticamente un problema. La conversione tra float e double è estremamente efficiente nelle moderne CPU. Può anche essere vettorializzato! Ci sono le istruzioni cvtpd2ps e cvtps2pd nel set di istruzioni SSE2 che esegue la conversione da doppio a float e viceversa (4 valori vengono convertiti contemporaneamente). Le istruzioni hanno una latenza di 4 cicli su tutte le CPU Intel che le supportano. 4 cicli per 4 conversioni è estremamente veloce.

+0

Votazione poiché è una soluzione. Ma ancora un po 'titubante per usarlo, questo significa due FOP. –

+1

@CookieMonster: la FPU della CPU di solito è abbastanza veloce. Troncare 'double' to' float' è un compito abbastanza semplice. Dubito che qualsiasi spostamento magico (se esiste) sarà più veloce di questo. – gexicide

+1

@CookieMonster: ho cercato le latenze delle istruzioni del cast vettorializzate. Sono abbastanza buoni (vedi il mio ultimo paragrafo modificato nella risposta). Non ho le latenze delle controparti non vettorializzate, ma sono abbastanza sicuro che siano altrettanto veloci. – gexicide

6

Una soluzione straight-forward potrebbe essere la seguente:

public class Scribble { 
    public static void main(String[] args) { 
     for (int i = 1; i <= 10; i++) { 

      double d = 1d/((double)i); 
      float f = (float) d; 

      boolean lossless = d == f; 

      System.out.println(d + " can be converted " + (lossless ? "lossless" : "only with loss")); 
     } 
    } 
} 

emette:

1.0 can be converted lossless 
0.5 can be converted lossless 
0.3333333333333333 can be converted only with loss 
0.25 can be converted lossless 
0.2 can be converted only with loss 
0.16666666666666666 can be converted only with loss 
0.14285714285714285 can be converted only with loss 
0.125 can be converted lossless 
0.1111111111111111 can be converted only with loss 
0.1 can be converted only with loss 

edit: spettacoli di confronto di velocità, che method2 sembra essere più veloce:

method1 | method2 | method3 
237094654 | 209365345 | 468025911 
214129288 | 209917275 | 448695709 
232093486 | 197637245 | 448153336 
249210162 | 200163771 | 460200921 
240685446 | 200638561 | 447061763 
332890287 | 337870633 | 450452194 
247054322 | 199045232 | 449442540 
235533069 | 200767924 | 452743201 
256274670 | 199153775 | 453373979 
298277375 | 198659529 | 456672251 
229360115 | 205883096 | 454198291 
25268| 224850463 | 452860277 
246047739 | 200070587 | 458091501 
304270790 | 204517093 | 463688631 
235058620 | 204675812 | 448639390 
260565871 | 205834286 | 458372075 
256008432 | 242574024 | 498943242 
311210028 | 208080237 | 478777466 
242014926 | 208995343 | 457901380 
239893559 | 205111348 | 451616471 

codice:

public class Scribble { 

    static int size = 1024*1024*100; 
    static boolean[] results = new boolean[size]; 
    static double[] values = new double[size]; 

    public static void main(String[] args) { 

     // generate values 
     for (int i = 0; i < size; i++) 
      values[i] = 1d/((double)i); 

     long start; 
     long duration; 

     System.out.println(" method1 | method2 | method3 "); 
     for (int i = 0; i < 20; i++) { 
      start = System.nanoTime(); 
      method1(size); 
      duration = System.nanoTime() - start; 
      System.out.printf("%9d", duration); 

      start = System.nanoTime(); 
      method2(size); 
      duration = System.nanoTime() - start; 
      System.out.printf(" | %9d", duration); 

      start = System.nanoTime(); 
      method3(size); 
      duration = System.nanoTime() - start; 
      System.out.printf(" | %9d\n", duration); 
     } 
    } 

    private static void method1(int size) { 
     boolean[] results = new boolean[size]; 
     for (int i = 0; i < size; i++) { 
      double d = values[i]; 
      float f = (float) d; 

      boolean lossless = d == f; 
      results[i] = lossless; 
     } 
    } 

    private static void method2(int size) { 
     for (int i = 0; i < size; i++) { 
      double d = values[i]; 
      results[i] = d == (double)(float)d; 
     } 
    } 

    private static void method3(int size) { 
     for (int i = 0; i < size; i++) { 
      double d = values[i]; 
      results[i] = Double.compare(d, (float) d) == 0; 
     } 
    } 
} 
+1

Se d-f == 0 è uguale a d == f. Dovrebbe essere qui? –

+0

Potrebbe essere che i denormali sarebbero stati necessari se d-f == 0 fosse usato. Vedi anche: https://randomascii.wordpress.com/2012/05/20/thats-not-normalthe-performance-of-odd-floats/ "La necessità dei denormali". Ma non sono ancora sicuro se questo accada anche nel caso particolare di andare tra galleggiante e doppio. –

1

Analogamente alla colata del numero di float e di nuovo a double e verifica di parità (==), può venir usata Double.compare() anche:

double d = 2/3.0; 

// 0 means OK, d fits into float 
if (Double.compare(d, (float) d) == 0) 
    System.out.println("OK, fits into float."); 

Inoltre, poiché confrontando un float per double sarà implicitamente pressofuso float a double, possiamo semplicemente scrivere:

if ((float) d == d) 
    System.out.println("OK, fits into float."); 
1

Se vuoi sapere se il tuo doppio valore si adatta alla gamma MAX e MIN di float, non puoi usare cast come (float)d == d perché d potrebbe essere adatto agli intervalli float ma non necessario ha gli stessi decimali dopo il cast.

In quanto caso si deve confrontare con Float.MAX_VALUE e Float.MIN_VALUE

`return d <= Float.MAX_VALUE && d>0 Float.MIN_VALUE`; 
+0

Non solo mantiene i numeri più grandi/più piccoli di quelli mobili, ma ha anche * una maggiore precisione *. – slartidan