Представлять десятичные числа с помощью целых чисел

#c #math

#c #математика

Вопрос:

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

Здесь на ум пришли шестнадцатеричные числа с коэффициентом преобразования.. Я могу технически отправить шестнадцатеричное значение типа (00)1105, а затем просто интерпретировать это как 11.05 (E ^ -2) на стороне программного обеспечения.

Это означало бы, что я технически мог бы обрабатывать десятичные числа на программной стороне, отправляя только целые числа на свое оборудование.

Существуют ли лучшие способы решения этой проблемы? (Двоичные значения не являются опцией, поскольку существует ограничение по длине сообщений, которые могут быть отправлены. (длина <= 10). Я не буду превышать E ^ 5 (nnnnn) для предполагаемого использования.)

Этот текущий подход имеет очевидные проблемы, и все подходы, вероятно, будут иметь это, но мне любопытно, какими другими способами этого можно достичь?

Не беспокойтесь об оборудовании, просто считайте это черным ящиком, где целые числа являются единственными допустимыми входными данными.

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

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

2. Аппаратное обеспечение может быть «черным ящиком», но, предположительно, вы хотите отправить в него значения, чтобы выполнить некоторые операции и получить результаты. Такая информация имеет решающее значение для принятия решения о том, какой подход является «лучшим», но вы не предоставили такой информации.

3. Почему вы думаете, что интерпретация шестнадцатеричных значений как десятичных значений является хорошим способом? Шестнадцатеричное — это всего лишь строковое представление, поэтому это неявное преобразование в десятичное кажется очень произвольным. Почему бы не использовать десятичные значения и где-нибудь установить фиксированную точку (т. Е. 100 равно 1.00)

4. Что именно может быть отправлено в сообщении? (Не более) 10 байт любого значения? Строка из (до) 10 шестнадцатеричных цифр? Что-то еще?…

5. @Joel ваш вопрос неясен, и вы не отвечаете на все замечания…

Ответ №1:

Из того, что я могу понять из вопроса, фиксированные точки могут вам помочь. Фиксированные точки позволяют выполнять десятичную арифметику с целыми числами, предполагая, что количество цифр после десятичной точки известно заранее.

Вот быстрая и грязная реализация фиксированных точек:

 #include <stdio.h>

#define FP_ABS(x) ((x) >= 0 ? (x) : -(x))

// This will determine the number of digits after the decimal point.
// It must be a power of 10.
// In this case, you can represent all the numbers in the range -2147483.648 to 2147483.647
// or if you want to be safe -2000000.000 to 2000000.000
#define FP_DECIMAL_FACTOR 1000

#define FP_LIT(x) ((int)((x) * FP_DECIMAL_FACTOR))
#define FP_ADD(x, y) ((x)   (y))
#define FP_SUB(x, y) ((x) - (y))
#define FP_MUL(x, y) ((x) * (y) / FP_DECIMAL_FACTOR)
#define FP_DIV(x, y) ((x) * FP_DECIMAL_FACTOR / (y))

#define FP_INT_PART(x) ((x) / FP_DECIMAL_FACTOR)
#define FP_DEC_PART(x) (FP_ABS((x) % FP_DECIMAL_FACTOR))


int main()
{
    int a = FP_LIT(25.01);
    int b = FP_LIT(12.2);

    printf("a = %d.dn", FP_INT_PART(a), FP_DEC_PART(a));
    printf("b = %d.dn", FP_INT_PART(b), FP_DEC_PART(b));

    int a_plus_b = FP_ADD(a, b);
    printf("a   b = %d.dn", FP_INT_PART(a_plus_b), FP_DEC_PART(a_plus_b));

    int a_minus_b = FP_SUB(a, b);
    printf("a - b = %d.dn", FP_INT_PART(a_minus_b), FP_DEC_PART(a_minus_b));

    int b_minus_a = FP_SUB(b, a);
    printf("b - a = %d.dn", FP_INT_PART(b_minus_a), FP_DEC_PART(b_minus_a));

    int a_multiply_b = FP_MUL(a, b);
    printf("a * b = %d.dn", FP_INT_PART(a_multiply_b), FP_DEC_PART(a_multiply_b));

    int a_divide_b = FP_DIV(a, b);
    printf("a / b = %d.dn", FP_INT_PART(a_divide_b), FP_DEC_PART(a_divide_b));

    int b_divide_a = FP_DIV(b, a);
    printf("b / a = %d.dn", FP_INT_PART(b_divide_a), FP_DEC_PART(b_divide_a));

    return 0;
}
  

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

1. Хорошее предложение. Остерегайтесь этого, FP_MUL и FP_DIV следует использовать long long арифметику, чтобы уменьшить вероятность арифметического переполнения, приводящего к неправильным результатам.

Ответ №2:

вы совсем не поняли, если вам просто нужно выполнить вычисление с плавающей запятой без FPU, просто используйте библиотеки, такие как библиотека с плавающей запятой для процессоров целых чисел

Ответ №3:

Я помню, что делал нечто подобное на экзамене. Я думаю, это зависит от приложения.

Для числовой обработки вы можете сделать что-то вроде этого:

  • масштабируйте все числа в диапазоне [-1; 1] путем вычитания средних значений и деления на максимальное из модулей. Таким образом, умножения будут принадлежать одному и тому же диапазону
  • Умножьте все числа на степень 2 в зависимости от вашей архитектуры ( 2^32 или 2^64 , например)
  • Округлить значения до целого
  • Выполняйте свои операции
  • Масштабируйте значения путем деления на степень 2
  • Верните числа в исходный диапазон

Я сообщу вам дополнительные подробности, как только смогу.