![]() |
1
28
您可以使用的Division版本 Russian Peasant Multiplication . 要查找其余部分,请执行(伪代码):
模量保留在A中。 您将需要实现移位、比较和减法,以便对由一对64位数字组成的值进行操作,但这非常简单。 这将最多循环255次(使用128位A)。当然,你需要对零除数做一个预检查。 |
![]() |
2
13
也许你正在寻找一个完成的程序,但是多精度算法的基本算法可以在Knuth的 Art of Computer Programming ,第2卷。你可以在网上找到描述的分割算法 here . 这些算法处理任意多精度算法,因此比您需要的更通用,但是您应该能够将它们简化为64位或32位数字上的128位算法。为合理的工作量做好准备:(a)理解算法;(b)将其转换为C或汇编程序。 您可能还想退房 Hacker's Delight 它充满了非常聪明的汇编程序和其他低级黑客,包括一些多精度的算法。 |
![]() |
3
11
鉴于
如果编译器支持64位整数,那么这可能是最简单的方法。
MSVC在32位x86上实现64位模块是一个毛茸茸的循环填充程序集。(
|
![]() |
4
7
这几乎是未经测试的部分速度修正mod128by64“俄罗斯农民”算法函数。不幸的是,我是Delphi用户,所以这个函数在Delphi下工作。:)但是汇编程序几乎是相同的,所以…
至少可以再进行一次速度优化!在“大除数移位优化”之后,我们可以测试除数高位,如果是0,我们不需要使用额外的BH寄存器作为第65位来存储它。所以循环的展开部分看起来像:
|
![]() |
5
4
我想分享一些想法。 恐怕不像msn建议的那么简单。 在表达式中:
乘法和加法都可能溢出。我认为人们可以考虑到这一点,并且仍然使用一些修改后的一般概念,但是有一些东西告诉我这将变得非常可怕。 我很好奇64位模块操作是如何在MSVC中实现的,我试图找出一些东西。我真的不知道程序集,我所能得到的只是Express版本,没有vc\crt\src\intel\llrem.asm的源代码,但是我想我在玩了一段调试程序和反汇编输出之后,设法了解了正在发生的事情。我试图搞清楚,如果是正整数,除数=2^32,余数是如何计算的。当然,有一些代码处理负数,但我没有深入研究。 我是这样看的: 如果除数>=2^32,则被除数和除数都会根据需要右移,以使除数适合32位。换言之:如果用n位数字将除数写为二进制,n>32,除数和被除数的n-32最低有效位都将被丢弃。之后,使用硬件支持将64位整数除以32位整数来执行除法。结果可能不正确,但我认为可以证明,结果最多可能会偏离1。除法后,除数(原除数)乘以结果,再减去被除数的积。然后,如果需要的话,通过加或减除数来修正(如果除法的结果被除掉了一个除数)。 利用硬件支持64位32位除法,很容易将128位整数除以32位整数。如果除数为<2^32,则只需执行以下4个除法即可计算余数: 假设股息存储在:
其余部分将包括:
在这4个步骤之后,变量余数将保存您要查找的内容。 (如果我弄错了,请不要杀我。我甚至不是程序员) 如果除数大于2^32-1,我就没有好消息了。在我之前描述的过程中,我没有完整的证据证明班次结束后的结果不超过1,我相信MSVC正在使用这个过程。然而,我认为这与这个事实有关,被丢弃的部分至少比除数小2^31倍,被除数小于2^64,除数大于2^32-1,所以结果小于2^32。 如果红利有128位,那么丢弃位的技巧就行不通了。所以在一般情况下,最好的解决方案可能是由GJ或CAF提出的。(好吧,这可能是最好的,即使丢弃比特工作。128位整数上的除法、乘法减法和更正可能较慢。) 我也在考虑使用浮点硬件。x87浮点单元使用80位精度格式,分数64位长。我想我们可以得到64位乘64位除法的精确结果。(不是直接使用余数,而是使用乘法和减法的余数,如“msvc过程”)。如果股息=2^64和<2^128以浮点格式存储,则与在“msvc过程”中丢弃最低有效位类似。也许有人能证明这种情况下的错误是有限度的,并发现它是有用的。我不知道它是否有机会比GJ的解决方案更快,但也许值得一试。 |
![]() |
6
4
解决办法取决于你到底想解决什么问题。 例如,如果在环模中执行算术运算,则使用64位整数 Montgomerys reduction 效率很高。当然,这假设您多次使用相同的模量,并且将环的元素转换为特殊的表示形式是值得的。 为了对蒙哥马利减少的速度给出一个非常粗略的估计:我有一个旧的基准测试,它在2.4GHz内核2上用64位模和1600纳秒的模幂进行模幂。这个求幂可以完成大约96个模块化乘法(和模块化约简),因此每个模块化乘法需要大约40个周期。 |
![]() |
7
3
我做了两个版本的mod128by64“俄罗斯农民”划分功能:经典和速度优化。速度优化可以在我的3GHz PC上每秒进行超过1000.000次随机计算,比经典功能快三倍以上。 如果我们比较128乘64和64乘64位模运算的执行时间,这个函数的速度只慢50%。 典型的俄罗斯农民:
速度优化的俄罗斯农民:
|
![]() |
8
3
@caf接受的答案确实不错,评价也很高,但它包含了多年未见的错误。 为了帮助测试这一点和其他解决方案,我发布了一个测试工具,并让它成为社区wiki。
|
![]() |
9
3
我知道这个问题指定了32位代码,但64位的答案可能对其他人有用或有趣。
是的,64b/32b=>32b分区确实是128b%64b=>64b.libgcc的有用构建块。
提供更广泛的多精度库,例如 https://gmplib.org/manual/Integer-Division.html#Integer-Division .
64位机器上的GNU C
确实提供了
X86—64
编译:
Godbolt compiler explorer
)一两个
这个
对于x86-64
(以及其他具有硬件划分指令的架构)
快速路径
(什么时候
回退路径仍然只使用两个64位
注意libgcc的
对于相同的重复模
|
![]() |
10
2
一般来说,除法运算速度慢,乘法运算速度快,移位速度快。从我迄今为止看到的答案来看,大多数答案都使用了使用位移位的蛮力方法。还有另一种方式。它是否更快还有待观察(也就是说分析它)。 不用除法,用倒数乘以。因此,要发现a%b,首先计算b的倒数。1/b.这可以通过使用牛顿-拉斐逊收敛方法的几个循环来实现。要做到这一点,将取决于表中一组良好的初始值。 关于牛顿-拉斐逊方法的更多细节,请参考 http://en.wikipedia.org/wiki/Division_(digital) 一旦有了倒数,商q=a*1/b。 余数r=a-q*b。 为了确定这是否会比蛮力更快(因为我们将使用32位寄存器模拟64位和128位数字,所以会有更多的乘法),请对其进行分析。 如果代码中的b是常量,则可以预先计算倒数,并使用最后两个公式进行简单计算。这,我肯定会比移位更快。 希望这有帮助。 |
![]() |
11
1
如果您最近使用的是x86计算机,那么SSE2+有128位寄存器。我从来没有尝试过为除基本x86以外的任何东西编写程序集,但我怀疑那里有一些指南。 |
![]() |
12
1
如果128位无符号乘63位无符号足够好,那么它可以在一个循环中完成,最多完成63个循环。 通过将MSN的溢出问题限制为1位,将其视为一个建议的解决方案。我们通过将问题分解为2,模块化乘法,并在最后添加结果来实现。 在下面的示例中,upper对应于最高有效的64位,lower对应于最低有效的64位,div是除数。
唯一的问题是,如果除数是64位,那么我们会得到1位溢出(信息丢失),从而产生错误的结果。 我还没有想出一个处理溢流的好方法,这让我很恼火。 |
![]() |
13
-2
由于在C中没有预先定义的128位整数类型,a的位必须在数组中表示。虽然B(64位整数)可以存储在 无符号长长整型 变量,为了有效地处理A和B,需要将B的位放入另一个数组中。 之后,b递增为bx2、bx3、bx4,…直到它是最大的B小于A。然后(A-B)可以计算,使用一些基础2的减法知识。 这是您正在寻找的解决方案吗? |