Forse avete bisogno di eseguire il debug di un'implementazione di un algoritmo in cui si può avere commesso un errore di codifica e vogliono tracciare i calcoli in virgola mobile in corso. Forse hai bisogno di un gancio per ispezionare tutti i valori su cui si sta operando, cercando i valori che sembrano fuori dalla portata che ti aspetti. In C++ puoi definire la tua classe floating point
e utilizzare l'overloading dell'operatore per scrivere i tuoi calcoli in modo naturale, pur mantenendo la possibilità di ispezionare tutti i calcoli.
Ad esempio, ecco un programma che definisce una classe FP
e stampa tutte le aggiunte e le moltiplicazioni.
#include <iostream>
struct FP {
double value;
FP(double value) : value(value) {}
};
std::ostream & operator<< (std::ostream &o, const FP &x) { o << x.value; return o; }
FP operator+(const FP & lhs, const FP & rhs) {
FP sum(lhs.value + rhs.value);
std::cout << "lhs=" << lhs.value << " rhs=" << rhs.value << " sum=" << sum << std::endl;
return sum;
}
FP operator*(const FP & lhs, const FP & rhs) {
FP product(lhs.value * rhs.value);
std::cout << "lhs=" << lhs.value << " rhs=" << rhs.value << " product=" << product << std::endl;
return product;
}
int main() {
FP x = 2.0;
FP y = 3.0;
std::cout << "answer=" << x + 2 * y << std::endl;
return 0;
}
che stampa
lhs=2 rhs=3 product=6
lhs=2 rhs=6 sum=8
answer=8
Aggiornamento: Ho migliorato il programma (x86) per mostrare le flag di stato a virgola mobile dopo ogni operazione in virgola mobile (solo implementato addizione e moltiplicazione, altri potrebbe essere facilmente aggiunto).
#include <iostream>
struct MXCSR {
unsigned value;
enum Flags {
IE = 0, // Invalid Operation Flag
DE = 1, // Denormal Flag
ZE = 2, // Divide By Zero Flag
OE = 3, // Overflow Flag
UE = 4, // Underflow Flag
PE = 5, // Precision Flag
};
};
std::ostream & operator<< (std::ostream &o, const MXCSR &x) {
if (x.value & (1<<MXCSR::IE)) o << " Invalid";
if (x.value & (1<<MXCSR::DE)) o << " Denormal";
if (x.value & (1<<MXCSR::ZE)) o << " Divide-by-Zero";
if (x.value & (1<<MXCSR::OE)) o << " Overflow";
if (x.value & (1<<MXCSR::UE)) o << " Underflow";
if (x.value & (1<<MXCSR::PE)) o << " Precision";
return o;
}
struct FP {
double value;
FP(double value) : value(value) {}
};
std::ostream & operator<< (std::ostream &o, const FP &x) { o << x.value; return o; }
FP operator+(const FP & lhs, const FP & rhs) {
FP sum(lhs.value);
MXCSR mxcsr, new_mxcsr;
asm ("movsd %0, %%xmm0 \n\t"
"addsd %3, %%xmm0 \n\t"
"movsd %%xmm0, %0 \n\t"
"stmxcsr %1 \n\t"
"stmxcsr %2 \n\t"
"andl $0xffffffc0,%2 \n\t"
"ldmxcsr %2 \n\t"
: "=m" (sum.value), "=m" (mxcsr.value), "=m" (new_mxcsr.value)
: "m" (rhs.value)
: "xmm0", "cc");
std::cout << "lhs=" << lhs.value
<< " rhs=" << rhs.value
<< " sum=" << sum
<< mxcsr
<< std::endl;
return sum;
}
FP operator*(const FP & lhs, const FP & rhs) {
FP product(lhs.value);
MXCSR mxcsr, new_mxcsr;
asm ("movsd %0, %%xmm0 \n\t"
"mulsd %3, %%xmm0 \n\t"
"movsd %%xmm0, %0 \n\t"
"stmxcsr %1 \n\t"
"stmxcsr %2 \n\t"
"andl $0xffffffc0,%2 \n\t"
"ldmxcsr %2 \n\t"
: "=m" (product.value), "=m" (mxcsr.value), "=m" (new_mxcsr.value)
: "m" (rhs.value)
: "xmm0", "cc");
std::cout << "lhs=" << lhs.value
<< " rhs=" << rhs.value
<< " product=" << product
<< mxcsr
<< std::endl;
return product;
}
int main() {
FP x = 2.0;
FP y = 3.9;
std::cout << "answer=" << x + 2.1 * y << std::endl;
std::cout << "answer=" << x + 2 * x << std::endl;
FP z = 1;
for(int i=0; i<310; ++i) {
std::cout << "i=" << i << " z=" << z << std::endl;
z = 10 * z;
}
return 0;
}
L'ultimo ciclo moltiplica un numero per 10
volte sufficiente a mostrare troppo pieno accadere. Noterai anche errori di precisione. Termina con il valore che è infinito una volta che trabocca.
Ecco la coda dell'uscita
lhs=10 rhs=1e+305 product=1e+306 Precision
i=306 z=1e+306
lhs=10 rhs=1e+306 product=1e+307
i=307 z=1e+307
lhs=10 rhs=1e+307 product=1e+308 Precision
i=308 z=1e+308
lhs=10 rhs=1e+308 product=inf Overflow Precision
i=309 z=inf
lhs=10 rhs=inf product=inf
@LihO Se potesse fare questo allora potrei anche rispondere alla mia domanda me stesso. Sarò felice di descrivere ciò che ha funzionato per me una volta che ho raccolto alcuni suggerimenti. In questo momento, non so da dove cominciare a cercare, o se un tale strumento può persino esistere. – clstaudt
Aritmetica in intero o in virgola mobile? Non è probabile che si raggiunga una condizione di overflow in virgola mobile a precisione doppia, poiché ciò si verifica quando si superano circa 308 cifre decimali (quando il numero è troppo grande per rappresentare l'esponente). Molto probabilmente hai a che fare con una perdita di precisione. – amdn
Per rispondere alla tua domanda abbiamo bisogno di sapere che cosa si intende per "risultati numerici diventano non corretta per i grandi formati di input" – amdn