Come trovare la lunghezza della mantissa su una macchina particolare?
-
20-08-2019 - |
Domanda
Voglio trovare il numero di cifre della mantissa e l'arrotondamento dell'unità su un determinato computer. Ho una comprensione di cosa sono, non ho idea di come trovarli, anche se capisco che possono variare da computer a computer.
Ho bisogno di questo numero per eseguire alcuni aspetti dell'analisi numerica, come l'analisi degli errori.
Quello che sto pensando attualmente è che potrei scrivere un piccolo programma c ++ per aumentare lentamente un numero fino a quando si verifica un overflow, ma non sono sicuro del tipo di numero da usare.
Sono sulla buona strada? Come si fa esattamente a calcolare questo?
Soluzione
Penserei che qualunque lingua tu stia usando specifica il modo in cui sono stati memorizzati i float. So che Java lo fa utilizzando uno standard IEEE specifico (754, credo).
Se non è specificato, penso che potresti semplicemente fare il tuo controllo aggiungendo 0,5 a 1 per vedere se il numero effettivo cambia. In tal caso, aggiungi 0,25 a 1, 0,125 a 1 e così via fino a quando il numero non cambia, qualcosa del tipo:
float a = 1;
float b = 0.5;
int bits = 0;
while (a + b != a) {
bits = bits + 1;
b = b / 2;
}
Se avessi solo 3 bit di mantissa, 1 + 1/16 sarebbe uguale a 1.
Allora hai esaurito i tuoi pezzi di mantissa.
Potrebbe essere necessario che il numero di base sia 2 anziché 1, poiché IEEE754 utilizza un "1+" implicito all'inizio.
EDIT:
Sembra che il metodo sopra descritto possa avere alcuni problemi in quanto fornisce 63 bit per un sistema che ha chiaramente float a 4 byte.
Sia che si tratti di risultati intermedi (ne dubito poiché lo stesso codice con cast espliciti [while (((float)(a + b) != (float)(a))
] ha problemi simili) o (più probabilmente, credo) la possibilità che il valore unitario a
possa essere rappresentato con bit più vicini al frazionario b
regolando l'esponente, non lo so ancora.
Per ora, è meglio fare affidamento sulle informazioni sulla lingua che ho menzionato sopra, come l'uso di IEEE754 (se tali informazioni sono disponibili).
Lascerò il codice problematico come una trappola per i giocatori diffidenti. Forse qualcuno con più conoscenza in virgola mobile quindi posso lasciare una nota che spiega perché agisce in modo strano (nessuna congettura, per favore :-).
MODIFICA 2:
Questo pezzo di codice lo risolve assicurando che gli intermedi siano memorizzati in float. Scopre che Jonathan Leffler aveva ragione: erano risultati intermedi.
#include <stdio.h>
#include <float.h>
int main(void) {
float a = 1;
float b = 0.5;
float c = a + b;
int bits = 1;
while (c != a) {
bits = bits + 1;
b = b / 2;
c = a + b;
}
printf("%d\n",FLT_MANT_DIG);
printf("%d\n",bits);
return 0;
}
Questo codice genera (24,24) per mostrare che il valore calcolato corrisponde a quello nel file di intestazione.
Sebbene scritto in C, dovrebbe essere applicabile a qualsiasi lingua (in particolare quella in cui le informazioni non sono disponibili in un'intestazione o in virtù di ciò che è specificato nella documentazione linguistica). Ho provato solo in C perché Eclipse impiega così tanto tempo ad avviarsi sul mio Ubuntu :-).
Altri suggerimenti
Per C, e per estensione C ++, le informazioni sono nelle intestazioni <float.h>
o <cfloat>
.
Per C99, le informazioni sono contenute nella sezione 5.2.4.2.2 della norma:
-
FLT_RADIX
-
FLT_MANT_DIG
-
FLT_DIG
-
FLT_EPSILON
-
FLT_MIN_EXP
-
FLT_MIN
-
FLT_MIN_10_EXP
-
FLT_MAX_EXP
-
FLT_MAX
-
FLT_MAX_10_EXP
E allo stesso modo per le variazioni DBL e LDBL sulla maggior parte di queste (no DBL_RADIX
o LDBL_RADIX
). Lo standard suggerisce valori appropriati per IEEE 754 (la versione precedente dello standard IEEE 754 che era attuale nel 1999; c'era una nuova versione pubblicata, credo, nel 2008).
Potresti voler controllare <limits>
nella tua libreria C ++:
#include <iostream>
#include <limits>
#include <typeinfo>
template <typename T>
void printDetailsFor() {
std::cout
<< "Printing details for " << typeid(T).name() << ":\n"
<< "\tradix: " << std::numeric_limits<T>::radix << "\n"
<< "\tradix digits: " << std::numeric_limits<T>::digits << "\n"
<< "\tepsilon: " << std::numeric_limits<T>::epsilon() << "\n"
<< std::endl;
}
int main() {
printDetailsFor<int>();
printDetailsFor<float>();
printDetailsFor<double>();
printDetailsFor<long double>();
return 0;
}
Penso che tu voglia std::numeric_limits<T>::digits
che dovrebbe essere uno in più rispetto al numero di bit di mantissa. La mia macchina stampa:
Printing details for i:
radix: 2
radix digits: 31
epsilon: 0
Printing details for f:
radix: 2
radix digits: 24
epsilon: 1.19209e-07
Printing details for d:
radix: 2
radix digits: 53
epsilon: 2.22045e-16
Printing details for e:
radix: 2
radix digits: 64
epsilon: 1.0842e-19