public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [patch,libgfortran] Fix binary128 ERFC_SCALED
@ 2013-11-17 21:41 FX
  2013-11-20 20:13 ` Steve Kargl
  0 siblings, 1 reply; 8+ messages in thread
From: FX @ 2013-11-17 21:41 UTC (permalink / raw)
  To: gfortran; +Cc: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 575 bytes --]

This patch fixes libgfortran’s binary128 [aka real(kind=16)] variant of ERFC_SCALED. The original code, which I had lifted from netlib, gives only 18 significant decimal digits, which is not enough for binary128 (33 decimal digits).

I thus implemented a new variant for binary128. For arguments < 12, it simply calls erfcq() then multiplies by expq(x*x). For larger arguments, it uses a power expansion in 1/x. The new implementation provides answers within to 2 ulp of the correct value.

Regtested on x86_64-apple-darwin13, comes with a testcase. OK to commit?
FX


[-- Attachment #2: erfc_scaled.ChangeLog --]
[-- Type: application/octet-stream, Size: 344 bytes --]

2013-11-17  Francois-Xavier Coudert  <fxcoudert@gcc.gnu.org>

	PR libfortran/49024
	* intrinsics/erfc_scaled.c (erfc_scaled_r16): New function.
	* intrinsics/erfc_scaled_inc.c: Do not provide quadruple
	precision variant.


2013-11-17  Francois-Xavier Coudert  <fxcoudert@gcc.gnu.org>

	PR libfortran/49024
	* gfortran.dg/erf_3.F90: New file.


[-- Attachment #3: erfc_scaled.diff --]
[-- Type: application/octet-stream, Size: 3174 bytes --]

Index: libgfortran/intrinsics/erfc_scaled.c
===================================================================
--- libgfortran/intrinsics/erfc_scaled.c	(revision 204446)
+++ libgfortran/intrinsics/erfc_scaled.c	(working copy)
@@ -45,8 +45,55 @@ see the files COPYING3 and COPYING.RUNTI
 #include "erfc_scaled_inc.c"
 #endif
 
-#ifdef HAVE_GFC_REAL_16
+#if defined(HAVE_GFC_REAL_16) && defined(GFC_REAL_16_IS_LONG_DOUBLE)
 #undef KIND
 #define KIND 16
 #include "erfc_scaled_inc.c"
 #endif
+
+
+/* For quadruple-precision (__float128), netlib's implementation is
+   not accurate enough.  We provide another one.  */
+
+
+extern GFC_REAL_16 erfc_scaled_r16 (GFC_REAL_16);
+export_proto(erfc_scaled_r16);
+
+
+GFC_REAL_16
+erfc_scaled_r16 (GFC_REAL_16 x)
+{
+  if (x < 12)
+    {
+      /* Compute directly as ERFC_SCALED(x) = ERFC(x) * EXP(X**2).
+	 This is not perfect, but much better than netlib.  */
+      return erfcq(x) * expq(x*x);
+    }
+  else
+    {
+      /* Calculate ERFC_SCALED(x) using a power series in 1/x:
+	 ERFC_SCALED(x) = 1 / (x * sqrt(pi))
+			 * (1 + Sum_n (-1)**n * (1 * 3 * 5 * ... * (2n-1))
+					      / (2 * x**2)**n)
+       */
+      GFC_REAL_16 sum = 0, oldsum;
+      GFC_REAL_16 inv2x2 = 1 / (2 * x * x);
+      GFC_REAL_16 fac = 1;
+      int n = 1;
+
+      while (n < 200)
+	{
+	  fac *= - (2*n - 1) * inv2x2;
+	  oldsum = sum;
+	  sum += fac;
+
+	  if (sum == oldsum)
+	    break;
+
+	  n++;
+	}
+
+      return (1 + sum) / x * (M_2_SQRTPIq / 2);
+    }
+}
+
Index: libgfortran/intrinsics/erfc_scaled_inc.c
===================================================================
--- libgfortran/intrinsics/erfc_scaled_inc.c	(revision 204446)
+++ libgfortran/intrinsics/erfc_scaled_inc.c	(working copy)
@@ -48,11 +48,6 @@ see the files COPYING3 and COPYING.RUNTI
 #  define TRUNC(x) truncl(x)
 # endif
 
-#elif (KIND == 16 && defined(GFC_REAL_16_IS_FLOAT128))
-
-#  define EXP(x) expq(x)
-#  define TRUNC(x) truncq(x)
-
 #else
 
 # error "What exactly is it that you want me to do?"
Index: gcc/testsuite/gfortran.dg/erf_3.F90
===================================================================
--- gcc/testsuite/gfortran.dg/erf_3.F90	(revision 0)
+++ gcc/testsuite/gfortran.dg/erf_3.F90	(working copy)
@@ -0,0 +1,44 @@
+! { dg-do run }
+! { dg-options "-fno-range-check -ffree-line-length-none -O0" }
+! { dg-add-options ieee }
+!
+! Check that simplification functions and runtime library agree on ERF,
+! ERFC and ERFC_SCALED, for quadruple-precision.
+
+program test
+  implicit none
+
+  real(kind=16) :: x16
+
+#define CHECK(a) \
+  x16 = a ; \
+  call check(erf(real(a,kind=16)), erf(x16)) ; \
+  call check(erfc(real(a,kind=16)), erfc(x16)) ; \
+  call check(erfc_scaled(real(a,kind=16)), erfc_scaled(x16))
+
+  CHECK(0.0)
+  CHECK(0.9)
+  CHECK(1.9)
+  CHECK(10.)
+  CHECK(11.)
+  CHECK(12.)
+  CHECK(13.)
+  CHECK(14.)
+  CHECK(49.)
+  CHECK(190.)
+
+  CHECK(-0.0)
+  CHECK(-0.9)
+  CHECK(-1.9)
+  CHECK(-19.)
+  CHECK(-190.)
+
+contains
+
+  subroutine check (a, b)
+    real(kind=16), intent(in) :: a, b
+    print *, abs(a-b) / spacing(a)
+    if (abs(a - b) > 10 * spacing(a)) call abort
+  end subroutine
+
+end program test

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2013-11-21  9:24 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-11-17 21:41 [patch,libgfortran] Fix binary128 ERFC_SCALED FX
2013-11-20 20:13 ` Steve Kargl
2013-11-20 22:19   ` FX
2013-11-20 23:02     ` Steve Kargl
2013-11-21  1:00       ` FX
2013-11-21 10:32     ` Andreas Schwab
2013-11-21 10:43       ` FX
2013-11-21 11:43         ` Andreas Schwab

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).