2015-08-12 30 views
12

Java 8 ci ha fornito Math.addExact() per numeri interi ma non decimali.Overflow doppio o BigDecimal?

E 'possibile che double e BigDecimal superino? Giudicando da Double.MAX_VALUE e da How to get biggest BigDecimal value Direi che la risposta è sì.

Come tale, perché non abbiamo anche Math.addExact() per questi tipi? Qual è il modo più gestibile per controllarlo?

+2

Nulla di precisione finita può, teoricamente, troppopieno. –

+5

Il supporto di addExact for double è molto più difficile in quanto è una rappresentazione approssimativa in primo luogo.Non è nemmeno possibile aggiungere 0,1 e 0,2 esattamente. L'overflow per BigDecimal è così grande che è probabile che si esaurisca prima la memoria. –

+2

Se con il doppio si va troppo lontano da non poter più essere rappresentato si ottiene un valore 'Infinity'. Quello sarebbe un trabocco. –

risposta

8

double overflow a Infinity e -Infinity, non si avvolge. BigDecimal non ha overflow, periodo, è limitato solo dalla quantità di memoria nel tuo computer. Vedere: How to get biggest BigDecimal value

L'unica differenza tra + e .addExact è che tenta di rilevare se si è verificato un overflow e genera un'eccezione invece di un wrapping. Ecco il codice sorgente:

public static int addExact(int x, int y) { 
    int r = x + y; 
    // HD 2-12 Overflow iff both arguments have the opposite sign of the result 
    if (((x^r) & (y^r)) < 0) { 
     throw new ArithmeticException("integer overflow"); 
    } 
    return r; 
} 

Se si desidera verificare che si è verificato un overflow, in un certo senso è più semplice farlo con double comunque, perché si può semplicemente verificare la presenza di Double.POSITIVE_INFINITY o Double.NEGATIVE_INFINITY; nel caso di int e long, è una questione un po 'più complicata perché non è sempre un valore fisso, ma in un altro, questi potrebbero essere input (ad esempio Infinity + 10 = Infinity e probabilmente non si vuole lanciare un'eccezione in questo caso) .

Per tutti questi motivi (e non abbiamo ancora menzionato lo NaN), questo è probabilmente il motivo per cui un tale metodo addExact non esiste nel JDK. Naturalmente, è sempre possibile aggiungere la propria implementazione a una classe di utilità nella propria applicazione.

+0

Quindi, per fare lo stesso per il doppio, sostituire le condizioni if ​​con 'if (r == Double.POSITIVE_INFINITY || r == Double.NEGATIVE_INFINITY)'? – Gili

+0

Per quanto riguarda 'BigDecimal', in teoria hai ragione. In pratica, esiste un limite di implementazione: http://stackoverflow.com/a/6792114/14731. – Gili

+0

@Gili Non proprio, perché dovresti consentire quel risultato se uno degli operandi fosse infinito - e cosa faresti se avessi aggiunto POSITIVE_INFINITY e NEGATIVE_INFINITY (che risulta in NaN, non infinito)? Questo dovrebbe essere considerato come "overflow"? Fondamentalmente, i doppi hanno un comportamento più complicato, e quindi sono più difficili da interpretare ordinatamente in risultati "ragionevoli" e "irragionevoli". – yshavit

3

La ragione per cui non è necessaria la funzione addExact per le cifre in virgola mobile è perché, invece di avvolgersi, viene portata a Double.Infinity.

Di conseguenza è possibile controllare molto facilmente al termine dell'operazione in caso di overflow o meno. Dal momento che Double.POSITIVE_INFINITY + Double.NEGATIVE_INFINITY è NaN è anche necessario verificare la presenza di NaN in caso di espressioni più complicate.

Questo non è solo più veloce ma anche più facile da leggere. Invece di dover Math.addExact(Math.addExact(x, y), z) aggiungere 3 doppie insieme, si può invece scrivere:

double result = x + y + z; 
if (Double.isInfinite(result) || Double.isNan(result)) throw ArithmeticException("overflow"); 

BigDecimal d'altra parte volontà davvero troppo pieno e gettare un corrispondente un'eccezione in questo caso, come pure - questo è molto improbabile che accada mai in pratica però.

+2

... che è reso possibile dal fatto che le operazioni su 'Infinito' sono ben definite, quindi' Infinity + qualsiasi numero diverso da -Infinity' sarà ancora 'Infinity'. È possibile eseguire qualsiasi serie di operazioni, se si trabocca in un punto intermedio, non si otterrà mai un risultato falso ma valido: sarà sempre uno degli infiniti o 'NaN'. Questo è in contrasto con i tipi interi, dove questo può accadere. – biziclop

+0

@biziclop Per essere precisi, dovresti verificare la presenza di 'NaN' anche in caso di espressioni più complicate. – Voo

3

Per double, consultare le altre risposte.

BigDecimal ha la protezione addExact() già costruito in molti metodi un'operazione aritmetica (ad esempio multiply) di BigDecimal contengono un controllo sulla scala del risultato:.

private int checkScale(long val) { 
    int asInt = (int)val; 
    if (asInt != val) { 
     asInt = val>Integer.MAX_VALUE ? Integer.MAX_VALUE : Integer.MIN_VALUE; 
     BigInteger b; 
     if (intCompact != 0 && 
      ((b = intVal) == null || b.signum() != 0)) 
      throw new ArithmeticException(asInt>0 ? "Underflow":"Overflow"); 
    } 
    return asInt; 
}