real.c (real_trunc, [...]): New functions to implement trunc, floor and ceil respecti...
authorRoger Sayle <roger@eyesopen.com>
Thu, 3 Jul 2003 21:38:55 +0000 (21:38 +0000)
committerRoger Sayle <sayle@gcc.gnu.org>
Thu, 3 Jul 2003 21:38:55 +0000 (21:38 +0000)
* real.c (real_trunc, real_floor, real_ceil): New functions
to implement trunc, floor and ceil respectively.
* real.h (real_trunc, real_floor, real_ceil): Prototype here.
* builtins.c (integer_valued_real_p): New function to test if
a floating point expression has an integer valued result.
(fold_trunc_transparent_mathfn): Optimize foo(foo(x)) as
foo(x) where foo is an integer rounding function.  Similarly,
optimize foo(bar(x)) as bar(x), and foo((double)(int)x) as
(double)(int)x when both foo and bar are integer rounding
functions and we don't need to honor errno.
(fold_builtin_trunc, fold_builtin_floor, fold_builtin_ceil):
New functions to fold trunc, floor and ceil.
(fold_builtin): Use fold_builtin_trunc to fold BUILT_IN_TRUNC*,
fold_builtin_floor to fold BUILT_IN_FLOOR* and fold_builtin_ceil
to fold BUILT_IN_CEIL*.
* fold-const.c (tree_expr_nonnegative_p): Handle FLOAT_EXPR and
the remaining integer rounding functions.

* gcc.dg/builtins-25.c: New testcase.
* gcc.dg/builtins-26.c: New testcase.

From-SVN: r68903

gcc/ChangeLog
gcc/builtins.c
gcc/fold-const.c
gcc/real.c
gcc/real.h
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/builtins-25.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/builtins-26.c [new file with mode: 0644]

index 7e5ccb44da8e39e7d0b3d02075c6063de292bcc6..2d9c8294307d13eda15b5ba62f93704ad59627ee 100644 (file)
@@ -1,3 +1,23 @@
+2003-07-03  Roger Sayle  <roger@eyesopen.com>
+
+       * real.c (real_trunc, real_floor, real_ceil): New functions
+       to implement trunc, floor and ceil respectively.
+       * real.h (real_trunc, real_floor, real_ceil): Prototype here.
+       * builtins.c (integer_valued_real_p): New function to test if
+       a floating point expression has an integer valued result.
+       (fold_trunc_transparent_mathfn): Optimize foo(foo(x)) as
+       foo(x) where foo is an integer rounding function.  Similarly,
+       optimize foo(bar(x)) as bar(x), and foo((double)(int)x) as
+       (double)(int)x when both foo and bar are integer rounding
+       functions and we don't need to honor errno.
+       (fold_builtin_trunc, fold_builtin_floor, fold_builtin_ceil):
+       New functions to fold trunc, floor and ceil.
+       (fold_builtin): Use fold_builtin_trunc to fold BUILT_IN_TRUNC*,
+       fold_builtin_floor to fold BUILT_IN_FLOOR* and fold_builtin_ceil
+       to fold BUILT_IN_CEIL*.
+       * fold-const.c (tree_expr_nonnegative_p): Handle FLOAT_EXPR and
+       the remaining integer rounding functions.
+
 2003-07-03  Eric Botcazou  <ebotcazou@libertysurf.fr>
 
        * config/sparc/sparc.c (function_arg_partial_nregs): Use
index e8ab4d5740d6c3a1ef50764e7b1c214047f6dfdf..94712150ad1f1d78754eaca9faaddaa1385e073c 100644 (file)
@@ -149,12 +149,16 @@ static tree fold_builtin_classify_type (tree);
 static tree fold_builtin_inf (tree, int);
 static tree fold_builtin_nan (tree, tree, int);
 static int validate_arglist (tree, ...);
+static bool integer_valued_real_p (tree);
 static tree fold_trunc_transparent_mathfn (tree);
 static bool readonly_data_expr (tree);
 static rtx expand_builtin_fabs (tree, rtx, rtx);
 static rtx expand_builtin_cabs (tree, rtx);
 static void init_builtin_dconsts (void);
 static tree fold_builtin_cabs (tree, tree, tree);
+static tree fold_builtin_trunc (tree);
+static tree fold_builtin_floor (tree);
+static tree fold_builtin_ceil (tree);
 
 /* Initialize mathematical constants for constant folding builtins.
    These constants need to be given to at least 160 bits precision.  */
