Преобразование плавающего числа в двоичное представление программы на C

#c #floating-point #binary #converters

#c #с плавающей запятой #двоичный #конвертеры

Вопрос:

 #include "ieee754.h"
#include <stdio.h>
#include <math.h>

//This program convert a floating number to its binary representation (IEEE754) in computer memory
int main() {
    long double f, binaryTotal, binaryFrac = 0.0, frac, fracFractor = 0.1;
    long int integer, binaryInt = 0;
    long int p = 0, rem, temp;

    printf("nEnter floating number: ");
    scanf("%Lf", amp;f);

    //separate the integer part from the input floating number
    integer = (int)f;

    //separate the fractional part from the input floating number
    frac = f - integer;

    //loop to convert integer part to binary
    while (integer != 0) {
        rem = integer % 2;
        binaryInt = binaryInt   rem *pow(10, p);
        integer = integer / 2;
        p  ;
    }

    //loop to convert fractional part to binary
    while (frac != 0) {
        frac = frac * 2;
        temp = frac;
        binaryFrac = binaryFrac   fracFractor * temp;
        if (temp == 1)
            frac = frac - temp;

        fracFractor = fracFractor / 10;
    }

    binaryTotal = binaryInt   binaryFrac;
    printf("binary equivalent = %Lfn", binaryTotal);
}
 

Я пытаюсь преобразовать плавающее число в двоичное представление (64-разрядное). Этот код работает, но не идеален. Например, когда я конвертирую .575 , это дает мне 0.100100 , но когда я выполняю преобразование с помощью этого веб-сайта http://www.exploringbinary.com/floating-point-converter /, правильный вывод должен быть 0.1001001100110011001100110011001100110011001100110011

У меня возникли проблемы с пониманием того, что заставляет мой код усекать число. Кто-нибудь может мне помочь, как это исправить? Я ценю вашу помощь.

Комментарии:

1. Скомпилируйте со всеми включенными предупреждениями.

2. @MichaelWalz предупреждения нет: ideone.com/aIShip

3. @mike ты должен написать, сколько цифр ты хочешь после . того, как: "%.20Lf" напечатает 0.10010011001100110011 .

4. @mike mch прав, мой комментарий здесь не очень уместен.

5. Это потому, что число не может быть точно представлено в двоичном формате (или double ). Таким образом, он может напечатать только ближайший номер, который может быть сохранен в a long double . Вы должны сохранить свой номер в виде строки.

Ответ №1:

Много проблем:

  1. Использование (int) для извлечения целочисленной части a long double сильно ограничивает диапазон. Использовать modfl(long double value, long double *iptr);
     long double f;
    long int integer;
    //separate the integer part from the input floating number
    // Weak code
    integer = (int)f;
    
    long double ipart;
    long double fpart = modfl(f, amp;ipart);
     
  2. long p; pow(10,p); —> потеря точности в pow() возвращаемом значении, когда p оно превышает некоторое значение (пример 25). Также странно использовать pow() с функцией , которая используется long double . Я бы ожидал powl() .
  3. Различные другие неточные проблемы с FP: fracFractor/10 , ограниченная точность long .

Код странный, поскольку он пытается преобразовать число FP (вероятно, в каком-то двоичном формате) в двоичное представление. Это не должно требоваться 10 нигде в коде.

Предложите что-нибудь простое, например

 #include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<float.h>

static void print_ipart(long double x) {
  int digit = (int) (modfl(x/2, amp;x)*2.0)   '0';
  if (x) {
    print_ipart(x);
  }
  putchar(digit);
}

void print_bin(long double x) {
  // Some TBD code
  // Handle NAN with isnan()
  // Handle infinity with isfinite()

  putchar(signbit(x) ? '-' : ' ');

  long double ipart;
  long double fpart = modfl(fabsl(x), amp;ipart);

  print_ipart(ipart);
  putchar('.');
  while (fpart) {
    long double ipart;
    fpart = modfl(fpart * 2, amp;ipart);
    putchar((int)ipart   '0');
  }
  putchar('n');
}

int main() {
  print_bin(-4.25);
  print_bin(.575);
  print_bin(DBL_MAX);
  print_bin(DBL_MIN);
  print_bin(DBL_TRUE_MIN);
}
 

Выходной сигнал

 -100.01
 0.1001001100110011001100110011001100110011001100110011001100110011
 1111111111111111111111111111111111111111111111111111100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.
 0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001
 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001
 

Комментарии:

1. Спасибо за вашу помощь! Это очень сложный способ записи. Однако это немного выходит за рамки моего нынешнего знакомства с C.. не могли бы вы объяснить, как (modfl(x / 2, amp; x) * 2.0) ‘0’ работает? Я обнаружил, что modfl отделяет intpart от fracpart, и я вижу использование деления на 2. Я предполагаю, что мои вопросы заключаются в том, что делает amp; x и почему вы умножаете его на 2? и вы ставите ‘0’ в конце, когда x%2 равно 0? Я думал, что этот проект конвертера будет хорошей практикой для написания кода на C, но сейчас это немного выходит за рамки моего понимания.. Я ценю вашу помощь

2. @mike amp;x — это адрес x . modfl(lf, addr) разбивается lf на часть дроби, которая возвращается, и часть целого числа, которая сохраняется по указанному адресу. Результатом функции является дробь, в этом коде она будет равна 0,0 или 0,5. Умножение на 2 дает 0,0 или 1,0. Добавление этого значения '0' делает '0' или '1' для печати. Надеюсь, этот подход проще, чем ваш оригинал. Я не думаю, что это выходит за рамки вашего понимания.

3. А, ладно, теперь все ясно. Большое вам спасибо за вашу доброту.

Ответ №2:

Вот почему это вряд ли сработает:

 fracFractor = 0.1
...
fracFractor = fracFractor/10
 

0.1 невозможно точно представить в любом двоичном формате с плавающей запятой. Вы не можете представить 0.1 как кратное отрицательной степени двойки. Разделив его на 10, он будет собирать ошибки округления на каждом шаге. Возможно, вы фактически завершили этот цикл, потому что в конечном итоге вы сравниваете повторяющуюся дробь с другой повторяющейся дробью.

И это сильно ограничит то, чего вы можете достичь:

 binaryTotal = binaryInt   binaryFrac;
 

Выполнение этого в формате с плавающей запятой будет иметь серьезные ограничения — не в последнюю очередь из-за того, что представление 0.1 невозможно представить, как описано выше. Вот почему вы получаете ответы, которые отображаются в виде комбинации двоичных и десятичных цифр.

Чтобы решить эту проблему, вам, вероятно, следует взглянуть на отдельные биты числа. Чтобы сохранить общую идею вашего решения, проще всего будет продолжать вычитать отрицательную степень двойки (0,5, 0,25 и т.д.) Из вашей дроби, проверить, по-прежнему ли она положительная, и построить строку на основе этого. Затем используйте аналогичную логику для целочисленной части.