Autore Topic: Calcoli con risultati "bizzarri"  (Letto 423 volte)

Offline wlf

  • Utente normale
  • ***
  • Post: 315
  • Respect: +8
    • Mostra profilo
  • Dispositivo Android:
    Xperia
Calcoli con risultati "bizzarri"
« il: 31 Agosto 2016, 14:52:40 CEST »
0
Eseguendo dei calcoli con l'evaluate di Android Studio ho i seguenti risultati:

Da 0.01 a 0.06 da risultati corretti.
0.07 * 100 = 7.000000000000001
.. risultati corretti.
0.14 * 100 = 14.000000000000002
.. risultati corretti.
0.28 * 100 = 28.000000000000004
0.29 * 100 = 28.999999999999996
.. risultati corretti.
0.55 * 100 = 55.00000000000001
0.56 * 100 = 56.00000000000001
0.57 * 100 = 56.99999999999999
0.58 * 100 = 57.99999999999999
.. risultati corretti.
1.09 * 100 = 109.00000000000001
1.10 * 100 = 110.00000000000001
1.11 * 100 = 111.00000000000001
1.12 * 100 = 112.00000000000001
1.13 * 100 = 112.99999999999999
1.14 * 100 = 113.99999999999999
1.15 * 100 = 114.99999999999999
1.16 * 100 = 115.99999999999999
.. risultati corretti.
2.01 * 100 = 200.99999999999997
.. risultati corretti.
2.03 * 100 = 202.99999999999997
.. risultati corretti.
2.05 * 100 = 204.99999999999997
.. risultati corretti.
2.07 * 100 = 206.99999999999997
.. risultati corretti.
2.18 * 100 = 218.00000000000003
.. risultati corretti.
2.20 * 100 = 220.00000000000003
.. risultati corretti.
2.22 * 100 = 222.00000000000003
.. risultati corretti.
2.24 * 100 = 224.00000000000003
.. risultati corretti.
2.26 * 100 = 225.99999999999997
.. risultati corretti.
2.28 * 100 = 227.99999999999997
.. risultati corretti.
2.30 * 100 = 229.99999999999997
.. risultati corretti.
2.32 * 100 = 231.99999999999997
.. risultati corretti.
2.43 * 100 = 243.00000000000003
.. risultati corretti.
2.45 * 100 = 245.00000000000003
.. risultati corretti.
2.47 * 100 = 247.00000000000003
.. risultati corretti.
2.49 * 100 = 249.00000000000003
etc

Perché solo con alcuni valori si presenta il problema e non con tutti? Perché questi sembrano essere sempre più frequenti?
Per ottenere un risultato corretto con tute le casistiche come bisogna procedere?

Offline Ohmnibus

  • Utente senior
  • ****
  • Post: 583
  • Respect: +130
    • Google+
    • @ohmnibus
    • Mostra profilo
    • Lords of Knowledge GdR
  • Dispositivo Android:
    Samsung Galaxy Nexus
  • Play Store ID:
    Ohmnibus
  • Sistema operativo:
    Windows 7 x64
Re:Calcoli con risultati "bizzarri"
« Risposta #1 il: 08 Settembre 2016, 10:51:26 CEST »
0

E' un errore di approssimazione dei float e, in misura minore, dei double.

Se hai bisogno di calcoli precisi, usa BigDecimal
Ohmnibus
Le mie app su Play Store

È stata trovata una soluzione al tuo problema? Evidenzia il post più utile premendo . È un ottimo modo per ringraziare chi ti ha aiutato.

Offline wlf

  • Utente normale
  • ***
  • Post: 315
  • Respect: +8
    • Mostra profilo
  • Dispositivo Android:
    Xperia
Re:Calcoli con risultati "bizzarri"
« Risposta #2 il: 08 Settembre 2016, 18:38:52 CEST »
0
Se mando in esecuzione il codice sottostante mi da comunque un risultato approssimativo:

Codice: [Seleziona]
new BigDecimal(0.52).multiply(new BigDecimal(1.2))
Risultato 0.6239999999999999982236431605997487464612841100428820882714347172137703267935648909769952297210693359375

Dove sta l'errore in questo caso?