@@ -5347,19 +5351,118 @@ fold_builtin_nan (tree arglist, tree type, int quiet)
   return build_real (type, real);
 }
 
-/* EXP is assumed to me builtin call where truncation can be propagated
+/* Return true if the floating point expression T has an integer value.
+   We also allow +Inf, -Inf and NaN to be considered integer values.  */
+
+static bool
+integer_valued_real_p (tree t)
+{
+  switch (TREE_CODE (t))
+    {
+    case FLOAT_EXPR:
+      return true;
+
+    case ABS_EXPR:
+    case SAVE_EXPR:
+    case NON_LVALUE_EXPR:
+      return integer_valued_real_p (TREE_OPERAND (t, 0));
+
+    case COMPOUND_EXPR:
+    case MODIFY_EXPR:
+    case BIND_EXPR:
+      return integer_valued_real_p (TREE_OPERAND (t, 1));
+
+    case PLUS_EXPR:
+    case MINUS_EXPR:
+    case MULT_EXPR:
+    case MIN_EXPR:
+    case MAX_EXPR:
+      return integer_valued_real_p (TREE_OPERAND (t, 0))
+            && integer_valued_real_p (TREE_OPERAND (t, 1));
+
+    case COND_EXPR:
+      return integer_valued_real_p (TREE_OPERAND (t, 1))
+            && integer_valued_real_p (TREE_OPERAND (t, 2));
+
+    case REAL_CST:
+      if (! TREE_CONSTANT_OVERFLOW (t))
+      {
+        REAL_VALUE_TYPE c, cint;
+
+       c = TREE_REAL_CST (t);
+       real_trunc (&cint, TYPE_MODE (TREE_TYPE (t)), &c);
+       return real_identical (&c, &cint);
+      }
+
+    case NOP_EXPR:
+      {
+       tree type = TREE_TYPE (TREE_OPERAND (t, 0));
+       if (TREE_CODE (type) == INTEGER_TYPE)
+         return true;
+       if (TREE_CODE (type) == REAL_TYPE)
+         return integer_valued_real_p (TREE_OPERAND (t, 0));
+       break;
+      }
+
+    case CALL_EXPR:
+      switch (builtin_mathfn_code (t))
+       {
+       case BUILT_IN_CEIL:
+       case BUILT_IN_CEILF:
+       case BUILT_IN_CEILL:
+       case BUILT_IN_FLOOR:
+       case BUILT_IN_FLOORF:
+       case BUILT_IN_FLOORL:
+       case BUILT_IN_NEARBYINT:
+       case BUILT_IN_NEARBYINTF:
+       case BUILT_IN_NEARBYINTL:
+       case BUILT_IN_ROUND:
+       case BUILT_IN_ROUNDF:
+       case BUILT_IN_ROUNDL:
+       case BUILT_IN_TRUNC:
+       case BUILT_IN_TRUNCF:
+       case BUILT_IN_TRUNCL:
+         return true;
+
+       default:
+         break;
+       }
+      break;
+
+    default:
+      break;
+    }
+  return false;
+}
+
+/* EXP is assumed to be builtin call where truncation can be propagated
    across (for instance floor((double)f) == (double)floorf (f).
    Do the transformation.  */
