Как получить знак, мантисса и показатель числа с плавающей запятой
У меня есть программа, которая работает на двух процессорах, одна из которых не поддерживает поддержку с плавающей запятой. Поэтому мне нужно выполнить вычисления с плавающей запятой, используя фиксированную точку в этом процессоре. Для этой цели я буду использовать библиотеку эмуляции с плавающей запятой.
Мне нужно сначала извлечь знаки, мантиссы и показатели чисел с плавающей запятой на процессоре, которые поддерживают плавающую точку. Итак, мой вопрос заключается в том, как я могу получить знак, мантисс и показатель числа чисел с плавающей точкой с одной точностью.
Следуя формату этого рисунка,
Это то, что я сделал до сих пор, но кроме знака, ни мантисса, ни экспоненты верны. Я думаю, что я что-то упустил.
void getSME( int& s, int& m, int& e, float number )
{
unsigned int* ptr = (unsigned int*)&number;
s = *ptr >> 31;
e = *ptr & 0x7f800000;
e >>= 23;
m = *ptr & 0x007fffff;
}
Ответы
Ответ 1
Я думаю, что лучше использовать союзы для выполнения бросков, это яснее.
#include <stdio.h>
typedef union {
float f;
struct {
unsigned int mantisa : 23;
unsigned int exponent : 8;
unsigned int sign : 1;
} parts;
} float_cast;
int main(void) {
float_cast d1 = { .f = 0.15625 };
printf("sign = %x\n", d1.parts.sign);
printf("exponent = %x\n", d1.parts.exponent);
printf("mantisa = %x\n", d1.parts.mantisa);
}
Пример, основанный на http://en.wikipedia.org/wiki/Single_precision
Ответ 2
Узнайте формат чисел с плавающей запятой, используемых в ЦП, которые непосредственно поддерживают плавающие точки и разбивают их на эти части. Наиболее распространенный формат IEEE-754.
В качестве альтернативы вы можете получить эти части с помощью нескольких специальных функций (double frexp(double value, int *exp);
и double ldexp(double x, int exp);
), как показано в этом ответе.
Другой вариант - использовать %a
с printf()
.
Ответ 3
Мой совет - придерживаться правила 0, а не повторять, что уже делают стандартные библиотеки, если этого достаточно. Посмотрите на math.h(cmath в стандартном С++) и функции frexp, frexpf, frexpl, которые разбивают значение с плавающей запятой (double, float или long double) в своей значимой и экспоненциальной части. Чтобы извлечь знак из знака, вы можете использовать signbit, также в math.h/cmath или copysign (только С++ 11). Некоторые альтернативы с более легкой семантикой - modf и ilogb/scalbn, доступные в С++ 11; http://en.cppreference.com/w/cpp/numeric/math/logb сравнивает их, но я не нашел в документации, как все эти функции ведут себя с +/- inf и NaN. Наконец, если вы действительно хотите использовать битмаски (например, вам отчаянно нужно знать точные биты, и ваша программа может иметь разные NaN с разными представлениями, и вы не доверяете вышеуказанным функциям), по крайней мере, сделать все независимым от платформы используя макросы в float.h/cfloat.
Ответ 4
Вы используете &
неправильные биты. Я думаю, вы хотите:
s = *ptr >> 31;
e = *ptr & 0x7f800000;
e >>= 23;
m = *ptr & 0x007fffff;
Помните, что когда вы &
, вы обнуляете биты, которые вы не устанавливаете. Таким образом, в этом случае вы хотите обнулить знаковый бит, когда вы получите экспонента, и вы хотите обнулить знаковый бит и экспонента, когда получите мантиссу.
Обратите внимание, что маски появляются непосредственно из вашего изображения. Итак, маска экспоненты будет выглядеть так:
0 11111111 00000000000000000000000
а маска мантиссы будет выглядеть так:
0 00000000 11111111111111111111111
Ответ 5
В пакете glibc-заголовков Linux содержится заголовок #include <ieee754.h>
с определениями типов с плавающей точкой, например:
union ieee754_double
{
double d;
/* This is the IEEE 754 double-precision format. */
struct
{
#if __BYTE_ORDER == __BIG_ENDIAN
unsigned int negative:1;
unsigned int exponent:11;
/* Together these comprise the mantissa. */
unsigned int mantissa0:20;
unsigned int mantissa1:32;
#endif /* Big endian. */
#if __BYTE_ORDER == __LITTLE_ENDIAN
# if __FLOAT_WORD_ORDER == __BIG_ENDIAN
unsigned int mantissa0:20;
unsigned int exponent:11;
unsigned int negative:1;
unsigned int mantissa1:32;
# else
/* Together these comprise the mantissa. */
unsigned int mantissa1:32;
unsigned int mantissa0:20;
unsigned int exponent:11;
unsigned int negative:1;
# endif
#endif /* Little endian. */
} ieee;
/* This format makes it easier to see if a NaN is a signalling NaN. */
struct
{
#if __BYTE_ORDER == __BIG_ENDIAN
unsigned int negative:1;
unsigned int exponent:11;
unsigned int quiet_nan:1;
/* Together these comprise the mantissa. */
unsigned int mantissa0:19;
unsigned int mantissa1:32;
#else
# if __FLOAT_WORD_ORDER == __BIG_ENDIAN
unsigned int mantissa0:19;
unsigned int quiet_nan:1;
unsigned int exponent:11;
unsigned int negative:1;
unsigned int mantissa1:32;
# else
/* Together these comprise the mantissa. */
unsigned int mantissa1:32;
unsigned int mantissa0:19;
unsigned int quiet_nan:1;
unsigned int exponent:11;
unsigned int negative:1;
# endif
#endif
} ieee_nan;
};
#define IEEE754_DOUBLE_BIAS 0x3ff /* Added to exponent. */
Ответ 6
- Не создавайте функции, которые делают несколько вещей.
- Не маскируйте, а затем сдвигайте; сдвиньте затем маску.
- Не изменяйте значения без необходимости, потому что это медленно, разрушает кэш и подвержен ошибкам.
- Не используйте магические числа.
/* NaNs, infinities, denormals unhandled */
/* assumes sizeof(float) == 4 and uses ieee754 binary32 format */
/* assumes two's-complement machine */
/* C99 */
#include <stdint.h>
#define SIGN(f) (((f) <= -0.0) ? 1 : 0)
#define AS_U32(f) (*(const uint32_t*)&(f))
#define FLOAT_EXPONENT_WIDTH 8
#define FLOAT_MANTISSA_WIDTH 23
#define FLOAT_BIAS ((1<<(FLOAT_EXPONENT_WIDTH-1))-1) /* 2^(e-1)-1 */
#define MASK(width) ((1<<(width))-1) /* 2^w - 1 */
#define FLOAT_IMPLICIT_MANTISSA_BIT (1<<FLOAT_MANTISSA_WIDTH)
/* correct exponent with bias removed */
int float_exponent(float f) {
return (int)((AS_U32(f) >> FLOAT_MANTISSA_WIDTH) & MASK(FLOAT_EXPONENT_WIDTH)) - FLOAT_BIAS;
}
/* of non-zero, normal floats only */
int float_mantissa(float f) {
return (int)(AS_U32(f) & MASK(FLOAT_MANTISSA_BITS)) | FLOAT_IMPLICIT_MANTISSA_BIT;
}
/* Hacker Delight book is your friend. */
Ответ 7
Это прекрасно работает для double
типа данных:
double n = 923842342.3423452;
int sign = *(uintmax_t *)&n >> 63;
int exp = (*(uintmax_t *)&n << 1 >> 53) - BIAS; /* i.e 1023 */
long mantissa = *(uintmax_t *)&n << 12 >> 12;
Если вы попытаетесь распечатать их в двоичном/десятичном виде, вы получите следующее:
In Binary: 0 0b00000011101 0b1011100010000101101110010011001010111101000111111000
In Decimal: 0 29 3246151636341240
Ответ 8
Переместите указатель на переменную с плавающей запятой как нечто вроде unsigned int
. Затем вы можете сдвигать и маскировать биты для получения каждого компонента.
float foo;
unsigned int ival, mantissa, exponent, sign;
foo = -21.4f;
ival = *((unsigned int *)&foo);
mantissa = ( ival & 0x7FFFFF);
ival = ival >> 23;
exponent = ( ival & 0xFF );
ival = ival >> 8;
sign = ( ival & 0x01 );
Очевидно, вы, вероятно, не использовали бы неподписанные ints для показателя экспоненты и знака, но это должно по крайней мере дать вам эту идею.