Как разделить два 64-битных числа в ядре Linux? - PullRequest
6 голосов
/ 30 августа 2008

Некоторый код, который округляет деление для демонстрации (C-синтаксис):

#define SINT64 long long int
#define SINT32 long int

SINT64 divRound(SINT64 dividend, SINT64 divisor)
{
  SINT32 quotient1 = dividend / divisor;

  SINT32 modResult = dividend % divisor;
  SINT32 multResult = modResult * 2;
  SINT32 quotient2 = multResult / divisor;

  SINT64 result = quotient1 + quotient2;

  return ( result );
}

Теперь, если бы это было пространство пользователя, мы, вероятно, даже не заметили бы, что наш компилятор генерирует код для этих операторов (например, divdi3 () для деления). Скорее всего, мы связываемся с 'libgcc', даже не подозревая об этом. Проблема в том, что Kernel-space отличается (например, нет libgcc). Что делать?

Просканируйте Google на некоторое время, обратите внимание, что почти все обращаются к неподписанному варианту:

#define UINT64 long long int
#define UINT32 long int

UINT64 divRound(UINT64 dividend, UINT64 divisor)
{
  UINT32 quotient1 = dividend / divisor;

  UINT32 modResult = dividend % divisor;
  UINT32 multResult = modResult * 2;
  UINT32 quotient2 = multResult / divisor;

  UINT64 result = quotient1 + quotient2;

  return ( result );
}

Я знаю, как это исправить: переопределить udivdi3 () и umoddi3 () с _do_div () _ из asm / div64.h . Сделано правильно? Неправильно. Signed - это не то же самое, что unsigned, sdivdi3 () _ не просто вызывает udivdi3 () , они являются отдельными функциями по определенной причине.

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

Спасибо, Чад

Ответы [ 4 ]

4 голосов
/ 13 октября 2008

Эта функциональность представлена ​​в / linux / lib / div64.c уже в ядре v2.6.22.

4 голосов
/ 30 августа 2008

Вот мое действительно наивное решение. Ваш пробег может отличаться.

Оставьте знак бита, который sign(dividend) ^ sign(divisor). (Или *, или /, если вы храните свой знак как 1 и -1, в отличие от false и true. В основном, отрицательный, если один из них отрицательный, положительный, если ни один, или оба отрицательные.) 1006 *

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

P.S. Именно так __divdi3 реализован в libgcc2.c (из GCC 4.2.3, версии, установленной в моей системе Ubuntu). Я только что проверил. : -)

0 голосов
/ 02 сентября 2008

Я не думаю (по крайней мере, не могу найти способ заработать) Ответ Криса работает в этом случае, потому что do_div () на самом деле меняет дивиденд на месте. Получение абсолютного значения подразумевает временную переменную, значение которой изменит то, что мне нужно, но не может быть передано из моего __ divdi3 () переопределения.

Я не вижу способа обойти сигнатуру параметра по значению __ divdi3 () на этом этапе, кроме как для имитации техники, используемой do_div () .

Может показаться, что я наклоняюсь назад и должен просто придумать алгоритм для выполнения 64-битного / 32-битного деления, которое мне действительно нужно. Однако здесь добавлено еще одно осложнение: у меня есть куча числового кода, использующего оператор «/», и мне нужно будет пройти через этот код и заменить каждый «/» вызовами моей функции.

Я достаточно отчаялся, чтобы сделать именно это.

Спасибо за продолжение, Чад

0 голосов
/ 30 августа 2008

ldiv?

Редактировать: перечитать заголовок, так что вы можете игнорировать это. Или нет, в зависимости от того, имеется ли соответствующая небиблиотечная версия.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...