如何在LLVM位码中实现floor、ceil和round?

4
我正在为一个基于LLVM的小型编程语言编写数学函数,目前我遇到了如何实现常见的取整函数floor、ceil和round(偶数)的问题。首先,因为我没有找到这些函数的算法描述,其次,我不熟悉LLVM在舍入方面的能力。
正确地对负数进行四舍五入是必须的,但精确舍入到特定精度则不是必需的。将结果舍入为整数值即可。如果可以指向任何现有的从LLVM位代码中使用的实现,则也可以解决问题。

4
并非真正的答案,否则我会给您一个。这只是一些轶事信息。我发现当我处于这种情况时,我会编写C代码以重现该函数,然后使用clang或llvm-gcc进行编译,并查看生成的llvm ir。这通常可以为我提供足够好的想法,以便我应该如何继续。 - jer
这可能略微偏题,但我认为在实现自己的数学库时考虑到浮点数运算的特殊性并编写正确的算法是非常重要的。这并不像一开始看起来那么简单,可以从这里看到例如这里 - Thies Heidecke
3个回答

2
你需要从LLVM语言参考手册开始。

你可以从实现trunc( )开始,例如以下示例(警告,不要真的使用这个示例,它只是作为一个示例,并不正确。请参见下面的讨论):

define float @trunc(float %x) {
    %rounded = fptosi float %x to i32
    %asFloat = sitofp i32 %rounded to float
    ret float %asFloat
}
fptosi ... to ...指令的文档说明按照向零舍入舍入模式将浮点数舍入为整数值。sitofp ... to ...指令将该值转换回浮点数值以返回。
但是,这种实现存在问题;阅读我链接的语言参考时,“如果舍入到最近的整数结果无法适应目标类型,则的行为未定义。”
不过这很容易解决,因为所有足够大的浮点数已经是整数,并且不需要舍入。如果x的绝对值大于或等于2 ^ 23,则可以直接返回x本身。
(这全部是单精度情况;对于双精度,您可能需要使用并且需要使用2 ^ 52的阈值)
对于其他操作,例如和,可以从开始,然后检查余数并相应地调整结果。
或者,您可以调用主机平台的C库,它已经包括了这些函数。这是许多编程语言采用的方法。

调用本地C库是个好主意。然而,根据LLVM语言参考,fptosi向0舍入,而不是向最近的舍入。 - keiter
@voxcogitatio:非常好的观点;这表明我在写那段代码片段时没有仔细阅读参考文献。我会在今天稍后进行更正。 - Stephen Canon

1

如果你在Google Code Search上搜索,会有一些结果。链接的示例假定IEEE浮点数。通常,普通PC的编译器只会将floor编译为浮点指令。例如,原始的387算术处理器具有指令FPREM,它或多或少地执行了floor所需的部分操作。


1
我已经按照以下方式为浮点向量实现了floor:'截断'值x,然后比较x和trunc(x)。当trunc(x)>x时,减去1,因为floor(x)必须始终最多为x。 我用Haskell编写了这个代码。我不知道这是否有帮助。请参见http://code.haskell.org/~thielema/llvm-extra/src/LLVM/Extra/Vector.hs中的floorLogical。
舍入到偶数通常很昂贵,也没有太大用处。我只是使用floor(x+0.5)。在SSE4.1中还有roundss、roundps等函数。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接