Союз-двоичный к двойному
Здравствуйте, ребята, я пытаюсь реализовать новую функцию frexp вручную. Для этого я использовал тип данных Union. Я могу найти экспоненту правильно, но моя проблема связана с богомолом. Я не могу найти правильное значение для mantisa part. Это дает мне действительно большое число, но когда . Я попытался сдвинуть двоичный код, но это также не помогло мне. У вас есть какие-нибудь идеи, как я могу найти богомола из этого двоичного файла? Спасибо. (Это для двойной плавающей единицы, и я предположил, что double-64 бит)
P. s. Есть еще одна вещь, которую я не понял. Чтобы найти правильное значение экспоненты, я предполагаю уменьшить это значение 1023 в теории (свойство смещения), но в этом примере мне нужно было уменьшить 1022, чтобы найти правильное значение. Что-то не так?
typedef union {
double f;
struct {
unsigned long mantisa : 52;
unsigned long exponent : 11;
unsigned long sign : 1;
} parts;
} double_cast;
double myfrexp(double number, int *exp)
{
double_cast d1;
d1.f = number;
unsigned long dd;
printf("n %x n", d1.parts.exponent);
*exp = d1.parts.exponent - 1022;
printf("n%dnn", *exp);
printf("n %lf n", (double)d1.parts.mantisa);
return d1.parts.mantisa;
}
Спасибо
2 ответа:
Чтобы уменьшить проблемы с конечностью и невозможностью иметь 52-битные поля
int
, Используйте объединениеdouble
иuint64_t
.Предполагается, что
double
конечность и целочисленная конечность-это одно и то же. Большинство систем делают это, но не все. От этого зависит следующее.+1 в вашем посте и
expo + 1
ниже, потому что 1.0 frexp(): 0.5double myfrexp(double number, int *exp) { static const uint64_t mantissa_mask = 0x000FFFFFFFFFFFFFllu; static const uint64_t mantissa_impliedBit = 0x0010000000000000llu; static const uint64_t expo_mask = 0x7FF0000000000000llu; static const uint64_t expo_norm = 0x3FE0000000000000llu; static const uint64_t sign_mask = 0x8000000000000000llu; static const int expo_NaN = 0x07FF; static const int expo_Bias = 1023; union { double d; uint64_t u; } x = { number }; uint64_t mantissa = x.u & mantissa_mask; int expo = (x.u & expo_mask) >> 52; if (expo == expo_NaN) { // Behavior for Infinity and NaN is unspecified. *exp = 0; return number; } if (expo > 0) { mantissa |= mantissa_impliedBit; // This line is illustrative, not needed. expo -= expo_Bias; } else if (mantissa == 0) { *exp = 0; return number; // Do not return 0.0 as that does not preserve -0.0 } else { // de-normal or sub-normal numbers expo = 1 - expo_Bias; // Bias different when biased exponent is 0 while (mantissa < mantissa_impliedBit) { mantissa <<= 1; expo--; } } *exp = expo + 1; mantissa &= ~mantissa_impliedBit; x.u = (x.u & sign_mask) | expo_norm | mantissa; return x.d; } #include <limits.h> #include <math.h> #include <memory.h> #include <stdio.h> #include <float.h> void frexp_test(double d) { int i1,i2; double d1,d2; d1 = frexp(d, &i1); d2 = myfrexp(d, &i2); if (memcmp(&d1,&d2,sizeof(d1)) != 0 || (i1 != i2)) { printf("%a (%a %x) (%a %x)\n", d, d1, i1, d2, i2); } } int main() { frexp_test(1.0); frexp_test(0.0); frexp_test(-0.0); frexp_test(DBL_MAX); frexp_test(-DBL_MAX); frexp_test(DBL_EPSILON); frexp_test(DBL_MIN); frexp_test(DBL_MIN/1024); frexp_test(DBL_MIN/1024/1024); frexp_test(INFINITY); //frexp_test(DBL_TRUE_MIN); return 0; }
Я не помню точных деталей чисел с плавающей запятой IEEE, но я, кажется, помню, что последовательные члены структуры размещаются по возрастающим адресам. Это означает, что ваш макет, похоже, предполагает, что мантисса хранится по самому низкому адресу. Я думаю, что такие опасения исчезнут, если вы приведете адрес двойной переменной к 64-битному целочисленному указателю и будете использовать исключительно shifts & masks.
Вот так:
uint64_t bits = *((uint64_t*)&number); uint64_t mantissa = bits & ((1<<52)-1); unsigned exponent = (bits>>52) & 0x7FF; unsigned sign = bits>>63;
Чтобы распечатать результаты, вы можете сделать (игнорируя 0.0 f, NaN и т. д.):
printf("%c1.%"PRIu64"e%u", sign?'-':'+', mantissa, exponent);
Для использования PRIu64 смотрите здесь