【发布时间】:2015-12-20 10:31:43
【问题描述】:
今天我在 LLVM IR 中遇到了浮点转换的奇怪问题。我使用的是 Windows 10 和 llvm-3.5.2。我用 C 写了这段代码:
#include <stdio.h>
int main() {
double b = 2.0;
float c = b;
printf("%d\n", c == 2.0);
return 0;
}
我使用clang -S -emit-llvm 并获得了这个 LLVM IR:
@.str = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1
; Function Attrs: nounwind
define i32 @main() #0 {
entry:
%retval = alloca i32, align 4
%b = alloca double, align 8
%c = alloca float, align 4
store i32 0, i32* %retval
store double 2.000000e+00, double* %b, align 8
%0 = load double* %b, align 8
%conv = fptrunc double %0 to float
store float %conv, float* %c, align 4
%1 = load float* %c, align 4
%conv1 = fpext float %1 to double
%cmp = fcmp oeq double %conv1, 2.000000e+00
%conv2 = zext i1 %cmp to i32
%call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i32 %conv2) #1
ret i32 0
}
; Function Attrs: nounwind
declare i32 @printf(i8*, ...) #0
哪个执行(在llvm-as 和lli 之后)给出了0 的错误结果!
问题似乎出在fpext 这不是很直观,因为它应该是这种“安全”转换(比fptrunc 更好的方向,即从双精度到浮点)。
我是这么认为的,因为当我改变这一行时:
%cmp = fcmp oeq double %conv1, 2.000000e+00
到这里:
%cmp = fcmp oeq float %1, 2.000000e+00
那么结果如预期1.
所以我的问题是为什么会这样,为什么转换失败?这是 LLVM 中的某种错误吗?还是存在一些更好的这种转换方式?或者也许我用 C 写了一个不安全的代码?如果是这样,那么使用浮点数就有很大的问题。执行此转换时,我对 clang 没有影响。例如,假设我想在上面输出c。然后我写了printf("%f", c),clang 生成了将c 转换为double 的代码,然后将其传递给printf。结果又不正确了。那么如何安全地打印一个浮点数呢?如何安全地使用花车?
我不确定这个问题是否也出现在 Linux 上。
【问题讨论】:
-
在我的带有 LLVM 3.4 的 Linux 机器上,Clang 也会产生 fcmp double 但会计算出您预期的结果。
-
我不明白——你为什么认为
float和double的精确比较是真的?不能保证。您问“如何打印浮点数”,但您没有在实际代码中打印浮点数。你见过这个吗? docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html -
好的,我明白了。你有一点,当混合这些类型时,我通常不应该期待好的结果。所以现在的问题实际上是关于打印花车。正如我写的,当我尝试在我的机器上使用上面的代码使用
printf("%f", c)执行此操作时,我也得到了0。似乎 printf 破坏了我的浮点数(因为检查时它确实等于 2.0f)。我不知道如何解决它。 -
@xan 用于打印浮动,请参阅 stackoverflow.com/a/63156309/8925535