+
 static tree
 fold_trunc_transparent_mathfn (tree exp)
 {
   tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
   tree arglist = TREE_OPERAND (exp, 1);
   enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
+  tree arg;
+
+  if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+    return 0;
 
-  if (optimize && validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+  arg = TREE_VALUE (arglist);
+  /* Integer rounding functions are idempotent.  */
+  if (fcode == builtin_mathfn_code (arg))
+    return arg;
+
+  /* If argument is already integer valued, and we don't need to worry
+     about setting errno, there's no need to perform rounding.  */
+  if (! flag_errno_math && integer_valued_real_p (arg))
+    return arg;
+
+  if (optimize)
     {
-      tree arg0 = strip_float_extensions (TREE_VALUE (arglist));
+      tree arg0 = strip_float_extensions (arg);
       tree ftype = TREE_TYPE (exp);
       tree newtype = TREE_TYPE (arg0);
       tree decl;
@@ -5461,6 +5564,97 @@ fold_builtin_cabs (tree fndecl, tree arglist, tree type)
   return NULL_TREE;
 }
 
+/* Fold function call to builtin trunc, truncf or truncl.  Return
+   NULL_TREE if no simplification can be made.  */
+
+static tree
+fold_builtin_trunc (tree exp)
+{
+  tree arglist = TREE_OPERAND (exp, 1);
+  tree arg;
+
+  if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+    return 0;
+
+  /* Optimize trunc of constant value.  */
+  arg = TREE_VALUE (arglist);
+  if (TREE_CODE (arg) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg))
+    {
+      REAL_VALUE_TYPE r, x;
+      tree type = TREE_TYPE (exp);
+
+      x = TREE_REAL_CST (arg);
+      real_trunc (&r, TYPE_MODE (type), &x);
+      return build_real (type, r);
+    }
+
+  return fold_trunc_transparent_mathfn (exp);
+}
+
+/* Fold function call to builtin floor, floorf or floorl.  Return
+   NULL_TREE if no simplification can be made.  */
+
+static tree
+fold_builtin_floor (tree exp)
+{
+  tree arglist = TREE_OPERAND (exp, 1);
+  tree arg;
+
+  if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+    return 0;
+
+  /* Optimize floor of constant value.  */
+  arg = TREE_VALUE (arglist);
+  if (TREE_CODE (arg) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg))
+    {
+      REAL_VALUE_TYPE x;
+
+      x = TREE_REAL_CST (arg);
+      if (! REAL_VALUE_ISNAN (x) || ! flag_errno_math)
+       {
+         tree type = TREE_TYPE (exp);
+         REAL_VALUE_TYPE r;
+
+         real_floor (&r, TYPE_MODE (type), &x);
+         return build_real (type, r);
+       }
+    }
+
+  return fold_trunc_transparent_mathfn (exp);
+}
+
+/* Fold function call to builtin ceil, ceilf or ceill.  Return
+   NULL_TREE if no simplification can be made.  */
+
+static tree
+fold_builtin_ceil (tree exp)
+{
+  tree arglist = TREE_OPERAND (exp, 1);
+  tree arg;
+
+  if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+    return 0;
+
+  /* Optimize ceil of constant value.  */
+  arg = TREE_VALUE (arglist);
+  if (TREE_CODE (arg) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg))
+    {
+      REAL_VALUE_TYPE x;
+
+      x = TREE_REAL_CST (arg);
+      if (! REAL_VALUE_ISNAN (x) || ! flag_errno_math)
+       {
+         tree type = TREE_TYPE (exp);
+         REAL_VALUE_TYPE r;
+
+         real_ceil (&r, TYPE_MODE (type), &x);
+         return build_real (type, r);
+       }
+    }
+
+  return fold_trunc_transparent_mathfn (exp);
+}
+
 /* Used by constant folding to eliminate some builtin calls early.  EXP is
    the CALL_EXPR of a call to a builtin function.  */
 
@@ -5918,12 +6112,18 @@ fold_builtin (tree exp)
     case BUILT_IN_FLOOR:
     case BUILT_IN_FLOORF:
     case BUILT_IN_FLOORL:
+      return fold_builtin_floor (exp);
+
     case BUILT_IN_CEIL:
     case BUILT_IN_CEILF:
     case BUILT_IN_CEILL:
+      return fold_builtin_ceil (exp);
+
     case BUILT_IN_TRUNC:
     case BUILT_IN_TRUNCF:
     case BUILT_IN_TRUNCL:
+      return fold_builtin_trunc (exp);
+
     case BUILT_IN_ROUND:
     case BUILT_IN_ROUNDF:
     case BUILT_IN_ROUNDL:
index c4826c784af919ecfa1fcf3cc393e60e1bf89042..0524e1dc16e2bb4f393e11de2f8248b0966a0473 100644 (file)
@@ -8105,6 +8105,8 @@ tree_expr_nonnegative_p (tree t)
       return tree_expr_nonnegative_p (TREE_OPERAND (t, 0));
     case NON_LVALUE_EXPR:
       return tree_expr_nonnegative_p (TREE_OPERAND (t, 0));
+    case FLOAT_EXPR:
+      return tree_expr_nonnegative_p (TREE_OPERAND (t, 0));
     case RTL_EXPR:
       return rtl_expr_nonnegative_p (RTL_EXPR_RTL (t));
 
