Hi, GCC happily transforms (float)-z into -(float)z, even when z is of unsigned type (when it's larger than float). That's wrong (the result should always be positive, because -z is). It seems to me that this bug exists in all reasonably recent GCC versions. The checking in convert_to_real is wrong, it compares type precisions of different classed types. The change with the least impact is to reject only TYPE_UNSIGNED inner types, but perhaps it would be better to only do the transformation if the inner type is FLOAT_TYPE_P as well.
Regstrapping on x86-64 in progress. Okay for trunk? Ciao, Michael. PR middle-end/57886 * convert.c (convert_to_real): Reject unsigned inner types. testsuite/ * c-c++-common/pr57886.c: New test. Index: convert.c =================================================================== --- convert.c (revision 200240) +++ convert.c (working copy) @@ -213,10 +213,11 @@ convert_to_real (tree type, tree expr) switch (TREE_CODE (expr)) { /* Convert (float)-x into -(float)x. This is safe for - round-to-nearest rounding mode. */ + round-to-nearest rounding mode when the inner type is signed. */ case ABS_EXPR: case NEGATE_EXPR: if (!flag_rounding_math + && !TYPE_UNSIGNED (itype) && TYPE_PRECISION (type) < TYPE_PRECISION (TREE_TYPE (expr))) return build1 (TREE_CODE (expr), type, fold (convert_to_real (type, Index: testsuite/c-c++-common/pr57886.c =================================================================== --- testsuite/c-c++-common/pr57886.c (revision 0) +++ testsuite/c-c++-common/pr57886.c (working copy) @@ -0,0 +1,12 @@ +/* { dg-do run } */ + +float global; +int main() +{ + unsigned long z = 1; + float x = -z; + global = x; + if (global < 0) + __builtin_abort (); + return 0; +}