Come posso 'tagliare' un C # doppia per il valore che verrà memorizzato come in un database SQLite?
-
25-09-2019 - |
Domanda
Ho notato che quando memorizzare un valore doppio come ad esempio x = 0.56657011973046234
in un database SQLite, e quindi recuperare in un secondo momento, ho y = 0.56657011973046201
. Secondo il SQLite spec e la . NET spec (nessuno dei quali ho inizialmente preso la briga di leggere :) questo è previsto e normale.
Il mio problema è che, mentre ad alta precisione non è importante, le mie offerte app con gli utenti inserendo / selezionando doppie che rappresentano informazioni 3D di base, e quindi l'esecuzione di simulazioni su di loro per trovare un risultato. E questo ingresso può essere salvato in un database SQLite essere ricaricato e rieseguire tardi.
La confusione si verifica perché una serie appena creato di ingressi ovviamente simulare in modo leggermente diverso a quegli stessi ingressi volta memorizzati e ricaricati (come valori doppi sono cambiati). Questo è logico, ma non desiderabile.
Non ho abbastanza venire a termini di come trattare con questo, ma nel frattempo mi piacerebbe limite / bloccare gli input dell'utente a valori che possono essere esattamente memorizzati in un database SQLite. Quindi, se un input dell'utente 0.56657011973046234
, è in realtà trasformato in 0.56657011973046201
.
Tuttavia non sono stato in grado di capire, dato un numero, quale valore viene memorizzato nel database, corto di effettivamente memorizzare e recuperare dal database, che sembra goffa. C'è un modo stabilito di fare questo?
Soluzione
doppio turno ha un'implementazione con un parametro che specifica il numero di cifre. Usare questo per turno a 14 cifre (diciamo) con: rval = Math.round (Val, 14)
Poi, intorno quando si riceve il valore dal database, e all'inizio di simulazioni, per esempio. Così i valori corrispondono?
Per ulteriori dettagli:
http://msdn.microsoft.com/en-us/library/ 75ks3aby.aspx
Un altro pensiero se non si stanno confrontando i valori nel database, solo la loro memorizzazione: Perché non semplicemente memorizzarle come dati binari? Poi tutti i bit saranno memorizzati e recuperati Verbatim?
Altri suggerimenti
La risposta potrebbe essere quella di memorizzare i valori doppie 17 significativi stringhe di cifre. Guardate la differenza tra come SQLite gestisce i numeri reali vs. testo (io illustrare con l'interfaccia a riga di comando, per semplicità):
sqlite> create table t1(dr real, dt varchar(25));
sqlite> insert into t1 values(0.56657011973046234,'0.56657011973046234');
sqlite> select * from t1;
0.566570119730462|0.56657011973046234
Memorizzazione con vera affinità è la causa del problema - SQLite dà solo di eseguire un'approssimazione di 15 cifre. Se invece si memorizza come testo, è possibile recuperare la stringa originale con il vostro programma C # e riconvertirlo in doppio originale.
Supponendo che sia SQL Lite e NET attuare correttamente la specifica IEEE, si dovrebbe essere in grado di ottenere gli stessi risultati numerici se si è utilizzato lo stesso tipo virgola mobile su entrambi i lati (perché il valore non deve essere modificato quando passato da database per C # e viceversa).
Al momento si sta utilizzando 8 byte IEEE in virgola mobile (singolo) (*) in SQL Lite e 16 byte in virgola mobile in C # (doppio). Il tipo float
in C # corrisponde alla 8 byte standard IEEE, in modo da utilizzare questo tipo anziché double
potrebbe risolvere il problema.
(*) La documentazione di SQL Lite dice che il Real è un valore in virgola mobile, memorizzato come un numero in virgola mobile IEEE 8 byte .
È possibile utilizzare una stringa per memorizzare il # nel db. Personalmente ho fatto quello che Winwaed suggerito di arrotondare prima di riporlo e dopo il recupero dal db (che ha usato numerica ()).
I ricordo di essere stato bruciato dai banchieri di arrotondamento, ma potrebbe essere solo che non ha soddisfatto le specifiche.
È possibile memorizzare il doppio come una stringa, e utilizzando la formattazione di andata e ritorno durante la conversione del doppio in una stringa, è garantito per generare lo stesso valore quando analizzato:
string formatted = theDouble.ToString("R", CultureInfo.Invariant);
Se si desidera che i valori di input decimali per andata e ritorno, allora si dovrà limitare a 15 cifre significative. Se si desidera che i valori in virgola mobile al SQLite interna a doppia precisione per andata e ritorno, allora si potrebbe essere fuori di fortuna; che richiede la stampa ad un minimo di 17 cifre significative, ma da quello che posso dire, SQLite li stampa a un massimo di 15 ( Modifica :? forse un esperto SQLite può confermare questo ho appena letto il codice sorgente e tracciato esso -. avevo ragione, la precisione è limitata a 15 cifre)
Ho provato il vostro esempio nella interfaccia di comando SQLite su Windows. Ho inserito ,56657011973046234, e selezionare tornato 0,566570119730462. In C, quando ho assegnato ,566570119730462 ad un doppio e stampato a 17 cifre, ho ottenuto 0,56657011973046201; che è lo stesso valore che si ottiene da C #. 0,56657011973046234 e ,56657011973046201 mappa per diversi numeri in virgola mobile, quindi in altre parole, lo SQLite non fa il doppio di andata e ritorno.