Fixture 173
float int conversions
C · 7 functions · 4 lanes · 15 of 28 function-lanes behave identically
4 of 4 lanes have a function that returns a different result after decompilation: clang-O2 (3/7), clang-O0 (4/7), gcc-O0 (4/7), gcc-O2 (4/7).
Conversions in both directions between integers and IEEE binary32/binary64.
Float-to-integer TRUNCATES TOWARD ZERO — -2.75 becomes -2, not -3 — and is UNDEFINED when the value does not fit the destination, so every function here range-checks before converting. The predicate is spelled !(in range) rather than out of range so that a NaN, which compares false against everything, takes the reject path instead of falling through it.
The reverse direction is exact for small magnitudes and ROUNDS for large ones: (float)INT32_MAX is 2147483648.0f, one above the value it came from. int32_round_trip_delta measures precisely that, which is why a recovery that drops the conversion, or performs it at the wrong width, cannot fake it.
#include <stdint.h>
/* Conversions in both directions between integers and IEEE binary32/binary64.
*
* Float-to-integer TRUNCATES TOWARD ZERO — `-2.75` becomes `-2`, not `-3` — and
* is UNDEFINED when the value does not fit the destination, so every function
* here range-checks before converting. The predicate is spelled `!(in range)`
* rather than `out of range` so that a NaN, which compares false against
* everything, takes the reject path instead of falling through it.
*
* The reverse direction is exact for small magnitudes and ROUNDS for large
* ones: `(float)INT32_MAX` is 2147483648.0f, one above the value it came from.
* `int32_round_trip_delta` measures precisely that, which is why a recovery
* that drops the conversion, or performs it at the wrong width, cannot fake it.
*/
#define FP173_INT32_LIMIT 2147483648.0f /* 2^31, exact in binary32 */
#define FP173_UINT32_LIMIT 4294967296.0f /* 2^32, exact in binary32 */
#define FP173_INT64_LIMIT 9223372036854775808.0 /* 2^63, exact in binary64 */
#define FP173_ROUND_LIMIT 1000000.0f
/* Shared by the two binary32 -> int32 paths so both reject identically. */
static int fp173_in_int32_range(float value) {
return value >= -FP173_INT32_LIMIT && value < FP173_INT32_LIMIT;
}
__attribute__((noinline)) int32_t truncate_toward_zero(float value) {
if (!fp173_in_int32_range(value)) {
return 0;
}
return (int32_t)value;
}
__attribute__((noinline)) int64_t truncate_double_to_i64(double value) {
if (!(value >= -FP173_INT64_LIMIT && value < FP173_INT64_LIMIT)) {
return 0;
}
return (int64_t)value;
}
/* Unsigned truncation is a different instruction sequence from the signed one
* (the destination has no sign bit to borrow), and a recovery that types the
* result signed disagrees for everything above 2^31. */
__attribute__((noinline)) uint32_t truncate_to_unsigned(float value) {
if (!(value >= 0.0f && value < FP173_UINT32_LIMIT)) {
return 0u;
}
return (uint32_t)value;
}
/* Rounding half away from zero, built out of truncation. Bounded well inside
* the int32 range so the `+/- 0.5f` cannot push the value out of it. */
__attribute__((noinline)) int32_t round_half_away_from_zero(float value) {
if (!(value >= -FP173_ROUND_LIMIT && value <= FP173_ROUND_LIMIT)) {
return 0;
}
return (int32_t)(value >= 0.0f ? value + 0.5f : value - 0.5f);
}
__attribute__((noinline)) float widen_int_to_float(int32_t value) {
return (float)value;
}
__attribute__((noinline)) double widen_long_to_double(int64_t value) {
return (double)value;
}
/* How far one int32 -> binary32 -> int32 round trip moved the value. The
* subtraction is performed at 64 bits: the two operands are within 128 of each
* other, but computing the difference in `int32_t` would still be reasoning
* about overflow that does not need to exist. */
__attribute__((noinline)) int32_t int32_round_trip_delta(int32_t value) {
float widened = (float)value;
if (!fp173_in_int32_range(widened)) {
return 0;
}
return (int32_t)((int64_t)(int32_t)widened - (int64_t)value);
} Recovered C
Generated by glaurung decompile --style decbench at b47f6b43.
baseline.json records the result after recompiling the C and calling it beside the
original with seeded inputs.
clang -O2
3/7int32_round_trip_delta fail 7 lines
// glaurung: int32_round_trip_delta @ 0x11d0
int32_t int32_round_trip_delta(int32_t arg0) {
float widened;
long ret;
ret = (unsigned long)((unsigned int)(((int)((float)((int)(arg0))) - arg0)));
return ((((0x4f000000 < ret) | (ret != ret)) | ((0x4f000000 == ret) | (ret != ret))) ? 0 : ret);
} round_half_away_from_zero fail 7 lines
// glaurung: round_half_away_from_zero @ 0x1170
int32_t round_half_away_from_zero(float arg0) {
long ret;
ret = (((arg0 < 0) | (arg0 != arg0)) == 0);
ret = (((0x49742400 < arg0) | (arg0 != arg0)) ? 0 : (int)((*(float *)((0x2028 + (ret * 4))) + arg0)));
return (((arg0 < 0xc9742400) | (arg0 != arg0)) ? 0 : ret);
} truncate_double_to_i64 fail 8 lines
// glaurung: truncate_double_to_i64 @ 0x1120
static unsigned char glaurung_global_2020[16] __attribute__((aligned(16)));
int64_t truncate_double_to_i64(double arg0) {
extern unsigned char glaurung_global_2020[16];
long t14;
t14 = *(long *)(&glaurung_global_2020[0]);
return (((arg0 < t14) | ((arg0 != arg0) | (t14 != t14))) ? 0 : ((((0x43e0000000000000 < arg0) | (arg0 != arg0)) | ((0x43e0000000000000 == arg0) | (arg0 != arg0))) ? 0 : (long long)(arg0)));
} truncate_to_unsigned pass 4 lines
// glaurung: truncate_to_unsigned @ 0x1150
uint32_t truncate_to_unsigned(float arg0) {
return (((arg0 < 0) | (arg0 != arg0)) ? 0 : ((((0x4f800000 < arg0) | (arg0 != arg0)) | ((0x4f800000 == arg0) | (arg0 != arg0))) ? 0 : (long long)(arg0)));
} truncate_toward_zero fail 4 lines
// glaurung: truncate_toward_zero @ 0x1100
int32_t truncate_toward_zero(float arg0) {
return (((arg0 < 0xcf000000) | (arg0 != arg0)) ? 0 : ((((0x4f000000 < arg0) | (arg0 != arg0)) | ((0x4f000000 == arg0) | (arg0 != arg0))) ? 0 : (int)(arg0)));
} widen_int_to_float pass 4 lines
// glaurung: widen_int_to_float @ 0x11b0
float widen_int_to_float(int32_t arg0) {
return (float)((int)(arg0));
} widen_long_to_double pass 4 lines
// glaurung: widen_long_to_double @ 0x11c0
double widen_long_to_double(int64_t arg0) {
return (double)((long long)(arg0));
} clang -O0
4/7int32_round_trip_delta pass 14 lines
// glaurung: int32_round_trip_delta @ 0x12d0
int32_t int32_round_trip_delta(int32_t arg0) {
extern int fp173_in_int32_range(float);
float widened;
long var7;
// x86-64 prologue: save rbp, frame 16 bytes
widened = (float)((int)(arg0));
var7 = ((long (*)(float))fp173_in_int32_range)(widened);
if (((unsigned long)((unsigned int)(var7)) != 0)) {
return (unsigned int)(((long)((int)((int)(widened))) - (long)(arg0)));
} else {
return 0;
}
} round_half_away_from_zero fail 18 lines
// glaurung: round_half_away_from_zero @ 0x1220
int32_t round_half_away_from_zero(float arg0) {
int local_4;
float local_c;
// x86-64 prologue: save rbp
if (((arg0 < 0xc9742400) | (arg0 != arg0))) {
// x86-64 epilogue: restore rbp
return 0;
}
if ((((0x49742400 < arg0) | (arg0 != arg0)) != 0)) {
// x86-64 epilogue: restore rbp
return 0;
}
local_c = (((arg0 < 0) | (arg0 != arg0)) ? (arg0 - ((union { unsigned int bits; float value; }){ .bits = (unsigned int)(0x3f000000) }).value) : (((union { unsigned int bits; float value; }){ .bits = (unsigned int)(0x3f000000) }).value + arg0));
local_4 = (int)(local_c);
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
} truncate_double_to_i64 fail 20 lines
// glaurung: truncate_double_to_i64 @ 0x1180
static unsigned char glaurung_global_2018[16] __attribute__((aligned(16)));
int64_t truncate_double_to_i64(double arg0) {
extern unsigned char glaurung_global_2018[16];
long local_8;
long var5;
// x86-64 prologue: save rbp
var5 = *(long *)(&glaurung_global_2018[0]);
if (((arg0 < var5) | ((arg0 != arg0) | (var5 != var5)))) {
// x86-64 epilogue: restore rbp
return 0;
}
if (((((0x43e0000000000000 < arg0) | (arg0 != arg0)) | ((0x43e0000000000000 == arg0) | (arg0 != arg0))) != 0)) {
// x86-64 epilogue: restore rbp
return 0;
}
local_8 = (long long)(arg0);
// x86-64 epilogue: restore rbp
return local_8;
} truncate_to_unsigned pass 16 lines
// glaurung: truncate_to_unsigned @ 0x11d0
uint32_t truncate_to_unsigned(float arg0) {
int local_4;
// x86-64 prologue: save rbp
if (((arg0 < 0) | (arg0 != arg0))) {
// x86-64 epilogue: restore rbp
return 0;
}
if (((((0x4f800000 < arg0) | (arg0 != arg0)) | ((0x4f800000 == arg0) | (arg0 != arg0))) != 0)) {
// x86-64 epilogue: restore rbp
return 0;
}
local_4 = (long long)(arg0);
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
} truncate_toward_zero fail 12 lines
// glaurung: truncate_toward_zero @ 0x1100
int32_t truncate_toward_zero(float arg0) {
extern int fp173_in_int32_range(float);
long var5;
// x86-64 prologue: save rbp, frame 16 bytes
var5 = ((long (*)(float))fp173_in_int32_range)(arg0);
if (((unsigned long)((unsigned int)(var5)) != 0)) {
return (unsigned int)((int)(arg0));
} else {
return 0;
}
} widen_int_to_float pass 6 lines
// glaurung: widen_int_to_float @ 0x12b0
float widen_int_to_float(int32_t arg0) {
// x86-64 prologue: save rbp
// x86-64 epilogue: restore rbp
return (float)((int)(arg0));
} widen_long_to_double pass 6 lines
// glaurung: widen_long_to_double @ 0x12c0
double widen_long_to_double(int64_t arg0) {
// x86-64 prologue: save rbp
// x86-64 epilogue: restore rbp
return (double)((long long)(arg0));
} gcc -O0
4/7int32_round_trip_delta pass 14 lines
// glaurung: int32_round_trip_delta @ 0x12b1
int32_t int32_round_trip_delta(int32_t arg0) {
extern int fp173_in_int32_range(float);
float widened;
long var12;
// x86-64 prologue: save rbp, frame 24 bytes
widened = (float)((int)(arg0));
var12 = ((long (*)(long))fp173_in_int32_range)((unsigned long)((unsigned int)(((union { unsigned int bits; float value; }){ .value = widened }).bits)));
if (((unsigned long)((unsigned int)(var12)) != 0)) {
return (unsigned int)(((int)(widened) - (unsigned long)((unsigned int)(arg0))));
} else {
return 0;
}
} round_half_away_from_zero fail 17 lines
// glaurung: round_half_away_from_zero @ 0x11fd
int32_t round_half_away_from_zero(float arg0) {
// x86-64 prologue: save rbp
if (((unsigned long)((unsigned char)(((unsigned long)((unsigned int)(((((arg0 < 0xc9742400) | (arg0 != arg0)) == 0) ^ 1))) & 255))) != 0)) {
// x86-64 epilogue: restore rbp
return 0;
}
if (((unsigned long)((unsigned char)(((unsigned long)((unsigned int)(((((0x49742400 < arg0) | (arg0 != arg0)) == 0) ^ 1))) & 255))) != 0)) {
// x86-64 epilogue: restore rbp
return 0;
}
if (((arg0 < 0) | (arg0 != arg0))) {
return (int)((arg0 - ((union { unsigned int bits; float value; }){ .bits = (unsigned int)(0x3f000000) }).value));
} else {
return (int)((((union { unsigned int bits; float value; }){ .bits = (unsigned int)(0x3f000000) }).value + arg0));
}
} truncate_double_to_i64 fail 18 lines
// glaurung: truncate_double_to_i64 @ 0x1163
static unsigned char glaurung_global_2008[16] __attribute__((aligned(16)));
int64_t truncate_double_to_i64(double arg0) {
extern unsigned char glaurung_global_2008[16];
long t14;
// x86-64 prologue: save rbp
t14 = *(long *)(&glaurung_global_2008[0]);
if (((unsigned long)((unsigned char)(((unsigned long)((unsigned int)(((((arg0 < t14) | ((arg0 != arg0) | (t14 != t14))) == 0) ^ 1))) & 255))) != 0)) {
// x86-64 epilogue: restore rbp
return 0;
}
if (((unsigned long)((unsigned char)(((unsigned long)((unsigned int)((((((0x43e0000000000000 < arg0) | (arg0 != arg0)) | ((0x43e0000000000000 == arg0) | (arg0 != arg0))) == 0) ^ 1))) & 255))) != 0)) {
// x86-64 epilogue: restore rbp
return 0;
}
// x86-64 epilogue: restore rbp
return (long long)(arg0);
} truncate_to_unsigned pass 14 lines
// glaurung: truncate_to_unsigned @ 0x11b1
uint32_t truncate_to_unsigned(float arg0) {
// x86-64 prologue: save rbp
if (((unsigned long)((unsigned char)(((unsigned long)((unsigned int)(((((arg0 < 0) | (arg0 != arg0)) == 0) ^ 1))) & 255))) != 0)) {
// x86-64 epilogue: restore rbp
return 0;
}
if (((unsigned long)((unsigned char)(((unsigned long)((unsigned int)((((((0x4f800000 < arg0) | (arg0 != arg0)) | ((0x4f800000 == arg0) | (arg0 != arg0))) == 0) ^ 1))) & 255))) != 0)) {
// x86-64 epilogue: restore rbp
return 0;
}
// x86-64 epilogue: restore rbp
return (long long)(arg0);
} truncate_toward_zero fail 12 lines
// glaurung: truncate_toward_zero @ 0x1130
int32_t truncate_toward_zero(float arg0) {
extern int fp173_in_int32_range(float);
long var5;
// x86-64 prologue: save rbp, frame 8 bytes
var5 = ((long (*)(float))fp173_in_int32_range)(arg0);
if (((unsigned long)((unsigned int)(var5)) != 0)) {
return (int)(arg0);
} else {
return 0;
}
} widen_int_to_float pass 6 lines
// glaurung: widen_int_to_float @ 0x1279
float widen_int_to_float(int32_t arg0) {
// x86-64 prologue: save rbp
// x86-64 epilogue: restore rbp
return (float)((int)(arg0));
} widen_long_to_double pass 6 lines
// glaurung: widen_long_to_double @ 0x128f
double widen_long_to_double(int64_t arg0) {
// x86-64 prologue: save rbp
// x86-64 epilogue: restore rbp
return (double)((long long)(arg0));
} gcc -O2
4/7int32_round_trip_delta pass 13 lines
// glaurung: int32_round_trip_delta @ 0x1200
int32_t int32_round_trip_delta(int32_t arg0) {
float widened;
float var5;
var5 = (float)((int)(arg0));
if (((var5 < 0xcf000000) | (var5 != var5))) {
return 0;
}
if (((((0x4f000000 < var5) | (var5 != var5)) | ((0x4f000000 == var5) | (var5 != var5))) == 0)) {
return (unsigned int)(((int)(var5) - arg0));
}
return 0;
} round_half_away_from_zero fail 13 lines
// glaurung: round_half_away_from_zero @ 0x1190
int32_t round_half_away_from_zero(float arg0) {
if (((arg0 < 0xc9742400) | (arg0 != arg0))) {
return 0;
}
if (((0x49742400 < arg0) | (arg0 != arg0))) {
return 0;
}
if (((arg0 < 0) | (arg0 != arg0))) {
return (int)((arg0 - ((union { unsigned int bits; float value; }){ .bits = (unsigned int)(0x3f000000) }).value));
}
return (int)((arg0 + ((union { unsigned int bits; float value; }){ .bits = (unsigned int)(0x3f000000) }).value));
} truncate_double_to_i64 fail 14 lines
// glaurung: truncate_double_to_i64 @ 0x1130
static unsigned char glaurung_global_2018[16] __attribute__((aligned(16)));
int64_t truncate_double_to_i64(double arg0) {
extern unsigned char glaurung_global_2018[16];
long t14;
t14 = *(long *)(&glaurung_global_2018[0]);
if (((arg0 < t14) | ((arg0 != arg0) | (t14 != t14)))) {
return 0;
}
if (((((0x43e0000000000000 < arg0) | (arg0 != arg0)) | ((0x43e0000000000000 == arg0) | (arg0 != arg0))) == 0)) {
return (long long)(arg0);
}
return 0;
} truncate_to_unsigned pass 10 lines
// glaurung: truncate_to_unsigned @ 0x1160
uint32_t truncate_to_unsigned(float arg0) {
if (((arg0 < 0) | (arg0 != arg0))) {
return 0;
}
if (((((0x4f800000 < arg0) | (arg0 != arg0)) | ((0x4f800000 == arg0) | (arg0 != arg0))) == 0)) {
return (long long)(arg0);
}
return 0;
} truncate_toward_zero fail 10 lines
// glaurung: truncate_toward_zero @ 0x1100
int32_t truncate_toward_zero(float arg0) {
if (((arg0 < 0xcf000000) | (arg0 != arg0))) {
return 0;
}
if (((((0x4f000000 < arg0) | (arg0 != arg0)) | ((0x4f000000 == arg0) | (arg0 != arg0))) == 0)) {
return (int)(arg0);
}
return 0;
} widen_int_to_float pass 4 lines
// glaurung: widen_int_to_float @ 0x11e0
float widen_int_to_float(int32_t arg0) {
return (float)((int)(arg0));
} widen_long_to_double pass 4 lines
// glaurung: widen_long_to_double @ 0x11f0
double widen_long_to_double(int64_t arg0) {
return (double)((long long)(arg0));
}