Offline Ohmnibus

  • Utente senior
  • ****
  • Post: 583
  • Respect: +130
    • Google+
    • @ohmnibus
    • Mostra profilo
    • Lords of Knowledge GdR
  • Dispositivo Android:
    Samsung Galaxy Nexus
  • Play Store ID:
    Ohmnibus
  • Sistema operativo:
    Windows 7 x64
Re:Calcoli con risultati "bizzarri"
« Risposta #3 il: 08 Settembre 2016, 20:56:40 CEST »
0

Creare un BigDecimal a partire da un double continua a generare errori di precisione: https://developer.android.com/reference/java/math/BigDecimal.html#BigDecimal(double, java.math.MathContext)

Prova ad usare valori in formato stringa:

Codice (Java): [Seleziona]
new BigDecimal("0.52").multiply(new BigDecimal("1.2"))
Ohmnibus
Le mie app su Play Store

È stata trovata una soluzione al tuo problema? Evidenzia il post più utile premendo . È un ottimo modo per ringraziare chi ti ha aiutato.

Offline wlf

  • Utente normale
  • ***
  • Post: 315
  • Respect: +8
    • Mostra profilo
  • Dispositivo Android:
    Xperia
Re:Calcoli con risultati "bizzarri"
« Risposta #4 il: 09 Settembre 2016, 09:38:36 CEST »
0
Ottimo! Quindi è la conversione di un Double in un BigDecimal
Codice: [Seleziona]
BigDecimal(double val)
che passa comunque il valore errato del Double! :(

Assodato questo, se io ho una serie di dati su un DB SQLite su una tabella in un campo definito come "numeric", facendo un query mi ritorna un Cursor; per prendere i dati sono "obbligato" a pasare per un Cursor.getDouble(), non mi sembra che possa passare direttamente in un BigDecimal con un Cursor.getBigDecimal().

Quindi se passo i miei valori (0.52 e 1.2) recuperati con dei Double in un BigDecimal avrò sempre l'errore! :(

Come faccio ad evitare i Double con un db SQLite? C'è modo di recuperare i miei valori senza avere l'errore di precisione?

Non dovrò passare per una doppia conversione Double-->String-->BigDecimal?

Codice: [Seleziona]
Double val1 = Cursor.getDouble(0); <-- 0.52
Double val2 = Cursor.getDouble(1); <-- 1.2
BigDecimal result = new BigDecimal(String.valueOf(val1)).multiply(new BigDecimal(String.valueOf(val2)));
« Ultima modifica: 09 Settembre 2016, 09:53:15 CEST da wlf »

Offline Ohmnibus

  • Utente senior
  • ****
  • Post: 583
  • Respect: +130
    • Google+
    • @ohmnibus
    • Mostra profilo
    • Lords of Knowledge GdR
  • Dispositivo Android:
    Samsung Galaxy Nexus
  • Play Store ID:
    Ohmnibus
  • Sistema operativo:
    Windows 7 x64
Re:Calcoli con risultati "bizzarri"
« Risposta #5 il: 09 Settembre 2016, 09:42:12 CEST »
+1

Converti in stringa il tuo double:

Codice (Java): [Seleziona]
new BigDecimal(Cursor.getDouble().toString());
Ohmnibus
Le mie app su Play Store

È stata trovata una soluzione al tuo problema? Evidenzia il post più utile premendo . È un ottimo modo per ringraziare chi ti ha aiutato.

Offline wlf

  • Utente normale
  • ***
  • Post: 315
  • Respect: +8
    • Mostra profilo
  • Dispositivo Android:
    Xperia
Re:Calcoli con risultati "bizzarri"
« Risposta #6 il: 09 Settembre 2016, 09:59:06 CEST »
0
Mentre mi rispondevi avevo fatto la stessa ipotesi ... ;)

