From 7d056a378ac5faf9b48b85c8dffef5e85d9f0a88 Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Wed, 22 Dec 2010 16:08:29 +0100 Subject: [PATCH] trunc: Implement result sign according to IEEE 754. * lib/trunc.c (MIN, MINUS_ZERO): New macros. (FUNC): Return +0.0 for 0 < x < 1 and -0.0 for -1 < x < 0. * tests/test-trunc2.c: Include minus-zero.h. (MINUS_ZERO): New macro. (trunc_reference): Keep in sync with lib/trunc.c. * tests/test-truncf2.c: Include minus-zero.h. (MINUS_ZERO): New macro. (truncf_reference): Keep in sync with lib/trunc.c. * tests/test-truncf-ieee.c (main): Test also values between -1 and 1. * tests/test-trunc-ieee.c (main): Likewise. * tests/test-truncl-ieee.c (main): Likewise. --- ChangeLog | 13 +++++++++++++ lib/trunc.c | 24 +++++++++++++++++++++--- tests/test-trunc-ieee.c | 11 +++++++++++ tests/test-trunc2.c | 16 ++++++++++++++-- tests/test-truncf-ieee.c | 11 +++++++++++ tests/test-truncf2.c | 16 ++++++++++++++-- tests/test-truncl-ieee.c | 11 +++++++++++ 7 files changed, 95 insertions(+), 7 deletions(-) diff --git a/ChangeLog b/ChangeLog index 3c1cb7749..b8af66437 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,18 @@ 2010-12-22 Bruno Haible + trunc: Implement result sign according to IEEE 754. + * lib/trunc.c (MIN, MINUS_ZERO): New macros. + (FUNC): Return +0.0 for 0 < x < 1 and -0.0 for -1 < x < 0. + * tests/test-trunc2.c: Include minus-zero.h. + (MINUS_ZERO): New macro. + (trunc_reference): Keep in sync with lib/trunc.c. + * tests/test-truncf2.c: Include minus-zero.h. + (MINUS_ZERO): New macro. + (truncf_reference): Keep in sync with lib/trunc.c. + * tests/test-truncf-ieee.c (main): Test also values between -1 and 1. + * tests/test-trunc-ieee.c (main): Likewise. + * tests/test-truncl-ieee.c (main): Likewise. + ceil: Implement result sign according to IEEE 754. * lib/ceil.c (MIN, MINUS_ZERO): New macros. (FUNC): Return -0.0 for -1 < x < 0. diff --git a/lib/trunc.c b/lib/trunc.c index 31e1e77d2..350459b2e 100644 --- a/lib/trunc.c +++ b/lib/trunc.c @@ -1,5 +1,5 @@ /* Round towards zero. - Copyright (C) 2007, 2009, 2010 Free Software Foundation, Inc. + Copyright (C) 2007, 2010 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -27,19 +27,29 @@ # define FUNC truncl # define DOUBLE long double # define MANT_DIG LDBL_MANT_DIG +# define MIN LDBL_MIN # define L_(literal) literal##L #elif ! defined USE_FLOAT # define FUNC trunc # define DOUBLE double # define MANT_DIG DBL_MANT_DIG +# define MIN DBL_MIN # define L_(literal) literal #else /* defined USE_FLOAT */ # define FUNC truncf # define DOUBLE float # define MANT_DIG FLT_MANT_DIG +# define MIN FLT_MIN # define L_(literal) literal##f #endif +/* -0.0. See minus-zero.h. */ +#if defined __hpux || defined __sgi || defined __ICC +# define MINUS_ZERO (-MIN * MIN) +#else +# define MINUS_ZERO L_(-0.0) +#endif + /* 2^(MANT_DIG-1). */ static const DOUBLE TWO_MANT_DIG = /* Assume MANT_DIG <= 5 * 31. @@ -65,8 +75,12 @@ FUNC (DOUBLE x) if (z > L_(0.0)) { + /* For 0 < x < 1, return +0.0 even if the current rounding mode is + FE_DOWNWARD. */ + if (z < L_(1.0)) + z = L_(0.0); /* Avoid rounding errors for values near 2^k, where k >= MANT_DIG-1. */ - if (z < TWO_MANT_DIG) + else if (z < TWO_MANT_DIG) { /* Round to the next integer (nearest or up or down, doesn't matter). */ z += TWO_MANT_DIG; @@ -78,8 +92,12 @@ FUNC (DOUBLE x) } else if (z < L_(0.0)) { + /* For -1 < x < 0, return -0.0 regardless of the current rounding + mode. */ + if (z > L_(-1.0)) + z = MINUS_ZERO; /* Avoid rounding errors for values near -2^k, where k >= MANT_DIG-1. */ - if (z > - TWO_MANT_DIG) + else if (z > - TWO_MANT_DIG) { /* Round to the next integer (nearest or up or down, doesn't matter). */ z -= TWO_MANT_DIG; diff --git a/tests/test-trunc-ieee.c b/tests/test-trunc-ieee.c index c0fc0f262..0828393e3 100644 --- a/tests/test-trunc-ieee.c +++ b/tests/test-trunc-ieee.c @@ -24,9 +24,20 @@ int main () { + /* See IEEE 754, section 6.3: + "the sign of the result of the round floating-point number to + integral value operation is the sign of the operand. These rules + shall apply even when operands or results are zero or infinite." */ + /* Zero. */ ASSERT (!signbit (trunc (0.0))); ASSERT (!!signbit (trunc (minus_zerod)) == !!signbit (minus_zerod)); + /* Positive numbers. */ + ASSERT (!signbit (trunc (0.3))); + ASSERT (!signbit (trunc (0.7))); + /* Negative numbers. */ + ASSERT (!!signbit (trunc (-0.3)) == !!signbit (minus_zerod)); + ASSERT (!!signbit (trunc (-0.7)) == !!signbit (minus_zerod)); return 0; } diff --git a/tests/test-trunc2.c b/tests/test-trunc2.c index c9f0d8fdd..748c045c7 100644 --- a/tests/test-trunc2.c +++ b/tests/test-trunc2.c @@ -29,6 +29,7 @@ #include #include "isnand-nolibm.h" +#include "minus-zero.h" #include "macros.h" @@ -38,6 +39,9 @@ #define MANT_DIG DBL_MANT_DIG #define L_(literal) literal +/* -0.0. See minus-zero.h. */ +#define MINUS_ZERO minus_zerod + /* 2^(MANT_DIG-1). */ static const DOUBLE TWO_MANT_DIG = /* Assume MANT_DIG <= 5 * 31. @@ -63,8 +67,12 @@ trunc_reference (DOUBLE x) if (z > L_(0.0)) { + /* For 0 < x < 1, return +0.0 even if the current rounding mode is + FE_DOWNWARD. */ + if (z < L_(1.0)) + z = L_(0.0); /* Avoid rounding errors for values near 2^k, where k >= MANT_DIG-1. */ - if (z < TWO_MANT_DIG) + else if (z < TWO_MANT_DIG) { /* Round to the next integer (nearest or up or down, doesn't matter). */ z += TWO_MANT_DIG; @@ -76,8 +84,12 @@ trunc_reference (DOUBLE x) } else if (z < L_(0.0)) { + /* For -1 < x < 0, return -0.0 regardless of the current rounding + mode. */ + if (z > L_(-1.0)) + z = MINUS_ZERO; /* Avoid rounding errors for values near -2^k, where k >= MANT_DIG-1. */ - if (z > - TWO_MANT_DIG) + else if (z > - TWO_MANT_DIG) { /* Round to the next integer (nearest or up or down, doesn't matter). */ z -= TWO_MANT_DIG; diff --git a/tests/test-truncf-ieee.c b/tests/test-truncf-ieee.c index c5c779ae7..76d06079e 100644 --- a/tests/test-truncf-ieee.c +++ b/tests/test-truncf-ieee.c @@ -24,9 +24,20 @@ int main () { + /* See IEEE 754, section 6.3: + "the sign of the result of the round floating-point number to + integral value operation is the sign of the operand. These rules + shall apply even when operands or results are zero or infinite." */ + /* Zero. */ ASSERT (!signbit (truncf (0.0f))); ASSERT (!!signbit (truncf (minus_zerof)) == !!signbit (minus_zerof)); + /* Positive numbers. */ + ASSERT (!signbit (truncf (0.3f))); + ASSERT (!signbit (truncf (0.7f))); + /* Negative numbers. */ + ASSERT (!!signbit (truncf (-0.3f)) == !!signbit (minus_zerof)); + ASSERT (!!signbit (truncf (-0.7f)) == !!signbit (minus_zerof)); return 0; } diff --git a/tests/test-truncf2.c b/tests/test-truncf2.c index f19ed837d..7d4d98f85 100644 --- a/tests/test-truncf2.c +++ b/tests/test-truncf2.c @@ -29,6 +29,7 @@ #include #include "isnanf-nolibm.h" +#include "minus-zero.h" #include "macros.h" @@ -38,6 +39,9 @@ #define MANT_DIG FLT_MANT_DIG #define L_(literal) literal##f +/* -0.0. See minus-zero.h. */ +#define MINUS_ZERO minus_zerof + /* 2^(MANT_DIG-1). */ static const DOUBLE TWO_MANT_DIG = /* Assume MANT_DIG <= 5 * 31. @@ -63,8 +67,12 @@ truncf_reference (DOUBLE x) if (z > L_(0.0)) { + /* For 0 < x < 1, return +0.0 even if the current rounding mode is + FE_DOWNWARD. */ + if (z < L_(1.0)) + z = L_(0.0); /* Avoid rounding errors for values near 2^k, where k >= MANT_DIG-1. */ - if (z < TWO_MANT_DIG) + else if (z < TWO_MANT_DIG) { /* Round to the next integer (nearest or up or down, doesn't matter). */ z += TWO_MANT_DIG; @@ -76,8 +84,12 @@ truncf_reference (DOUBLE x) } else if (z < L_(0.0)) { + /* For -1 < x < 0, return -0.0 regardless of the current rounding + mode. */ + if (z > L_(-1.0)) + z = MINUS_ZERO; /* Avoid rounding errors for values near -2^k, where k >= MANT_DIG-1. */ - if (z > - TWO_MANT_DIG) + else if (z > - TWO_MANT_DIG) { /* Round to the next integer (nearest or up or down, doesn't matter). */ z -= TWO_MANT_DIG; diff --git a/tests/test-truncl-ieee.c b/tests/test-truncl-ieee.c index de6c9adf0..173c1ae1a 100644 --- a/tests/test-truncl-ieee.c +++ b/tests/test-truncl-ieee.c @@ -29,9 +29,20 @@ main () BEGIN_LONG_DOUBLE_ROUNDING (); + /* See IEEE 754, section 6.3: + "the sign of the result of the round floating-point number to + integral value operation is the sign of the operand. These rules + shall apply even when operands or results are zero or infinite." */ + /* Zero. */ ASSERT (!signbit (truncl (0.0L))); ASSERT (!!signbit (truncl (minus_zerol)) == !!signbit (minus_zerol)); + /* Positive numbers. */ + ASSERT (!signbit (truncl (0.3L))); + ASSERT (!signbit (truncl (0.7L))); + /* Negative numbers. */ + ASSERT (!!signbit (truncl (-0.3L)) == !!signbit (minus_zerol)); + ASSERT (!!signbit (truncl (-0.7L)) == !!signbit (minus_zerol)); return 0; } -- 2.11.0