@Artyom_Kopan

Как сделать такой финт ушами с double?

По содержимому памяти вывести значение типа double в экспоненциальной форме: sm*2^{Sp}, где s — знак мантиссы, m — мантисса, S — знак порядка, p — порядок числа.
Примеры:
Enter a number: -2.5
Result: -1.25*2^1
Enter a number: 12312.323
Result: +1.5029691162109375384*2^13

Я знаю, как сделать это алгоритмически, но нужно именно извлечь это представление из памяти. Слышал что-то про применение в данном случае union и приведения к int64_t, но не совсем понял, как это применить
  • Вопрос задан
  • 222 просмотра
Решения вопроса 1
wataru
@wataru Куратор тега C++
Разработчик на С++, экс-олимпиадник.
Используйте memcopy. Копируете из адреса дабловой переменной в адрес интовой.

Трюки с union и реинтерпритацей указателей довольно опасны.
Ответ написан
Пригласить эксперта
Ответы на вопрос 3
@Mercury13
Программист на «си с крестами» и не только
Почему нельзя? Пишу на Си с крестами, но там ничего специфичного нет, кроме парочки перестраховок.
В Си++20 появился ещё и bit_cast.

#include <iostream>

union DoubleInt {
    double asDouble;
    uint64_t asInt;
};

static_assert(sizeof(double) == sizeof(uint64_t), "Strange machine with double != int64");

constexpr int BITS_MANTISSA = 52;
constexpr int BITS_EXPONENT = 11;
constexpr int BITS_SIGN = 1;

static_assert(BITS_MANTISSA + BITS_EXPONENT + BITS_SIGN == 64, "Programmer's funkup");

constexpr uint64_t MANTISSA_UNIT = uint64_t(1) << BITS_MANTISSA;
constexpr uint64_t MANTISSA_MASK = MANTISSA_UNIT - 1;

constexpr int EXPONENT_SHIFT = BITS_MANTISSA;
constexpr uint64_t EXPONENT_MAX = (uint64_t(1) << BITS_EXPONENT) - 1;
constexpr uint64_t EXPONENT_ORIGIN = EXPONENT_MAX >> 1;
constexpr uint64_t EXPONENT_MASK = EXPONENT_MAX << EXPONENT_SHIFT;
constexpr uint64_t EXPONENT_SHIFTED_ORIGIN = EXPONENT_ORIGIN << EXPONENT_SHIFT;

constexpr int SIGN_SHIFT = BITS_MANTISSA + BITS_EXPONENT;
constexpr uint64_t SIGN_MASK = uint64_t(1) << SIGN_SHIFT;

int main()
{
    DoubleInt x { -3.45 };

    // Простите уж, без денормализованных чисел

    // Оставим знак и мантиссу
    DoubleInt xMantissa = x;
    xMantissa.asInt &= (MANTISSA_MASK | SIGN_MASK);
    // И добавим туда стандартный нулевой порядок
    xMantissa.asInt |= EXPONENT_SHIFTED_ORIGIN;

    // Извлечём порядок
    int exponent = ((x.asInt & EXPONENT_MASK) >> EXPONENT_SHIFT) - EXPONENT_ORIGIN;

    std::cout << xMantissa.asDouble << "*2^" << exponent << std::endl;

    return 0;
}
Ответ написан
@User700
Как-то так
union double_bitfld_t {
 double dbl;
 struct {
  unsigned int mnt : 52;
  unsigned int exp : 11;
  unsigned int sgn : 1;
 } bits;
};

double_bitfld_t x;
cin >> x.dbl;
cout << (x.bits.sgn ? '+' : '-') << (1+x.bits.mnt/double(1<<52)) << "*2^" << (x.bits.exp-1023) << endl;

Конечно нужно проверить, отладить
Вместо "1+x.bits.mnt/double(1сдвиг52)" лучше как выше завести другую переменную и изменить порядок на 0, получив мантиссу как double
Ответ написан
@AntonSazonov
Почитайте про битовые поля (bit fields).
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы