Convertir Float a BigInt (también conocido como Portable Way de obtener exponentes binarios y Mantissa)

StackOverflow https://stackoverflow.com/questions/2133903

Pregunta

En C ++, tengo una clase Bigint que puede contener un entero de tamaño arbitrario.

Me gustaría convertir grandes flotantes o números dobles en Bigint. Tengo un método de trabajo, pero es un poco hack. Utilicé la especificación de número IEEE 754 para obtener el signo binario, la mantissa y el exponente del número de entrada.

Aquí está el código (se ignora aquí, eso no es 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;

Funciona, pero es bastante feo, y no sé qué tan portátil es. ¿Hay una mejor manera de hacer esto? ¿Hay una forma menos fea y portátil de extraer la mantissa binaria y el exponente de un flotador o doble?


Gracias por las respuestas. Para la posteridad, aquí hay una solución que usa FREXP. Es menos eficiente debido al bucle, pero funciona para Float y Doble por igual, no usa reinterpret_cast o depende de ningún conocimiento de las representaciones de números de puntos flotantes.

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;
}   
¿Fue útil?

Solución

¿No puedes extraer normalmente los valores usando frexp (), frexpf (), frexpl ()?

Otros consejos

¡Me gusta tu solución! Me consiguió en el camino correcto.

Sin embargo, recomendaría una cosa: ¿por qué no obtener un montón de bits a la vez y casi siempre eliminar cualquier bucle? Implementé una función flotante a bigint como esta:

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;
}

(Por cierto, no sé de una manera fácil de poner constantes de potencia de dos puntos flotantes, así que definí Float_Pow_2 de la siguiente manera):

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);
};

Si el flotador siempre contiene un valor integral, simplemente colóquelo a int: float_to_int = (unsigned long) entrada.

Por cierto, 77e12 desborda un flotador. Un doble lo sostendrá, pero luego necesitará este elenco: (sin firmar largo) entrada).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top