2012-07-27 6 views
7

Quando si gestiscono valori in virgola mobile in Java, la chiamata al metodo toString() fornisce un valore stampato con il numero corretto di cifre significative in virgola mobile. Tuttavia, in C++, la stampa di un float tramite stringstream arrotonda il valore dopo 5 o meno cifre. C'è un modo per "stampare bene" un float in C++ al numero corretto (presunto) di cifre significative?Come calcolare il numero di cifre decimali significative di un doppio C++?


EDIT: Penso di essere frainteso. Voglio che l'output sia di lunghezza dinamica, non una precisione fissa. Conosco la setprecision. Se guardi il sorgente Java per Double, calcola il numero di cifre significative in qualche modo, e mi piacerebbe davvero capire come funziona e/o quanto sia fattibile replicarlo facilmente in C++.

/* 
* FIRST IMPORTANT CONSTRUCTOR: DOUBLE 
*/ 
public FloatingDecimal(double d) 
{ 
    long dBits = Double.doubleToLongBits(d); 
    long fractBits; 
    int  binExp; 
    int  nSignificantBits; 

    // discover and delete sign 
    if ((dBits&signMask) != 0){ 
     isNegative = true; 
     dBits ^= signMask; 
    } else { 
     isNegative = false; 
    } 
    // Begin to unpack 
    // Discover obvious special cases of NaN and Infinity. 
    binExp = (int)((dBits&expMask) >> expShift); 
    fractBits = dBits&fractMask; 
    if (binExp == (int)(expMask>>expShift)) { 
     isExceptional = true; 
     if (fractBits == 0L){ 
      digits = infinity; 
     } else { 
      digits = notANumber; 
      isNegative = false; // NaN has no sign! 
     } 
     nDigits = digits.length; 
     return; 
    } 
    isExceptional = false; 
    // Finish unpacking 
    // Normalize denormalized numbers. 
    // Insert assumed high-order bit for normalized numbers. 
    // Subtract exponent bias. 
    if (binExp == 0){ 
     if (fractBits == 0L){ 
      // not a denorm, just a 0! 
      decExponent = 0; 
      digits = zero; 
      nDigits = 1; 
      return; 
     } 
     while ((fractBits&fractHOB) == 0L){ 
      fractBits <<= 1; 
      binExp -= 1; 
     } 
     nSignificantBits = expShift + binExp +1; // recall binExp is - shift count. 
     binExp += 1; 
    } else { 
     fractBits |= fractHOB; 
     nSignificantBits = expShift+1; 
    } 
    binExp -= expBias; 
    // call the routine that actually does all the hard work. 
    dtoa(binExp, fractBits, nSignificantBits); 
} 

Dopo questa funzione, si chiama dtoa(binExp, fractBits, nSignificantBits); che gestisce una serie di casi - questo è da openjdk6


Per maggiore chiarezza, un esempio: Java:

double test1 = 1.2593; 
double test2 = 0.004963; 
double test3 = 1.55558742563; 

System.out.println(test1); 
System.out.println(test2); 
System.out.println(test3); 

uscita :

1.2593 
0.004963 
1.55558742563 

C++:

std::cout << test1 << "\n"; 
std::cout << test2 << "\n"; 
std::cout << test3 << "\n"; 

uscita:

1.2593 
0.004963 
1.55559 
+3

Il metodo 'toString' non dà il "numero corretto" di figure significative; non ha modo di sapere quale delle figure sia significativa. I numeri in virgola mobile IEEE non rappresentano tali informazioni. –

+0

Per riferimento, ecco un approccio Java comune a [* Customizing Formats *] (http://docs.oracle.com/javase/tutorial/i18n/format/decimalFormat.html). – trashgod

+0

Java esegue una serie di calcoli sul doppio valore durante la stampa: non sono abbastanza esperto per capire cosa sta facendo. Vedi edit – dave

risposta

5

penso che si sta parlando di come stampare il numero minimo di cifre in virgola mobile che consentono di leggere esattamente lo stesso numero in virgola mobile indietro. Questo documento è una buona introduzione a questo delicato problema.

http://grouper.ieee.org/groups/754/email/pdfq3pavhBfih.pdf

La funzione dtoa si presenta come lavoro di David Gay, è possibile trovare la fonte qui http://www.netlib.org/fp/dtoa.c (anche se questo è C non è Java).

Gay ha anche scritto un articolo sul suo metodo. Non ho un link ma è referenziato nel documento di cui sopra, quindi puoi probabilmente farlo su Google.

+0

Grazie! Mi sento come se fossi l'unica persona a ottenere ciò di cui stavo parlando. – dave

0

È possibile utilizzare la tecnica ios_base :: precisione cui è possibile specificare il numero di cifre che si desidera

Per esempio

#include <iostream> 
using namespace std; 

int main() { 
double f = 3.14159; 
cout.unsetf(ios::floatfield);   // floatfield not set 
cout.precision(5); 
cout << f << endl; 
cout.precision(10); 
cout << f << endl; 
cout.setf(ios::fixed,ios::floatfield); // floatfield set to fixed 
cout << f << endl; 
return 0; 

Il codice sopra riportato con uscita
3,1416
3,14159
3,1415900000

+0

Quello che sto cercando di evitare sono gli zeri finali nell'esempio. Non voglio un valore fisso per la mia uscita. – dave

0

esiste un'utility chiamata numeric_limits:

#include <limits> 

    ... 
    int num10 = std::numeric_limits<double>::digits10; 
    int max_num10 = std::numeric_limits<double>::max_digits10; 

Si noti che i numeri IEEE non sono rappresentate le cifre esattamente bydecimal. Queste sono quantità binarie.Un numero più preciso è il numero di bit binari:

int bits = std::numeric_limits<double>::digits; 

Per stampare praticamente tutte le cifre significative utilizzano setprecision con questo:

out.setprecision(std::numeric_limits<double>::digits10); 
+0

questo non è quello che sto cercando – dave

+0

OK, ho capito. Vuoi numeri che tracciano la precisione dalla costruzione attraverso operazioni numeriche? Lo standard C++ non ha questo. Ci sono sforzi per aggiungere numeri multi-precisione. Penso che il tuo requisito sia ragionevole per tali numeri. Fondamentalmente, i numeri in virgola mobile fondamentali non hanno le intelligenze per questo e non lo faranno mai. – emsr

+0

Controllare questo: http://www.holoborodko.com/pavel/mpfr/. Penso che questo sia quello che vuoi. In bocca al lupo. – emsr