E' poco lineare passare per  per una doppia conversione Double-->String-->BigDecimal, ma se è l'unica strada percorribile ... :(

Praticamente/sostanzialmente per evitare errori di precisione è "indispensabile" passare sempre per una conversione  Double-->String .... gli altri tipi di conversione invece generano errori ...

Offline Ohmnibus

  • Utente senior
  • ****
  • Post: 583
  • Respect: +130
    • Google+
    • @ohmnibus
    • Mostra profilo
    • Lords of Knowledge GdR
  • Dispositivo Android:
    Samsung Galaxy Nexus
  • Play Store ID:
    Ohmnibus
  • Sistema operativo:
    Windows 7 x64
Re:Calcoli con risultati "bizzarri"
« Risposta #7 il: 09 Settembre 2016, 10:03:30 CEST »
0

Credo che la best practice consista nel lavorare univocamente con i BigDecimal, ed usare le stringhe in fase di memorizzazione su DB
Ohmnibus
Le mie app su Play Store

È stata trovata una soluzione al tuo problema? Evidenzia il post più utile premendo . È un ottimo modo per ringraziare chi ti ha aiutato.

Offline wlf

  • Utente normale
  • ***
  • Post: 315
  • Respect: +8
    • Mostra profilo
  • Dispositivo Android:
    Xperia
Re:Calcoli con risultati "bizzarri"
« Risposta #8 il: 09 Settembre 2016, 10:45:23 CEST »
0
Credo che la best practice consista nel lavorare univocamente con i BigDecimal, ed usare le stringhe in fase di memorizzazione su DB

Addirittura usare le stringhe in fase di memorizzazione su DB?

E se uno volesse fare una query con una SUM o quant'altro dovrebbe prima effettuare una query col Cursor e poi scorrersi tutto il Cursor per effettuare i calcoli?

Facendosi una SUM del campo numeric del DB o qualche altra operazione c'è sempre il pericolo di errori di precisione? SQLite i numeric li tratta come dei Double?

Che senso ha aver fatto un DB con campi di diverso tipo (text, integer, numeric, etc) se poi è una best practice utilizzarlo come string per memorizzare i dati?

Offline Ohmnibus

  • Utente senior
  • ****
  • Post: 583
  • Respect: +130
    • Google+
    • @ohmnibus
    • Mostra profilo
    • Lords of Knowledge GdR
  • Dispositivo Android:
    Samsung Galaxy Nexus
  • Play Store ID:
    Ohmnibus
  • Sistema operativo:
    Windows 7 x64
Re:Calcoli con risultati "bizzarri"
« Risposta #9 il: 09 Settembre 2016, 11:09:23 CEST »
+1

Il problema dei numeri in virgola mobile è molto semplice: sono rappresentati da un numero finito di bit (64) che possono esprimere un numero finito di valori. Se la somma di due di questi valori ricade in un intervallo non rappresentabile, avviene un arrotondamento. E' lo stesso concetto di quando si divide un intero per 2: puoi avere un arrotondamento o no.

Lo stesso problema ovviamente esiste nei database.

Se si ha la necessità di lavorare con precisioni maggiori è necessario adottare misure alternative, come i BigDecimal. La differenziazione dei dati è una questione di comodità, ma come detto il concetto decade se si hanno necessità specifiche.

Per la questione delle aggregazioni non so darti una risposta definitiva. Di nuovo, se hai bisogno di assoluta precisione forse è il caso di scorrere tutta la tabella e sommare i valori. In questo caso può tornare di aiuto una tabella di appoggio dove pre-calcoli queste somme man mano che inserisci i record nel db. Non esiste una risposta univoca, in quanto dipende molto da ciò che vuoi ottenere.

Nota, comunque, che la tipizzazione di SQLite è molto soft, e da specifiche (non ho mai provato) puoi tranquillamente salvare una stringa in una colonna dichiarata NUMERIC:
Citazione
A column with NUMERIC affinity may contain values using all five storage classes. When text data is inserted into a NUMERIC column, the storage class of the text is converted to INTEGER or REAL (in order of preference)

Fonte: https://www.sqlite.org/datatype3.html

Come poi SQLite tratta questi valori nelle aggregazioni non te lo so dire. Ti consiglio di fare delle prove, ed al peggio evitare le aggregazioni.
Ohmnibus
Le mie app su Play Store

È stata trovata una soluzione al tuo problema? Evidenzia il post più utile premendo . È un ottimo modo per ringraziare chi ti ha aiutato.

Offline wlf

  • Utente normale
  • ***
  • Post: 315
  • Respect: +8
    • Mostra profilo
  • Dispositivo Android:
    Xperia
Re:Calcoli con risultati "bizzarri"
« Risposta #10 il: 09 Settembre 2016, 11:56:24 CEST »
0
Come poi SQLite tratta questi valori nelle aggregazioni non te lo so dire. Ti consiglio di fare delle prove, ed al peggio evitare le aggregazioni.

La documentazione non lascia dubbi .... :(

Citazione
The result of sum() is an integer value if all non-NULL inputs are integers. If any input to sum() is neither an integer or a NULL then sum() returns a floating point value which might be an approximation to the true sum.

La certezza è che si otterrà un valore approssimato; quindi le strade "percorribili" sembrano essere 2:

1) O farsi la somma dei valori BigDecimal in un ciclo dopo averli ripresi dal Cursor della query.
2) O memorizzare tutto come interi e quindi eseguire una somma di interi.

