Печать двоичного представления типа float / double дает один и тот же результат для двух разных чисел - PullRequest
1 голос
/ 28 января 2020

Я должен напечатать двоичное представление (из памяти) для чисел с плавающей запятой / двойников. Хотя он работает довольно хорошо для целых чисел, у меня есть странное поведение для некоторых чисел.

Я имею в виду, результаты для 3.14 и 21.37 одинаковы (как двойные). За 8,5 я получаю 0с.

21,37 как двойное значение:

01010001111010111000010100011111010100011110101110001111010111000010100011111

3,14 как двойное значение:

010100011110101110001 * 101011100101101111100101101111100101101110000000000 double:

000000000000000000000000000000000000000000000000000000000000000000000000000000

#include <stdio.h>
int printBit(int c, int i) {
    return (c & (1 << (i - 1))) ? 1 : 0;
}

int main()
{
    double f;
    int *b;
    scanf("%lf", &f);

    b = &f;

    int i;
    printf("Jako double: \n");
    for (i = sizeof(f) * 8; i > 0; i--)
    {
        printf("%i", printBit(*b, i));
    }

    printf("\n");
}

Ответы [ 2 ]

3 голосов
/ 28 января 2020

Ответ "pragmati c" заключается в том, что sizeof(int) и sizeof(double), вероятно, не совпадают на вашей платформе; последний, вероятно, больше, поэтому вы читаете только часть double, которая одинакова в обоих случаях.

Но, если углубиться глубже, утверждение b = &f; является нарушением строгое псевдонимы , поэтому поведение вашей программы не определено в точке разыменования b.

Хороший способ обойти это - прочитать память, приведя &f к const unsigned char* ( это исключение из правила строгого алиасинга), и использование арифметики указателя c для этого указателя до sizeof(f).

2 голосов
/ 28 января 2020

Вы точно не работаете, печатаете правильные данные.

Проблема в том, что sizeof (int) в вашей системе меньше sizeof (double). Существует также строгая проблема с алиасами. Здесь это на самом деле строго теоретически, но, вообще говоря, вообще не используйте наложение указателя.

Итак, как правильно использовать типы:

  1. Использовать союзы
  2. Использовать memcpy
  3. Использовать массив символов.

методы 1 и 3 показаны ниже.

Объединение в ряд (https://godbolt.org/z/qxi8sK)

void prinFloatAsUnsigned(float x)
{
    union
    {
        uint32_t u;
        float f;
    }u32 = {.f = x};

    printf("sizeof(float) = %zu flaot as unsigned = 0x%x\n", sizeof(float), u32.u);
}

int main(void)
{
    prinFloatAsUnsigned(3.14f);
}

метод memcpy

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>

void printasbin(void *buf)
{
    uint64_t val;

    memcpy(&val, buf, sizeof(val));
    for(int x = 63; x >=0; x--)
        printf("%c", '0' + !!(val & (1ULL << x)));
    printf("\n");
}


int main(void)
{
    double x = 3.14;
    printf("%f = \t",x);
    printasbin(&x);
    x = 21.37;
    printf("%f = \t",x);
    printasbin(&x);
    x = 8.5;
    printf("%f = \t",x);
    printasbin(&x);
}

результат:

3.140000  =     0100000000001001000111101011100001010001111010111000010100011111
21.370000 =     0100000000110101010111101011100001010001111010111000010100011111
8.500000  =     0100000000100001000000000000000000000000000000000000000000000000

https://godbolt.org/z/3mLHw9

или напечатано по-вашему (немного изменено - в IT мы считаем биты с нуля)

int printBit(void *buff, int i) 
{
    unsigned char *data = buff;
    return !!(data[i / 8] & (1 << (i & 7)));
}


int main(void)
{
    double f = 3.14;
    printf("Jako double: %f\n", f);
    for (int i = sizeof(f) * 8 - 1; i >= 0; i--)
    {
        printf("%d", printBit(&f, i));
    }
    printf("\n");

    f = 21.37;
    printf("Jako double: %f\n", f);
    for (int i = sizeof(f) * 8 - 1; i >= 0; i--)
    {
        printf("%d", printBit(&f, i));
    }
    printf("\n");

    f = 8.5;
    printf("Jako double: %f\n", f);
    for (int i = sizeof(f) * 8 - 1; i >= 0; i--)
    {
        printf("%d", printBit(&f, i));
    }
    printf("\n");

результат:

Jako double: 3.140000
0100000000001001000111101011100001010001111010111000010100011111
Jako double: 21.370000
0100000000110101010111101011100001010001111010111000010100011111
Jako double: 8.500000
0100000000100001000000000000000000000000000000000000000000000000

https://godbolt.org/z/Gn4vpK

...