@@ -8141,6 +8143,15 @@ tree_expr_nonnegative_p (tree t)
              case BUILT_IN_FLOOR:
              case BUILT_IN_FLOORF:
              case BUILT_IN_FLOORL:
+             case BUILT_IN_NEARBYINT:
+             case BUILT_IN_NEARBYINTF:
+             case BUILT_IN_NEARBYINTL:
+             case BUILT_IN_ROUND:
+             case BUILT_IN_ROUNDF:
+             case BUILT_IN_ROUNDL:
+             case BUILT_IN_TRUNC:
+             case BUILT_IN_TRUNCF:
+             case BUILT_IN_TRUNCL:
                return tree_expr_nonnegative_p (TREE_VALUE (arglist));
 
              case BUILT_IN_POW:
index b491d885a9f11566562b2348b1c5701dd7c15c6a..6bc4d52fabc7380fff6cda548d75006ecff972e6 100644 (file)
@@ -4748,3 +4748,49 @@ real_powi (r, mode, x, n)
   return inexact;
 }
 
+/* Round X to the nearest integer not larger in absolute value, i.e.
+   towards zero, placing the result in R in mode MODE.  */
+
+void
+real_trunc (r, mode, x)
+     REAL_VALUE_TYPE *r;
+     enum machine_mode mode;
+     const REAL_VALUE_TYPE *x;
+{
+  do_fix_trunc (r, x);
+  if (mode != VOIDmode)
+    real_convert (r, mode, r);
+}
+
+/* Round X to the largest integer not greater in value, i.e. round
+   down, placing the result in R in mode MODE.  */
+
+void
+real_floor (r, mode, x)
+     REAL_VALUE_TYPE *r;
+     enum machine_mode mode;
+     const REAL_VALUE_TYPE *x;
+{
+  do_fix_trunc (r, x);
+  if (! real_identical (r, x) && r->sign)
+    do_add (r, r, &dconstm1, 0);
+  if (mode != VOIDmode)
+    real_convert (r, mode, r);
+}
+
+/* Round X to the smallest integer not less then argument, i.e. round
+   up, placing the result in R in mode MODE.  */
+
+void
+real_ceil (r, mode, x)
+     REAL_VALUE_TYPE *r;
+     enum machine_mode mode;
+     const REAL_VALUE_TYPE *x;
+{
+  do_fix_trunc (r, x);
+  if (! real_identical (r, x) && ! r->sign)
+    do_add (r, r, &dconst1, 0);
+  if (mode != VOIDmode)
+    real_convert (r, mode, r);
+}
+
index 71e3cc4b6b9f8222d6836dc72a278f871a9d5c06..fcd7ae7f15593e798440f4a112132d585be02392 100644 (file)
@@ -375,4 +375,15 @@ extern bool real_powi                      PARAMS ((REAL_VALUE_TYPE *,
                                                 const REAL_VALUE_TYPE *,
                                                 HOST_WIDE_INT));
 
+/* Standard round to integer value functions.  */
+extern void real_trunc PARAMS ((REAL_VALUE_TYPE *,
+                                enum machine_mode,
+                                const REAL_VALUE_TYPE *));
+extern void real_floor PARAMS ((REAL_VALUE_TYPE *,
+                                enum machine_mode,
+                                const REAL_VALUE_TYPE *));
+extern void real_ceil  PARAMS ((REAL_VALUE_TYPE *,
+                                enum machine_mode,
+                                const REAL_VALUE_TYPE *));
+
 #endif /* ! GCC_REAL_H */
index 7e4c0e659d4662db04de39ea7679d4460fd9b44c..b2db902de721baa020840ac03ed302355a258dd4 100644 (file)
@@ -1,3 +1,8 @@
+2003-07-03  Roger Sayle  <roger@eyesopen.com>
+
+       * gcc.dg/builtins-25.c: New testcase.
+       * gcc.dg/builtins-26.c: New testcase.
+
 2003-07-03  Janis Johnson  <janis187@us.ibm.com>
 
        * gcc.dg/compat/vector-defs.h: New file.
