Today I came across weird problem with floating point cast in LLVM IR. I'm using Windows 10, and llvm-3.5.2. I wrote this code in C:
#include <stdio.h>
int main() {
double b = 2.0;
float c = b;
printf("%d\n", c == 2.0);
return 0;
}
And I used clang -S -emit-llvm
and obtained this 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
Which executed (after llvm-as
and lli
) gave an incorrect result of 0
!
The problem seems to be in fpext
which isn't very intuitive, because it should be this 'safe' conversion (better direction than fptrunc
which is from double to float).
I think so, because when I change this line:
%cmp = fcmp oeq double %conv1, 2.000000e+00
to this:
%cmp = fcmp oeq float %1, 2.000000e+00
then the result is as expected 1
.
So my question is why is it like that, why this conversion fails? Is this some kind of a bug in LLVM? Or there exists some better way of this type of conversion? Or maybe I wrote an unsafe code in C? If so, then there is a huge problem in using floats. I don't have an influence on clang when performing this conversions. For example let's say that I want to output c
above. Then I write printf("%f", c)
and clang generates code that casts c
to double before passing it to printf
. And the result is incorrect again. So how to safely print a float? How to safely use floats at all?
I'm not sure whether this problem appears on Linux too.