Summary of the foo.ll Transformation

Original Transformation

The source in foo.ll checks whether:

fabs(float(x) - float(y)) < C

can be replaced with:

x == y

The concrete file uses:

Alive2 validates the original example with:

alive-tv --disable-undef-input --smt-to=30000 foo.ll

and reports:

Transformation seems to be correct!

General Rule

For a binary floating-point format with precision p bits, the rewrite is sound for:

%a = uitofp_or_sitofp iN %x to FP
%b = uitofp_or_sitofp iN %y to FP
%d = fsub FP %a, %b
%u = call FP @llvm.fabs(FP %d)
%cmp = fcmp olt FP-or-wider %u, C

to:

%eq = icmp eq iN %x, %y

when both of these hold:

  1. 0 < C <= 1
  2. The integer-to-FP conversion is injective over the full input domain

That injectivity condition becomes:

Why This Works

Under those conditions:

So fabs(a - b) < C is equivalent to x == y exactly for 0 < C <= 1.

The fpext from float to double in the original file does not change the argument; it only compares the same nonnegative value in a wider format.

float Boundaries

For IEEE float, the precision is p = 24. Therefore:

Alive2 Boundary Checks

These checks were run with --disable-undef-input --smt-to=30000.

Verified

Rejected

Signed Failure Past the Boundary

For sitofp i26 -> float, the transformation is not sound because conversion is no longer injective. A simple example is:

Bottom Line

The rewrite

fabs(uitofp_or_sitofp(x) - uitofp_or_sitofp(y)) < C
    -> x == y

is sound when:

For float, that means: