Как привести вещественные числа к одному порядку при простых арифметических операциях UNIX C

Создали следующую структуру для хранения больших чисел:

typedef enum {
  L_BITS, //младшие биты
  M_BITS, //средние
  H_BITS, //старшие
  S_BITS, //16-23 бит под экспаненту 31 под знак
  ALL_BITS
} e_dec_bit_t;

typedef struct {
  int bits[ALL_BITS];
} my_decimal;

#define INT_BITS sizeof(int) * 8
#define DEC_BITS ALL_BITS * sizeof(int) * 8

#define NEW_DECIMAL(l_b, m_b, h_b, scale, sign) \
  { l_b, m_b, h_b, ((scale << 16) | (sign << 31)) }

S_BITS

  • Биты от 0 до 15, младшее слово, не используются и должны быть равны нулю;
  • Биты с 16 по 23 должны содержать показатель степени от 0 до 28, который указывает степень 10 для разделения целого числа;
  • Биты с 24 по 30 не используются и должны быть равны нулю;
  • Бит 31 содержит знак;

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

Двоичное представление Decimal состоит из 1-разрядного знака, 96-разрядного целого числа и коэффициента масштабирования, используемого для деления 96-разрядного целого числа и указания того, какая его часть является десятичной дробью. Коэффициент масштабирования неявно равен числу 10, возведенному в степень в диапазоне от 0 до 28. Следовательно, двоичное представление Decimal имеет вид ((от -2^96 до 2^96) / 10^(от 0 до 28)), где -(2^96-1) равно минимальному значению, а 2^96-1 равно максимальному значению.

К примеру имеем следующие вводные данные:

M1 = 123456 exp1 = 2 -> 12.3456

M2 = 123456 exp2 = 3 -> 123.456

Здесь я возможно ошибаюсь и следует думать иначе:

M1 = 123456 exp1 = 2 -> 1234.56

M2 = 123456 exp2 = 3 -> 123.456

В данном случае как мне легче считать, так это в 10 форме (так мне легче представлять), я sub exp2 - exp1 = 1

И пока в цикле я ниже этого значения а именно 1 я представляю что у первого числа мнимый 0 в начале и выполняю действия как:

012345(6) +
123456    =
135801

Числа которые я складываю выглядят следующим образом если не учитывать порядок:

 1 | 1110 0010 0100 0000 +
 1 | 1110 0010 0100 0000 =
10 | 0001 0010 0111 1001

В каждом из первых трёх чанков выполняем все опперации побитово:

bool get_bit(const my_decimal *src, const size_t index) {
  int mask = (1LU << (index % 0x20));
  return src->bits[index / 0x20] & mask;
}

*Вопрос в следующем: как выполнять все операции побитово, учитывая порядок находящийся в между 16-23 bits?

Все действия должны быть подобны Decimal in C*

Я вероятно думаю в 10 формате, и не замечаю явных вещей.


Ответы (1 шт):

Автор решения: Astrodynamic

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


int normalize_numbers(my_decimal *a, my_decimal *b) {
  int flag = my_NORMAL_VALUE;
  int scale = get_scale(a) - get_scale(b);
  my_decimal temp_d = create_integer_decimal(10, 0, 0);
  if (scale < 0) {
    flag = multiple_in_loop(a, temp_d, abs(scale));
  } else if (scale > 0) {
    flag = multiple_in_loop(b, temp_d, abs(scale));
  }
  return flag;
}

int multiple_in_loop(my_decimal *src, my_decimal multiplicand, size_t times) {
  int flag = my_NORMAL_VALUE;
  for (size_t i = 0; i < times && flag == my_NORMAL_VALUE; ++i) {
    s21_decimal t_src = *src;
    memset(src, 0, sizeof(s21_decimal));
    flag = my_mul(t_src, multiplicand, src);
  }
  return flag;
}

А таким образом жертвую

int reduction_order(my_decimal *value_1, my_decimal *value_2) {
  int flag = my_NORMAL_VALUE;
  int s_v1 = get_scale(value_1);
  int s_v2 = get_scale(value_1);
  s21_decimal denominator = create_integer_decimal(10, 0, 0);
  s21_decimal t_value_1 = create_integer_decimal(0, 0, 0);
  s21_decimal t_value_2 = create_integer_decimal(0, 0, 0);
  if (s_v1 && s_v1 > s_v2) {
    flag = my_div(*value_1, denominator, &t_value_1);
    *value_1 = t_value_1;
  } else if (s_v2 && s_v1 < s_v2) {
    flag = my_div(*value_2, denominator, &t_value_2);
    *value_2 = t_value_2;
  } else if (s_v1 == s_v2) {
    flag = my_div(*value_1, denominator, &t_value_1);
    flag = my_div(*value_2, denominator, &t_value_2);
    *value_1 = t_value_1;
    *value_2 = t_value_2;
  }
  return flag;
}
→ Ссылка