如何在Linux内核中除以两个64位数字?

时间:2020-03-05 18:45:16  来源:igfitidea点击:

一些代码将除法进行四舍五入来演示(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"链接。问题在于内核空间不同(例如,没有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 );
}

我知道如何解决此问题:使用asm / div64.h中的_do_div()_覆盖udivdi3()和umoddi3()。做对了吗?错误的。有符号与无符号是不同的,sdivdi3()_并不简单地调用udivdi3(),由于某种原因,它们是独立的函数。

你解决了这个问题吗?我们是否知道可以帮助我做到这一点的图书馆?我真的很困惑,所以无论我们在这里看到什么,我现在都不知道都将非常有帮助。

谢谢,
乍得

解决方案

回答

ldiv`

编辑:重读标题,所以我们可能要忽略它。是否,取决于它是否具有适当的非库版本。

回答

这是我真正的天真解决方案。你的旅费可能会改变。

保留一个符号位,即sign(dividend)^ sign(divisor)。 (或者,如果我们将符号存储为1和-1,则为*或者/,与false和true相对。基本上,如果其中一个为负,则为负;如果两个都不为负,则为正。)

然后,对两者的绝对值调用无符号除法函数。然后将标志重新粘贴到结果上。

P.S.这实际上是在libgcc2.c(从我的Ubuntu系统上安装的版本GCC 4.2.3)中实现__divdi3的方式。我刚刚检查。 :-)

回答

在这种情况下,我认为(至少无法找到一种方法)克里斯的答案有效,因为do_div()实际上就地改变了股利。获取绝对值意味着一个临时变量,该变量的值将更改我需要的方式,但不能从__divdi3()重写中传递出去。

除了模仿do_div()所使用的技术外,我目前看不到__divdi3()的按值签名的方法。

似乎我在向后弯腰,应该提出一种算法来执行我实际需要的64位/ 32位除法。不过,这里增加的复杂性是我使用" /"运算符使用了一堆数字代码,并且需要遍历该代码并将每个" /"替换为我的函数调用。

我正拼命地做到这一点。

感谢跟进,
乍得

回答

此功能早在内核v2.6.22之前就已在/linux/lib/div64.c中引入。