Nel secondo caso dovrò gestirmi il numero di cifre decimali ...

Offline Ohmnibus

  • Utente senior
  • ****
  • Post: 583
  • Respect: +130
    • Google+
    • @ohmnibus
    • Mostra profilo
    • Lords of Knowledge GdR
  • Dispositivo Android:
    Samsung Galaxy Nexus
  • Play Store ID:
    Ohmnibus
  • Sistema operativo:
    Windows 7 x64
Re:Calcoli con risultati "bizzarri"
« Risposta #11 il: 09 Settembre 2016, 12:06:51 CEST »
0

Visto che si sta parlando di precisione, quello della virgola fissa è una soluzione che volutamente non ho proposto prima.

Considera che con la virgola fissa hai la certezza di avere un errore in caso di divisione, nello stesso identico modo di come avviene per gli interi:

12.17 / 2 = 6.08 (invece di 6.085)
12.17 / 3 = 4.05 (invece di 4.05666666...)

Se invece lavori solo con somme e sottrazioni, sei a posto.
Ohmnibus
Le mie app su Play Store

È stata trovata una soluzione al tuo problema? Evidenzia il post più utile premendo . È un ottimo modo per ringraziare chi ti ha aiutato.

Offline wlf

  • Utente normale
  • ***
  • Post: 315
  • Respect: +8
    • Mostra profilo
  • Dispositivo Android:
    Xperia
Re:Calcoli con risultati "bizzarri"
« Risposta #12 il: 09 Settembre 2016, 13:00:47 CEST »
0
Facendo sostanzialmente soprattutto somme e sottrazioni sarebbe perfetto; per le moltiplicazioni potrei optare sul BigDecimal (1.2kg di mele per 0,52€ al kg come nell'esempio iniziale).

Ma in questo caso sarebbe come si fa ad utilizzare la virgola fissa?

Offline Ohmnibus

  • Utente senior
  • ****
  • Post: 583
  • Respect: +130
    • Google+
    • @ohmnibus
    • Mostra profilo
    • Lords of Knowledge GdR
  • Dispositivo Android:
    Samsung Galaxy Nexus
  • Play Store ID:
    Ohmnibus
  • Sistema operativo:
    Windows 7 x64
Re:Calcoli con risultati "bizzarri"
« Risposta #13 il: 09 Settembre 2016, 13:11:22 CEST »
0
Nulla di che: ragioni come se avessi degli interi, ma prima di visualizzare dividi tutto per un fattore predefinito (o dopo aver letto da input, moltiplichi), es 100 se usi due valori decimali.

Per cui 1.2 sarà rappresentato in memoria come 120, 45 come 4500 e 0.12 come 12.
Ohmnibus
Le mie app su Play Store

È stata trovata una soluzione al tuo problema? Evidenzia il post più utile premendo . È un ottimo modo per ringraziare chi ti ha aiutato.

Offline wlf

  • Utente normale
  • ***
  • Post: 315
  • Respect: +8
    • Mostra profilo
  • Dispositivo Android:
    Xperia
Re:Calcoli con risultati "bizzarri"
« Risposta #14 il: 09 Settembre 2016, 14:59:20 CEST »
0
A ok!
Pensavo che tu stessi dicendo con un numero decimali fisso, non utilizzando gli interi moltiplicando/dividendo 10 elevato al numero di decimali a seconda che si tratti rispettivamente di input/output.

Mi era sfuggito quale fosse il tipo variabile con un numero di decimali fisso.  ;)