Convertire galleggiante bigint (aka modo comune di ottenere esponente binario mantissa)
-
22-09-2019 - |
Domanda
In C ++, ho una classe bigint che può contenere un numero intero di dimensione arbitraria.
Mi piacerebbe convertire galleggiante di grandi dimensioni o di numeri doppi a BIGINT. Ho un metodo di lavoro, ma è un po 'di hack. Ho usato IEEE 754 specifica il numero per ottenere il segnale binario, mantissa ed esponente del numero di ingresso.
Ecco il codice (segno viene ignorato qui, questo non è importante):
float input = 77e12;
bigint result;
// extract sign, exponent and mantissa,
// according to IEEE 754 single precision number format
unsigned int *raw = reinterpret_cast<unsigned int *>(&input);
unsigned int sign = *raw >> 31;
unsigned int exponent = (*raw >> 23) & 0xFF;
unsigned int mantissa = *raw & 0x7FFFFF;
// the 24th bit is always 1.
result = mantissa + 0x800000;
// use the binary exponent to shift the result left or right
int shift = (23 - exponent + 127);
if (shift > 0) result >>= shift; else result <<= -shift;
cout << input << " " << result << endl;
Funziona, ma è piuttosto brutto, e io non so come portatile è. C'è un modo migliore per fare questo? C'è un modo portatile meno brutta di estrarre la mantissa e l'esponente binario da un galleggiante o raddoppiare?
Grazie per le risposte. Ai posteri, ecco una soluzione che utilizza frexp. E 'meno efficiente a causa del ciclo, ma funziona per float e double simili, non usa reinterpret_cast o dipendere alcuna conoscenza di floating point rappresentazioni numeriche.
float input = 77e12;
bigint result;
int exponent;
double fraction = frexp (input, &exponent);
result = 0;
exponent--;
for (; exponent > 0; --exponent)
{
fraction *= 2;
if (fraction >= 1)
{
result += 1;
fraction -= 1;
}
result <<= 1;
}
Soluzione
Non puoi normalmente estrarre i valori utilizzando frexp (), frexpf (), frexpl () ?
Altri suggerimenti
Mi piace la tua soluzione! E mi ha fatto sulla strada giusta.
Mi raccomando una cosa però - perché non avere un mucchio di bit in una sola volta ed eliminare quasi sempre qualsiasi loop? Ho implementato una funzione float-to-bigint in questo modo:
template<typename F>
explicit inline bigint(F f, typename std::enable_if<(std::is_floating_point<F>::value)>::type* enable = nullptr) {
int exp;
F fraction = frexp(fabs(f),&exp);
F chunk = floor(fraction *= float_pow_2<F,ulong_bit_count>::value);
*this = ulong(chunk); // will never overflow; frexp() is guaranteed < 1
exp -= ulong_bit_count;
while (sizeof(F) > sizeof(ulong) && (fraction -= chunk)) // this is very unlikely
{
chunk = floor(fraction *= float_pow_2<F,ulong_bit_count>::value);
*this <<= ulong_bit_count;
(*this).data[0] = ulong(chunk);
exp -= ulong_bit_count;
}
*this <<= exp;
sign = f < 0;
}
(A proposito, non so di un modo semplice per mettere in virgola mobile potenze di due costanti, così ho definito float_pow_2 come segue):
template<typename F, unsigned Exp, bool Overflow = (Exp >= sizeof(unsigned))>
struct float_pow_2 {
static constexpr F value = 1u << Exp;
};
template<typename F, unsigned Exp>
struct float_pow_2<F,Exp,true> {
static constexpr F half = float_pow_2<F,Exp/2>::value;
static constexpr F value = half * half * (Exp & 1 ? 2 : 1);
};
Se il galleggiante contiene sempre un valore integrale, basta cast a int. Float_to_int = input (più firmato)
A proposito, 77e12 trabocca un galleggiante. Un doppio si terrà, ma poi avrete bisogno di questo cast:. (Lunga unsigned long) ingresso