diff --git a/gcc/testsuite/gcc.dg/builtins-25.c b/gcc/testsuite/gcc.dg/builtins-25.c
new file mode 100644 (file)
index 0000000..4950566
--- /dev/null
@@ -0,0 +1,188 @@
+/* Copyright (C) 2003 Free Software Foundation.
+
+   Check that constant folding of built-in math functions doesn't
+   break anything and produces the expected results.
+
+   Written by Roger Sayle, 28th June 2003.  */
+
+/* { dg-do link } */
+/* { dg-options "-O2" } */
+
+extern void link_error(void);
+
+extern double trunc(double);
+extern double floor(double);
+extern double ceil(double);
+
+extern float truncf(float);
+extern float floorf(float);
+extern float ceilf(float);
+
+extern long double truncl(long double);
+extern long double floorl(long double);
+extern long double ceill(long double);
+
+void test()
+{
+  if (trunc (0.0) != 0.0)
+    link_error ();
+  if (floor (0.0) != 0.0)
+    link_error ();
+  if (ceil (0.0) != 0.0)
+    link_error ();
+
+  if (trunc (6.0) != 6.0)
+    link_error ();
+  if (floor (6.0) != 6.0)
+    link_error ();
+  if (ceil (6.0) != 6.0)
+    link_error ();
+
+  if (trunc (-8.0) != -8.0)
+    link_error ();
+  if (floor (-8.0) != -8.0)
+    link_error ();
+  if (ceil (-8.0) != -8.0)
+    link_error ();
+
+  if (trunc (3.2) != 3.0)
+    link_error ();
+  if (floor (3.2) != 3.0)
+    link_error ();
+  if (ceil (3.2) != 4.0)
+    link_error ();
+
+  if (trunc (-2.8) != -2.0)
+    link_error ();
+  if (floor (-2.8) != -3.0)
+    link_error ();
+  if (ceil (-2.8) != -2.0)
+    link_error ();
+
+  if (trunc (0.01) != 0.0)
+    link_error ();
+  if (floor (0.01) != 0.0)
+    link_error ();
+  if (ceil (0.01) != 1.0)
+    link_error ();
+
+  if (trunc (-0.7) != 0.0)
+    link_error ();
+  if (floor (-0.7) != -1.0)
+    link_error ();
+  if (ceil (-0.7) != 0.0)
+    link_error ();
+}
+
+void testf()
+{
+  if (truncf (0.0f) != 0.0f)
+    link_error ();
+  if (floorf (0.0f) != 0.0f)
+    link_error ();
+  if (ceilf (0.0f) != 0.0f)
+    link_error ();
+
+  if (truncf (6.0f) != 6.0f)
+    link_error ();
+  if (floorf (6.0f) != 6.0f)
+    link_error ();
+  if (ceilf (6.0f) != 6.0f)
+    link_error ();
+
+  if (truncf (-8.0f) != -8.0f)
+    link_error ();
+  if (floorf (-8.0f) != -8.0f)
+    link_error ();
+  if (ceilf (-8.0f) != -8.0f)
+    link_error ();
+
+  if (truncf (3.2f) != 3.0f)
+    link_error ();
+  if (floorf (3.2f) != 3.0f)
+    link_error ();
+  if (ceilf (3.2f) != 4.0f)
+    link_error ();
+
+  if (truncf (-2.8f) != -2.0f)
+    link_error ();
+  if (floorf (-2.8f) != -3.0f)
+    link_error ();
+  if (ceilf (-2.8f) != -2.0f)
+    link_error ();
+
+  if (truncf (0.01f) != 0.0f)
+    link_error ();
+  if (floorf (0.01f) != 0.0f)
+    link_error ();
+  if (ceilf (0.01f) != 1.0f)
+    link_error ();
+
+  if (truncf (-0.7f) != 0.0f)
+    link_error ();
+  if (floorf (-0.7f) != -1.0f)
+    link_error ();
+  if (ceilf (-0.7f) != 0.0f)
+    link_error ();
+}
+
+void testl()
+{
+  if (truncl (0.0l) != 0.0l)
+    link_error ();
+  if (floorl (0.0l) != 0.0l)
+    link_error ();
+  if (ceill (0.0l) != 0.0l)
+    link_error ();
+
+  if (truncl (6.0l) != 6.0l)
+    link_error ();
+  if (floorl (6.0l) != 6.0l)
+    link_error ();
+  if (ceill (6.0l) != 6.0l)
+    link_error ();
+
+  if (truncl (-8.0l) != -8.0l)
+    link_error ();
+  if (floorl (-8.0l) != -8.0l)
+    link_error ();
+  if (ceill (-8.0l) != -8.0l)
+    link_error ();
+
+  if (truncl (3.2l) != 3.0l)
+    link_error ();
+  if (floorl (3.2l) != 3.0l)
+    link_error ();
+  if (ceill (3.2l) != 4.0l)
+    link_error ();
+
+  if (truncl (-2.8l) != -2.0l)
+    link_error ();
+  if (floorl (-2.8l) != -3.0l)
+    link_error ();
+  if (ceill (-2.8l) != -2.0l)
+    link_error ();
+
+  if (truncl (0.01l) != 0.0l)
+    link_error ();
+  if (floorl (0.01l) != 0.0l)
+    link_error ();
+  if (ceill (0.01l) != 1.0l)
+    link_error ();
+
+  if (truncl (-0.7l) != 0.0l)
+    link_error ();
+  if (floorl (-0.7l) != -1.0l)
+    link_error ();
+  if (ceill (-0.7l) != 0.0l)
+    link_error ();
+}
+
+int main()
+{
+  test ();
+  testf ();
+  testl ();
+  return 0;
+}
+
diff --git a/gcc/testsuite/gcc.dg/builtins-26.c b/gcc/testsuite/gcc.dg/builtins-26.c
new file mode 100644 (file)
index 0000000..c4d03cd
--- /dev/null
@@ -0,0 +1,105 @@
+/* Copyright (C) 2003 Free Software Foundation.
+
+   Check that constant folding of built-in math functions doesn't
+   break anything and produces the expected results.
+
+   Written by Roger Sayle, 28th June 2003.  */
+
+/* { dg-do link } */
+/* { dg-options "-O2 -ffast-math" } */
+
+extern void link_error(void);
+
+extern double trunc(double);
+extern double floor(double);
+extern double ceil(double);
+
+extern float truncf(float);
+extern float floorf(float);
+extern float ceilf(float);
+
+extern long double truncl(long double);
+extern long double floorl(long double);
+extern long double ceill(long double);
+
+void test(double x)
+{
+  if (trunc (trunc (x)) != trunc (x))
+    link_error ();
+  if (trunc (floor (x)) != floor (x))
+    link_error ();
+  if (trunc (ceil (x)) != ceil (x))
+    link_error ();
+
+  if (floor (trunc (x)) != trunc (x))
+    link_error ();
+  if (floor (floor (x)) != floor (x))
+    link_error ();
+  if (floor (ceil (x)) != ceil (x))
+    link_error ();
+
+  if (ceil (trunc (x)) != trunc (x))
+    link_error ();
+  if (ceil (floor (x)) != floor (x))
+    link_error ();
+  if (ceil (ceil (x)) != ceil (x))
+    link_error ();
+}
+
+void testf(float x)
+{
+  if (truncf (truncf (x)) != truncf (x))
+    link_error ();
+  if (truncf (floorf (x)) != floorf (x))
+    link_error ();
+  if (truncf (ceilf (x)) != ceilf (x))
+    link_error ();
+
+  if (floorf (truncf (x)) != truncf (x))
+    link_error ();
+  if (floorf (floorf (x)) != floorf (x))
+    link_error ();
+  if (floorf (ceilf (x)) != ceilf (x))
+    link_error ();
+
+  if (ceilf (truncf (x)) != truncf (x))
+    link_error ();
+  if (ceilf (floorf (x)) != floorf (x))
+    link_error ();
+  if (ceilf (ceilf (x)) != ceilf (x))
+    link_error ();
+}
+
+void testl(long double x)
+{
+  if (truncl (truncl (x)) != truncl (x))
+    link_error ();
+  if (truncl (floorl (x)) != floorl (x))
+    link_error ();
+  if (truncl (ceill (x)) != ceill (x))
+    link_error ();
+
+  if (floorl (truncl (x)) != truncl (x))
+    link_error ();
+  if (floorl (floorl (x)) != floorl (x))
+    link_error ();
+  if (floorl (ceill (x)) != ceill (x))
+    link_error ();
+
+  if (ceill (truncl (x)) != truncl (x))
+    link_error ();
+  if (ceill (floorl (x)) != floorl (x))
+    link_error ();
+  if (ceill (ceill (x)) != ceill (x))
+    link_error ();
+}
+
+
+int main()
+{
+  test (3.2);
+  testf (3.2f);
+  testl (3.2l);
+  return 0;
+}
+