1

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.

xan
  • 1,053
  • 1
  • 10
  • 29
  • 1
    On my Linux machine with LLVM 3.4 Clang also produces a fcmp double but computes the result you expected. – box Dec 20 '15 at 10:37
  • 1
    I don't follow--why do you expect exact-comparison of a `float` and a `double` to be true? It isn't guaranteed. You ask "how to print a float" but you aren't printing floats in your actual code. Have you seen this? https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html – John Zwinck Dec 20 '15 at 10:37
  • Ok, I see. You have a point, that when mixing those types I shouldn't expect good results in general. So now the question is really about printing floats. As I wrote when I try to do this with `printf("%f", c)` on my machine with this code above I get `0` as well. Seems that printf destroys my float (because it really equals 2.0f when checked). I don't know how to workaround it. – xan Dec 20 '15 at 11:12
  • @xan for printing floats see https://stackoverflow.com/a/63156309/8925535 – droptop Jul 31 '20 at 14:45

1 Answers1

1

I worked in ubuntu14.04 LLVM3.6, I have the save output like you, but also get the right answer.

I think you should check you clang version, if the clang used other version of llvm, your may have the unexpected answer.

like this:

clang --version

Ubuntu clang version 3.6.0-2ubuntu1~trusty1 (tags/RELEASE_360/final) (based on LLVM 3.6.0)
Target: x86_64-pc-linux-gnu
Thread model: posix

the llvm-IR:

; ModuleID = 'main.c'
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-linux-gnu"

@.str = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1

; Function Attrs: nounwind uwtable
define i32 @main() #0 {
  %1 = alloca i32, align 4
  %b = alloca double, align 8
  %c = alloca float, align 4
  store i32 0, i32* %1
  store double 2.000000e+00, double* %b, align 8
  %2 = load double* %b, align 8
  %3 = fptrunc double %2 to float
  store float %3, float* %c, align 4
  %4 = load float* %c, align 4
  %5 = fpext float %4 to double
  %6 = fcmp oeq double %5, 2.000000e+00
  %7 = zext i1 %6 to i32
  %8 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i32 %7)
  ret i32 0
}

declare i32 @printf(i8*, ...) #1

attributes #0 = { nounwind uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }

!llvm.ident = !{!0}

!0 = !{!"Ubuntu clang version 3.6.0-2ubuntu1~trusty1 (tags/RELEASE_360/final) (based on LLVM 3.6.0)"}