unofficial mirror of guile-devel@gnu.org 
 help / color / mirror / Atom feed
* fix for expt bug
@ 2010-10-31 18:12 Ramakrishnan Muthukrishnan
  2010-11-01  0:03 ` Mark H Weaver
  0 siblings, 1 reply; 24+ messages in thread
From: Ramakrishnan Muthukrishnan @ 2010-10-31 18:12 UTC (permalink / raw)
  To: guile-devel

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

Hi,

As reported here:

<https://savannah.gnu.org/bugs/index.php?31464>

expt currently wrongly prints the results for any negative base
numbers. The attached patch (also attached with the bug tracker) has a
fix and also adds a test case.

This is my first patch to guile, so please correct me if I have done
something wrong. I will be happy to rework it. I am yet to recieve the
assignment papers from the FSF (but they have been despatched already
by the FSF), I will mail them back as soon as I get it.

thanks
-- 
  Ramakrishnan

[-- Attachment #2: expt-fix.patch --]
[-- Type: text/x-patch, Size: 2350 bytes --]

From c23939a1c2b7bfe1b3cf20abb6a7b431699281a1 Mon Sep 17 00:00:00 2001
From: Ramakrishnan Muthukrishnan <vu3rdd@gmail.com>
Date: Sun, 31 Oct 2010 23:22:52 +0530
Subject: [PATCH] Fix for bug #31464. expt needs to treat negative bases specially.
 Also adding test-suite cases for expt.

* libguile/numbers.c: if base is negative, find the absolute value
  of the base, find log and then later reapply the negative sign.
  The bug was caused by the fact that log of a negative number is
  undefined.

* test-suite/tests/numbers.test: Two new test cases for expt. For
  cases where the base is negative and the power to be raised is
  not an integer, the result should be a complex number.
---
 libguile/numbers.c            |   11 +++++++++++
 test-suite/tests/numbers.test |    7 ++++++-
 2 files changed, 17 insertions(+), 1 deletions(-)

diff --git a/libguile/numbers.c b/libguile/numbers.c
index fbc6cc8..07ae95d 100644
--- a/libguile/numbers.c
+++ b/libguile/numbers.c
@@ -5451,6 +5451,17 @@ SCM_DEFINE (scm_expt, "expt", 2, 0, 0,
     {
       return scm_from_double (pow (scm_to_double (x), scm_to_double (y)));
     }
+  else if (scm_is_real (x) &&
+           scm_is_real (y) &&
+           (scm_to_double (x) < 0) &&
+           !SCM_FRACTIONP (y))
+    {
+      SCM x_abs;
+
+      x_abs = scm_abs (x);
+      return scm_product(scm_from_double (-1.0),
+                         scm_exp (scm_product (scm_log (x_abs), y)));
+    }
   else
     return scm_exp (scm_product (scm_log (x), y));
 }
diff --git a/test-suite/tests/numbers.test b/test-suite/tests/numbers.test
index 3c3e14f..3d53bbe 100644
--- a/test-suite/tests/numbers.test
+++ b/test-suite/tests/numbers.test
@@ -2892,7 +2892,12 @@
   (pass-if "(= 1 (expt 0 0))" (= 1 (expt 0 0)))
   (pass-if "(= 1 (expt 0 0.0))" (= 1 (expt 0 0.0)))
   (pass-if "(= 1 (expt 0.0 0))" (= 1 (expt 0.0 0)))
-  (pass-if "(= 1 (expt 0.0 0.0))" (= 1 (expt 0.0 0.0))))
+  (pass-if "(= 1 (expt 0.0 0.0))" (= 1 (expt 0.0 0.0)))
+  ;; tests for non zero values of base and exponent.
+  (pass-if "(eqv-loosely? -2742638075.5 (expt -2742638075.5 1)"
+           (eqv-loosely? -2742638075.5 (expt -2742638075.5 1)))
+  (pass-if "(eqv-loosely? 1.0000000000000002+1.7320508075688772i (expt -8 1/3)"
+           (eqv-loosely? 1.0000000000000002+1.7320508075688772i (expt -8 1/3))))
 
 ;;;
 ;;; asinh
-- 
1.7.2.3


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

* Re: fix for expt bug
  2010-10-31 18:12 fix for expt bug Ramakrishnan Muthukrishnan
@ 2010-11-01  0:03 ` Mark H Weaver
  2010-11-01  4:03   ` Ramakrishnan Muthukrishnan
  0 siblings, 1 reply; 24+ messages in thread
From: Mark H Weaver @ 2010-11-01  0:03 UTC (permalink / raw)
  To: Ramakrishnan Muthukrishnan; +Cc: guile-devel

Ramakrishnan,

Your fix is incorrect.  You have assumed that (-a)^b = -(a^b),
but this is true only if b is an odd integer.  A correct relation
is (-a)^b = (-1)^b * a^b.

As reported by Ludovic:

scheme@(guile-user)> (expt -2742638075.5 2)
$8 = 7.52206361318235e18-1842.31337891184i
scheme@(guile-user)> (* -2742638075.5 -2742638075.5)
$9 = 7.52206361318235e18

The reported bug is simply due to roundoff error.  You must know
something about complex numbers and specifically complex exponentials to
understand what's happening here.  The spurious imaginary part is
actually quite miniscule compared with the real part.  If one looks at
these results in polar form, the complex argument (angle) should ideally
be an exact zero, but is instead an inexact number very close to 0
(about 2.45e-16 radians in this case).

The most straightforward solution to this bug is to support a few
special cases, such as:

  (-a)^b = -(a^b)   (where a is positive real and b is an odd integer)
  (-a)^b =   a^b    (where a is positive real and b is an even integer)

A more elaborate solution would involve supporting a polar
representation of complex numbers where the angle can be an exact
rational times pi, but this is almost certainly not worth it.

    Best,
     Mark


Ramakrishnan Muthukrishnan <vu3rdd@gmail.com> wrote:
> <https://savannah.gnu.org/bugs/index.php?31464>
>
> expt currently wrongly prints the results for any negative base
> numbers. The attached patch (also attached with the bug tracker) has a
> fix and also adds a test case.
>
> This is my first patch to guile, so please correct me if I have done
> something wrong. I will be happy to rework it. I am yet to recieve the
> assignment papers from the FSF (but they have been despatched already
> by the FSF), I will mail them back as soon as I get it.
>
> thanks
> -- 
>   Ramakrishnan
>
> From c23939a1c2b7bfe1b3cf20abb6a7b431699281a1 Mon Sep 17 00:00:00 2001
> From: Ramakrishnan Muthukrishnan <vu3rdd@gmail.com>
> Date: Sun, 31 Oct 2010 23:22:52 +0530
> Subject: [PATCH] Fix for bug #31464. expt needs to treat negative bases specially.
>  Also adding test-suite cases for expt.
>
> * libguile/numbers.c: if base is negative, find the absolute value
>   of the base, find log and then later reapply the negative sign.
>   The bug was caused by the fact that log of a negative number is
>   undefined.
>
> * test-suite/tests/numbers.test: Two new test cases for expt. For
>   cases where the base is negative and the power to be raised is
>   not an integer, the result should be a complex number.
> ---
>  libguile/numbers.c            |   11 +++++++++++
>  test-suite/tests/numbers.test |    7 ++++++-
>  2 files changed, 17 insertions(+), 1 deletions(-)
>
> diff --git a/libguile/numbers.c b/libguile/numbers.c
> index fbc6cc8..07ae95d 100644
> --- a/libguile/numbers.c
> +++ b/libguile/numbers.c
> @@ -5451,6 +5451,17 @@ SCM_DEFINE (scm_expt, "expt", 2, 0, 0,
>      {
>        return scm_from_double (pow (scm_to_double (x), scm_to_double (y)));
>      }
> +  else if (scm_is_real (x) &&
> +           scm_is_real (y) &&
> +           (scm_to_double (x) < 0) &&
> +           !SCM_FRACTIONP (y))
> +    {
> +      SCM x_abs;
> +
> +      x_abs = scm_abs (x);
> +      return scm_product(scm_from_double (-1.0),
> +                         scm_exp (scm_product (scm_log (x_abs), y)));
> +    }
>    else
>      return scm_exp (scm_product (scm_log (x), y));
>  }
> diff --git a/test-suite/tests/numbers.test b/test-suite/tests/numbers.test
> index 3c3e14f..3d53bbe 100644
> --- a/test-suite/tests/numbers.test
> +++ b/test-suite/tests/numbers.test
> @@ -2892,7 +2892,12 @@
>    (pass-if "(= 1 (expt 0 0))" (= 1 (expt 0 0)))
>    (pass-if "(= 1 (expt 0 0.0))" (= 1 (expt 0 0.0)))
>    (pass-if "(= 1 (expt 0.0 0))" (= 1 (expt 0.0 0)))
> -  (pass-if "(= 1 (expt 0.0 0.0))" (= 1 (expt 0.0 0.0))))
> +  (pass-if "(= 1 (expt 0.0 0.0))" (= 1 (expt 0.0 0.0)))
> +  ;; tests for non zero values of base and exponent.
> +  (pass-if "(eqv-loosely? -2742638075.5 (expt -2742638075.5 1)"
> +           (eqv-loosely? -2742638075.5 (expt -2742638075.5 1)))
> +  (pass-if "(eqv-loosely? 1.0000000000000002+1.7320508075688772i (expt -8 1/3)"
> +           (eqv-loosely? 1.0000000000000002+1.7320508075688772i (expt -8 1/3))))
>  
>  ;;;
>  ;;; asinh



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

* Re: fix for expt bug
  2010-11-01  0:03 ` Mark H Weaver
@ 2010-11-01  4:03   ` Ramakrishnan Muthukrishnan
  2010-11-01 18:23     ` Ramakrishnan Muthukrishnan
  0 siblings, 1 reply; 24+ messages in thread
From: Ramakrishnan Muthukrishnan @ 2010-11-01  4:03 UTC (permalink / raw)
  To: Mark H Weaver; +Cc: guile-devel

On Mon, Nov 1, 2010 at 5:33 AM, Mark H Weaver <mhw@netris.org> wrote:
> Ramakrishnan,
>
> Your fix is incorrect.  You have assumed that (-a)^b = -(a^b),

Mark,

Yes, you are right. Not sure what I was thinking when I made the patch. :-(

> The reported bug is simply due to roundoff error.  You must know
> something about complex numbers and specifically complex exponentials to
> understand what's happening here.  The spurious imaginary part is
> actually quite miniscule compared with the real part.  If one looks at
> these results in polar form, the complex argument (angle) should ideally
> be an exact zero, but is instead an inexact number very close to 0
> (about 2.45e-16 radians in this case).
>
> The most straightforward solution to this bug is to support a few
> special cases, such as:
>
>  (-a)^b = -(a^b)   (where a is positive real and b is an odd integer)
>  (-a)^b =   a^b    (where a is positive real and b is an even integer)

Thanks. I will take a dig at doing this solution.

-- 
  Ramakrishnan



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

* Re: fix for expt bug
  2010-11-01  4:03   ` Ramakrishnan Muthukrishnan
@ 2010-11-01 18:23     ` Ramakrishnan Muthukrishnan
  2010-11-02  4:55       ` Mark H Weaver
  0 siblings, 1 reply; 24+ messages in thread
From: Ramakrishnan Muthukrishnan @ 2010-11-01 18:23 UTC (permalink / raw)
  To: guile-devel

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

I reworked the patch a bit as per Mark's comments. It takes care of
the sign part and handles integer power values properly. It also fixes
another bug with the current expt implementation where base is an
integer and the power value is not (eg: (expt 2 2.0) gives an error in
the current git master. The patch fixes this too.)

Also adds some test cases. Please review.

Thanks
Ramakrishnan

[-- Attachment #2: expt-fix.patch --]
[-- Type: text/x-patch, Size: 3282 bytes --]

From 025bde78d4c199dee1d2857e913d69ce4d7c2e59 Mon Sep 17 00:00:00 2001
From: Ramakrishnan Muthukrishnan <vu3rdd@gmail.com>
Date: Sun, 31 Oct 2010 23:22:52 +0530
Subject: [PATCH] Fix for bug #31464. expt needs to treat negative bases specially.

* libguile/numbers.c: If base is negative, expt needs to find
  -x^n = (-1^n) * (|x|^n). We find x^n and then if n is odd, we
  also multiply the result with -1. These changes apply only for
  cases where n is an integer.

* test-suite/tests/numbers.test: Two new test cases for expt. For
  cases where the base is negative and the power to be raised is
  not an integer, the result should be a complex number.
---
 libguile/numbers.c            |   20 +++++++++++++++++++-
 test-suite/tests/numbers.test |   15 ++++++++++++++-
 2 files changed, 33 insertions(+), 2 deletions(-)

diff --git a/libguile/numbers.c b/libguile/numbers.c
index fbc6cc8..5bbf4b0 100644
--- a/libguile/numbers.c
+++ b/libguile/numbers.c
@@ -5445,12 +5445,30 @@ SCM_DEFINE (scm_expt, "expt", 2, 0, 0,
 	    "Return @var{x} raised to the power of @var{y}.") 
 #define FUNC_NAME s_scm_expt
 {
-  if (scm_is_true (scm_exact_p (x)) && scm_is_integer (y))
+  if (scm_is_true (scm_exact_p (x)) &&
+      scm_is_true (scm_exact_p (y)) &&
+      !SCM_FRACTIONP (y))
     return scm_integer_expt (x, y);
   else if (scm_is_real (x) && scm_is_real (y) && scm_to_double (x) >= 0.0)
     {
       return scm_from_double (pow (scm_to_double (x), scm_to_double (y)));
     }
+  else if (scm_is_real (x) &&
+           scm_is_real (y) &&
+           (scm_to_double (x) < 0) &&
+           !SCM_FRACTIONP (y))
+    {
+      SCM x_abs, result;
+
+      x_abs = scm_abs (x);
+      result = scm_from_double (pow (scm_to_double (x_abs), scm_to_double (y)));
+
+      if (scm_is_true (scm_equal_p (y, scm_floor (y))) &&
+          scm_is_true (scm_odd_p (y)))
+        return scm_product (scm_from_double (-1.0), result);
+      else
+        return result;
+    }
   else
     return scm_exp (scm_product (scm_log (x), y));
 }
diff --git a/test-suite/tests/numbers.test b/test-suite/tests/numbers.test
index 3c3e14f..637449f 100644
--- a/test-suite/tests/numbers.test
+++ b/test-suite/tests/numbers.test
@@ -2892,7 +2892,20 @@
   (pass-if "(= 1 (expt 0 0))" (= 1 (expt 0 0)))
   (pass-if "(= 1 (expt 0 0.0))" (= 1 (expt 0 0.0)))
   (pass-if "(= 1 (expt 0.0 0))" (= 1 (expt 0.0 0)))
-  (pass-if "(= 1 (expt 0.0 0.0))" (= 1 (expt 0.0 0.0))))
+  (pass-if "(= 1 (expt 0.0 0.0))" (= 1 (expt 0.0 0.0)))
+  ;; tests for non zero values of base and exponent.
+  (pass-if "(eqv-loosely? -2742638075.5 (expt -2742638075.5 1))"
+            (eqv-loosely? -2742638075.5 (expt -2742638075.5 1)))
+  (pass-if "(eqv-loosely? 4.0 (expt -2.0 2.0))"
+           (eqv-loosely? 4.0 (expt -2.0 2.0)))
+  (pass-if "(eqv-loosely? -0.125 (expt -2.0 -3.0))"
+           (eqv-loosely? -0.125 (expt -2.0 -3.0)))
+  (pass-if "(eqv-loosely? -0.125 (expt -2 -3.0))"
+           (eqv-loosely? -0.125 (expt -2 -3.0)))
+  (pass-if "(eqv-loosely? 0.25 (expt 2.0 -2.0))"
+           (eqv-loosely? 0.25 (expt 2.0 -2.0)))
+  (pass-if "(eqv-loosely? 1.0000000000000002+1.7320508075688772i (expt -8 1/3))"
+            (eqv-loosely? 1.0000000000000002+1.7320508075688772i (expt -8 1/3))))
 
 ;;;
 ;;; asinh
-- 
1.7.2.3


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

* Re: fix for expt bug
  2010-11-01 18:23     ` Ramakrishnan Muthukrishnan
@ 2010-11-02  4:55       ` Mark H Weaver
  2010-11-02  5:22         ` Ramakrishnan Muthukrishnan
  0 siblings, 1 reply; 24+ messages in thread
From: Mark H Weaver @ 2010-11-02  4:55 UTC (permalink / raw)
  To: Ramakrishnan Muthukrishnan; +Cc: guile-devel

Hi Ramakrishnan,

Thanks for the revised patch.  There are still some problems:

> diff --git a/libguile/numbers.c b/libguile/numbers.c
> index fbc6cc8..5bbf4b0 100644
> --- a/libguile/numbers.c
> +++ b/libguile/numbers.c
> @@ -5445,12 +5445,30 @@ SCM_DEFINE (scm_expt, "expt", 2, 0, 0,
>  	    "Return @var{x} raised to the power of @var{y}.") 
>  #define FUNC_NAME s_scm_expt
>  {
> -  if (scm_is_true (scm_exact_p (x)) && scm_is_integer (y))
> +  if (scm_is_true (scm_exact_p (x)) &&
> +      scm_is_true (scm_exact_p (y)) &&
> +      !SCM_FRACTIONP (y))
>      return scm_integer_expt (x, y);

This is an improvement, but isn't quite right.  scm_integer_expt
requires that y be an exact integer, but x is allowed to be any scheme
number whatsoever.  So the "scm_exact_p (x)" doesn't belong.  It should
simply be changed to "scm_exact_p (y)" instead.

The other problem is that !SCM_FRACTIONP is not the right test.
Although it is currently true that the only exact numbers in guile are
integers and rationals, there's no guarantee that other exact numbers
won't be added in the future.

The test above should be this:

    if (scm_is_true (scm_exact_p (y)) && scm_is_integer (y))

>    else if (scm_is_real (x) && scm_is_real (y) && scm_to_double (x) >= 0.0)
>      {
>        return scm_from_double (pow (scm_to_double (x), scm_to_double (y)));
>      }
> +  else if (scm_is_real (x) &&
> +           scm_is_real (y) &&
> +           (scm_to_double (x) < 0) &&
> +           !SCM_FRACTIONP (y))

The !SCM_FRACTIONP (y) does not belong here.  Instead, you should test
scm_is_integer (y), which will also make the scm_is_real (y) test
redundant.  As is, your code will produce the wrong answer if y is an
inexact floating-point number such as 0.5.  SCM_FRACTIONP returns true
only for exact rational numbers.

So the above test should be:

  else if (scm_is_real (x) && scm_is_integer (y) && scm_to_double (x) < 0)

> +    {
> +      SCM x_abs, result;
> +
> +      x_abs = scm_abs (x);

You've already established that x is a negative real, so it is wasteful
to do another test in scm_abs.  Just negate x.

Also, it's inefficient to create so many intermediate floating-point SCM
values, since it involves allocating heap cells.  You don't need x_abs,
and "result" can be a C double.  See my suggested code below.

> +      result = scm_from_double (pow (scm_to_double (x_abs), scm_to_double (y)));
> +
> +      if (scm_is_true (scm_equal_p (y, scm_floor (y))) &&

The test above is better replaced by scm_is_integer (y), but it's needed
in the outer conditional anyway, so you can drop it here.

> +          scm_is_true (scm_odd_p (y)))
> +        return scm_product (scm_from_double (-1.0), result);
> +      else
> +        return result;
> +    }

In summary, I think the new clause should be something like: (UNTESTED!)

  else if (scm_is_real (x) && scm_is_integer (y) && scm_to_double (x) < 0)
  {
    double result;

    result = pow (-scm_to_double (x), scm_to_double (y));
    if (scm_is_true (scm_odd_p (y)))
      result = -result;
    return scm_from_double (result);
  }

     Best,
      Mark



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

* Re: fix for expt bug
  2010-11-02  4:55       ` Mark H Weaver
@ 2010-11-02  5:22         ` Ramakrishnan Muthukrishnan
  2010-11-02  5:36           ` Ramakrishnan Muthukrishnan
  0 siblings, 1 reply; 24+ messages in thread
From: Ramakrishnan Muthukrishnan @ 2010-11-02  5:22 UTC (permalink / raw)
  To: Mark H Weaver; +Cc: guile-devel

On Tue, Nov 2, 2010 at 10:25 AM, Mark H Weaver <mhw@netris.org> wrote:
> Hi Ramakrishnan,
>
> Thanks for the revised patch.  There are still some problems:
> [...]
>
> This is an improvement, but isn't quite right.  scm_integer_expt
> requires that y be an exact integer, but x is allowed to be any scheme
> number whatsoever.  So the "scm_exact_p (x)" doesn't belong.  It should
> simply be changed to "scm_exact_p (y)" instead.
>
> The other problem is that !SCM_FRACTIONP is not the right test.
> Although it is currently true that the only exact numbers in guile are
> integers and rationals, there's no guarantee that other exact numbers
> won't be added in the future.
>
> The test above should be this:
>
>    if (scm_is_true (scm_exact_p (y)) && scm_is_integer (y))

Thanks Mark, for the comments. I am learning a lot. I will make the
changes and test them.

-- 
  Ramakrishnan



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

* Re: fix for expt bug
  2010-11-02  5:22         ` Ramakrishnan Muthukrishnan
@ 2010-11-02  5:36           ` Ramakrishnan Muthukrishnan
  2010-11-02 19:19             ` Mark H Weaver
                               ` (2 more replies)
  0 siblings, 3 replies; 24+ messages in thread
From: Ramakrishnan Muthukrishnan @ 2010-11-02  5:36 UTC (permalink / raw)
  To: guile-devel

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

Here is the updated patch.

thanks
Ramakrishnan

[-- Attachment #2: expt-fix.patch --]
[-- Type: text/x-patch, Size: 3066 bytes --]

From e320d79c8f3cd8b7ddcb9c2d13356e34a3346cfe Mon Sep 17 00:00:00 2001
From: Ramakrishnan Muthukrishnan <vu3rdd@gmail.com>
Date: Sun, 31 Oct 2010 23:22:52 +0530
Subject: [PATCH] Fix for bug #31464. expt needs to treat negative bases specially.

* libguile/numbers.c: If base is negative, expt needs to find
  -x^n = (-1^n) * (|x|^n). We find x^n and then if n is odd, we
  also multiply the result with -1. These changes apply only for
  cases where n is an integer.

* test-suite/tests/numbers.test: Two new test cases for expt. For
  cases where the base is negative and the power to be raised is
  not an integer, the result should be a complex number.
---
 libguile/numbers.c            |   13 ++++++++++++-
 test-suite/tests/numbers.test |   15 ++++++++++++++-
 2 files changed, 26 insertions(+), 2 deletions(-)

diff --git a/libguile/numbers.c b/libguile/numbers.c
index fbc6cc8..4b81d98 100644
--- a/libguile/numbers.c
+++ b/libguile/numbers.c
@@ -5445,12 +5445,23 @@ SCM_DEFINE (scm_expt, "expt", 2, 0, 0,
 	    "Return @var{x} raised to the power of @var{y}.") 
 #define FUNC_NAME s_scm_expt
 {
-  if (scm_is_true (scm_exact_p (x)) && scm_is_integer (y))
+  if (scm_is_true (scm_exact_p (y)) && scm_is_integer (y))
     return scm_integer_expt (x, y);
   else if (scm_is_real (x) && scm_is_real (y) && scm_to_double (x) >= 0.0)
     {
       return scm_from_double (pow (scm_to_double (x), scm_to_double (y)));
     }
+  else if (scm_is_real (x) && scm_is_integer (y) && (scm_to_double (x) < 0))
+    {
+      double result;
+      
+      result = pow (-scm_to_double (x), scm_to_double (y));
+
+      if (scm_is_true (scm_odd_p (y)))
+        return scm_from_double (-result);
+      else
+        return scm_from_double (result);
+    }
   else
     return scm_exp (scm_product (scm_log (x), y));
 }
diff --git a/test-suite/tests/numbers.test b/test-suite/tests/numbers.test
index 3c3e14f..637449f 100644
--- a/test-suite/tests/numbers.test
+++ b/test-suite/tests/numbers.test
@@ -2892,7 +2892,20 @@
   (pass-if "(= 1 (expt 0 0))" (= 1 (expt 0 0)))
   (pass-if "(= 1 (expt 0 0.0))" (= 1 (expt 0 0.0)))
   (pass-if "(= 1 (expt 0.0 0))" (= 1 (expt 0.0 0)))
-  (pass-if "(= 1 (expt 0.0 0.0))" (= 1 (expt 0.0 0.0))))
+  (pass-if "(= 1 (expt 0.0 0.0))" (= 1 (expt 0.0 0.0)))
+  ;; tests for non zero values of base and exponent.
+  (pass-if "(eqv-loosely? -2742638075.5 (expt -2742638075.5 1))"
+            (eqv-loosely? -2742638075.5 (expt -2742638075.5 1)))
+  (pass-if "(eqv-loosely? 4.0 (expt -2.0 2.0))"
+           (eqv-loosely? 4.0 (expt -2.0 2.0)))
+  (pass-if "(eqv-loosely? -0.125 (expt -2.0 -3.0))"
+           (eqv-loosely? -0.125 (expt -2.0 -3.0)))
+  (pass-if "(eqv-loosely? -0.125 (expt -2 -3.0))"
+           (eqv-loosely? -0.125 (expt -2 -3.0)))
+  (pass-if "(eqv-loosely? 0.25 (expt 2.0 -2.0))"
+           (eqv-loosely? 0.25 (expt 2.0 -2.0)))
+  (pass-if "(eqv-loosely? 1.0000000000000002+1.7320508075688772i (expt -8 1/3))"
+            (eqv-loosely? 1.0000000000000002+1.7320508075688772i (expt -8 1/3))))
 
 ;;;
 ;;; asinh
-- 
1.7.2.3


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

* Re: fix for expt bug
  2010-11-02  5:36           ` Ramakrishnan Muthukrishnan
@ 2010-11-02 19:19             ` Mark H Weaver
  2010-11-02 23:00             ` Ludovic Courtès
  2010-11-03  2:10             ` Mark H Weaver
  2 siblings, 0 replies; 24+ messages in thread
From: Mark H Weaver @ 2010-11-02 19:19 UTC (permalink / raw)
  To: Ramakrishnan Muthukrishnan; +Cc: guile-devel

Hi Ramakrishnan,

The code in your latest patch looks good to me, though the commit
message has some problems, and I'd add more test cases:

> * libguile/numbers.c: If base is negative, expt needs to find
>   -x^n = (-1^n) * (|x|^n). We find x^n and then if n is odd, we
>   also multiply the result with -1. These changes apply only for
>   cases where n is an integer.

In the equation above, -x^n parses as -(x^n), but it should be (-x)^n.
Same problem with (-1^n).  Also, the absolute value should be removed.
It is superfluous for the case you are handling (x>0), and for other
cases it is erroneous.

The equation above should be:  (-x)^n = (-1)^n * x^n

Also, I would add a couple more test cases:

* Test the case from Ludovic's original bug report:
  (expt -2742638075.5 2) should loosely equal
  7.52206361318235e18

* Test that (expt -1 0.5) is loosely equal to +i.
  (I believe this would have failed with your second patch)

    Thanks,
      Mark



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

* Re: fix for expt bug
  2010-11-02  5:36           ` Ramakrishnan Muthukrishnan
  2010-11-02 19:19             ` Mark H Weaver
@ 2010-11-02 23:00             ` Ludovic Courtès
  2010-11-03  2:10             ` Mark H Weaver
  2 siblings, 0 replies; 24+ messages in thread
From: Ludovic Courtès @ 2010-11-02 23:00 UTC (permalink / raw)
  To: guile-devel

Hello!

Thank you Ramakrishnan for the patch, and thanks Mark for the detailed
review!  It looks like we’re getting close.  :-)

In addition to Mark’s latest comments, just a few cosmetic notes:

Ramakrishnan Muthukrishnan <vu3rdd@gmail.com> writes:

> * libguile/numbers.c: If base is negative, expt needs to find
>   -x^n = (-1^n) * (|x|^n). We find x^n and then if n is odd, we
>   also multiply the result with -1. These changes apply only for
>   cases where n is an integer.

Please follow GNU change log conventions, i.e., describe the changes,
not the rationale (info "(standards) Change Logs").  Provide appropriate
explanations in ‘scm_expt’, though.

> * test-suite/tests/numbers.test: Two new test cases for expt. For
>   cases where the base is negative and the power to be raised is
>   not an integer, the result should be a complex number.

Ditto: list the new test case names.

> +  (pass-if "(eqv-loosely? -2742638075.5 (expt -2742638075.5 1))"
> +            (eqv-loosely? -2742638075.5 (expt -2742638075.5 1)))

The first argument to ‘pass-if’ can be omitted altogether.

Thanks,
Ludo’.




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

* Re: fix for expt bug
  2010-11-02  5:36           ` Ramakrishnan Muthukrishnan
  2010-11-02 19:19             ` Mark H Weaver
  2010-11-02 23:00             ` Ludovic Courtès
@ 2010-11-03  2:10             ` Mark H Weaver
  2010-11-03  9:41               ` Ramakrishnan Muthukrishnan
  2 siblings, 1 reply; 24+ messages in thread
From: Mark H Weaver @ 2010-11-03  2:10 UTC (permalink / raw)
  To: Ramakrishnan Muthukrishnan; +Cc: guile-devel

Hi Ramakrishnan,

I noticed one more problem with your commit message.  It should mention
the other included bug fix, for when the exponent is an inexact integer,
e.g. (expt 2 2.0).

   Thanks,
     Mark



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

* Re: fix for expt bug
  2010-11-03  2:10             ` Mark H Weaver
@ 2010-11-03  9:41               ` Ramakrishnan Muthukrishnan
  2010-11-03 15:32                 ` Mark H Weaver
  2010-11-03 16:22                 ` Mark H Weaver
  0 siblings, 2 replies; 24+ messages in thread
From: Ramakrishnan Muthukrishnan @ 2010-11-03  9:41 UTC (permalink / raw)
  To: guile-devel

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

Thanks again, Mark and Ludovic.

Attached is an updated patch.

thanks
-- 
  Ramakrishnan

[-- Attachment #2: expt-fix.patch --]
[-- Type: text/x-patch, Size: 3100 bytes --]

From a1dd2da8562ddeb2052f2994ad0302bcc8d5d1a2 Mon Sep 17 00:00:00 2001
From: Ramakrishnan Muthukrishnan <vu3rdd@gmail.com>
Date: Sun, 31 Oct 2010 23:22:52 +0530
Subject: [PATCH] Adding a case for `expt' when base is negative.

* libguile/numbers.c (scm_expt): Add a case when base is a
  negative number. Also fix the bug for the case when exponent
  is an inexact number.

* test-suite/tests/numbers.test ("expt"): Add tests for the cases
  when base is an integer and exponent is an inexact number, base
  is an integer and exponent is a rational number, base and
  exponent are both inexact and so on.
---
 libguile/numbers.c            |   18 +++++++++++++++++-
 test-suite/tests/numbers.test |   13 ++++++++++++-
 2 files changed, 29 insertions(+), 2 deletions(-)

diff --git a/libguile/numbers.c b/libguile/numbers.c
index fbc6cc8..e4b03aa 100644
--- a/libguile/numbers.c
+++ b/libguile/numbers.c
@@ -5445,12 +5445,28 @@ SCM_DEFINE (scm_expt, "expt", 2, 0, 0,
 	    "Return @var{x} raised to the power of @var{y}.") 
 #define FUNC_NAME s_scm_expt
 {
-  if (scm_is_true (scm_exact_p (x)) && scm_is_integer (y))
+  if (scm_is_true (scm_exact_p (y)) && scm_is_integer (y))
     return scm_integer_expt (x, y);
   else if (scm_is_real (x) && scm_is_real (y) && scm_to_double (x) >= 0.0)
     {
       return scm_from_double (pow (scm_to_double (x), scm_to_double (y)));
     }
+  else if (scm_is_real (x) && scm_is_integer (y) && (scm_to_double (x) < 0))
+    {
+      double result;
+
+      /* If base is negative, expt needs to find -x^n = (-1^n) * (x^n).
+         We find x^n and then if n is odd, we also multiply the result
+         with -1. These changes apply only for cases where n is an
+         integer.
+      */
+      result = pow (-scm_to_double (x), scm_to_double (y));
+
+      if (scm_is_true (scm_odd_p (y)))
+        return scm_from_double (-result);
+      else
+        return scm_from_double (result);
+    }
   else
     return scm_exp (scm_product (scm_log (x), y));
 }
diff --git a/test-suite/tests/numbers.test b/test-suite/tests/numbers.test
index 3c3e14f..97c58c9 100644
--- a/test-suite/tests/numbers.test
+++ b/test-suite/tests/numbers.test
@@ -2892,7 +2892,18 @@
   (pass-if "(= 1 (expt 0 0))" (= 1 (expt 0 0)))
   (pass-if "(= 1 (expt 0 0.0))" (= 1 (expt 0 0.0)))
   (pass-if "(= 1 (expt 0.0 0))" (= 1 (expt 0.0 0)))
-  (pass-if "(= 1 (expt 0.0 0.0))" (= 1 (expt 0.0 0.0))))
+  (pass-if "(= 1 (expt 0.0 0.0))" (= 1 (expt 0.0 0.0)))
+  ;; tests for non zero values of base and exponent.
+  (pass-if (eqv-loosely? -2742638075.5 (expt -2742638075.5 1)))
+  (pass-if (eqv-loosely? (* -2742638075.5 -2742638075.5)
+                         (expt -2742638075.5 2)))
+  (pass-if (eqv-loosely? 4.0 (expt -2.0 2.0)))
+  (pass-if (eqv-loosely? -0.125 (expt -2.0 -3.0)))
+  (pass-if (eqv-loosely? -0.125 (expt -2 -3.0)))
+  (pass-if (eqv-loosely? 0.25 (expt 2.0 -2.0)))
+  (pass-if (eqv-loosely? +i (expt -1 0.5)))
+  (pass-if (eqv-loosely? +i (expt -1 1/2)))
+  (pass-if (eqv-loosely? 1.0000000000000002+1.7320508075688772i (expt -8 1/3))))
 
 ;;;
 ;;; asinh
-- 
1.7.2.3


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

* Re: fix for expt bug
  2010-11-03  9:41               ` Ramakrishnan Muthukrishnan
@ 2010-11-03 15:32                 ` Mark H Weaver
  2010-11-03 16:41                   ` Ramakrishnan Muthukrishnan
  2010-11-03 16:22                 ` Mark H Weaver
  1 sibling, 1 reply; 24+ messages in thread
From: Mark H Weaver @ 2010-11-03 15:32 UTC (permalink / raw)
  To: Ramakrishnan Muthukrishnan; +Cc: guile-devel

Hi Ramakrishnan,

We're almost there, but you neglected one of the comments I made about
your previous patch.

> +      /* If base is negative, expt needs to find -x^n = (-1^n) * (x^n).
> +         We find x^n and then if n is odd, we also multiply the result
> +         with -1. These changes apply only for cases where n is an
> +         integer.
> +      */

I repeat: In the equation above, -x^n parses as -(x^n), but it should be
(-x)^n.  Same problem with (-1^n).  Exponentiation has higher precedence
than negation.

The equation above should be:  (-x)^n = (-1)^n * x^n

Feel free to add more parentheses if you like, but the ones I have
included are essential and must not be removed.

Everything else looks good to me.

    Thanks,
      Mark



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

* Re: fix for expt bug
  2010-11-03  9:41               ` Ramakrishnan Muthukrishnan
  2010-11-03 15:32                 ` Mark H Weaver
@ 2010-11-03 16:22                 ` Mark H Weaver
  2010-11-03 17:53                   ` Ramakrishnan Muthukrishnan
  2010-11-03 23:27                   ` Ludovic Courtès
  1 sibling, 2 replies; 24+ messages in thread
From: Mark H Weaver @ 2010-11-03 16:22 UTC (permalink / raw)
  To: Ramakrishnan Muthukrishnan; +Cc: guile-devel

Ramakrishnan and others,

I just realized that there is a better way to fix these bugs.  We don't
need a new top-level case in expt after all.  Instead, we generalize the
scm_integer_expt case to support inexact integer exponents.

Within that case, if the exponent is an inexact integer, then we make it
exact and make the base inexact, and then call scm_integer_expt.

I think this strategy is better for these reasons:

* Fewer top-level cases to check, thus making expt faster.
* More precise answers in the case of inexact integer exponents.
* More cases handled without spurious imaginary parts turning up, for
  example (expt +234234234.5i 4.0)
* Simpler code

Something like the code below.  (WARNING: UNTESTED!)

What do you all think?  Can you see any problems with this strategy?

    Best,
     Mark


index fbc6cc8..f38772e 100644
--- a/libguile/numbers.c
+++ b/libguile/numbers.c
@@ -5445,8 +5445,14 @@ SCM_DEFINE (scm_expt, "expt", 2, 0, 0,
 	    "Return @var{x} raised to the power of @var{y}.") 
 #define FUNC_NAME s_scm_expt
 {
-  if (scm_is_true (scm_exact_p (x)) && scm_is_integer (y))
-    return scm_integer_expt (x, y);
+  if (scm_is_integer (y))
+    {
+      if (scm_is_true (scm_exact_p (y)))
+	return scm_integer_expt (x, y);
+      else
+	return scm_integer_expt (scm_exact_to_inexact(x),
+				 scm_inexact_to_exact(y));
+    }
   else if (scm_is_real (x) && scm_is_real (y) && scm_to_double (x) >= 0.0)
     {
       return scm_from_double (pow (scm_to_double (x), scm_to_double (y)));



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

* Re: fix for expt bug
  2010-11-03 15:32                 ` Mark H Weaver
@ 2010-11-03 16:41                   ` Ramakrishnan Muthukrishnan
  0 siblings, 0 replies; 24+ messages in thread
From: Ramakrishnan Muthukrishnan @ 2010-11-03 16:41 UTC (permalink / raw)
  To: Mark H Weaver; +Cc: guile-devel

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

On Wed, Nov 3, 2010 at 9:02 PM, Mark H Weaver <mhw@netris.org> wrote:
> Hi Ramakrishnan,
>
> We're almost there, but you neglected one of the comments I made about
> your previous patch.

Sorry, I should pay more attention. :-(

Attaching the modified patch.
-- 
  Ramakrishnan

[-- Attachment #2: expt-fix.patch --]
[-- Type: text/x-patch, Size: 3102 bytes --]

From 6cca8a66a3daedb551f4f80170966d74b6143ba6 Mon Sep 17 00:00:00 2001
From: Ramakrishnan Muthukrishnan <vu3rdd@gmail.com>
Date: Sun, 31 Oct 2010 23:22:52 +0530
Subject: [PATCH] Adding a case for `expt' when base is negative.

* libguile/numbers.c (scm_expt): Add a case when base is a
  negative number. Also fix the bug for the case when exponent
  is an inexact number.

* test-suite/tests/numbers.test ("expt"): Add tests for the cases
  when base is an integer and exponent is an inexact number, base
  is an integer and exponent is a rational number, base and
  exponent are both inexact and so on.
---
 libguile/numbers.c            |   18 +++++++++++++++++-
 test-suite/tests/numbers.test |   13 ++++++++++++-
 2 files changed, 29 insertions(+), 2 deletions(-)

diff --git a/libguile/numbers.c b/libguile/numbers.c
index fbc6cc8..f07f53d 100644
--- a/libguile/numbers.c
+++ b/libguile/numbers.c
@@ -5445,12 +5445,28 @@ SCM_DEFINE (scm_expt, "expt", 2, 0, 0,
 	    "Return @var{x} raised to the power of @var{y}.") 
 #define FUNC_NAME s_scm_expt
 {
-  if (scm_is_true (scm_exact_p (x)) && scm_is_integer (y))
+  if (scm_is_true (scm_exact_p (y)) && scm_is_integer (y))
     return scm_integer_expt (x, y);
   else if (scm_is_real (x) && scm_is_real (y) && scm_to_double (x) >= 0.0)
     {
       return scm_from_double (pow (scm_to_double (x), scm_to_double (y)));
     }
+  else if (scm_is_real (x) && scm_is_integer (y) && (scm_to_double (x) < 0))
+    {
+      double result;
+
+      /* If base is negative, expt needs to find (-x)^n = (-1^n) * (x^n).
+         We find x^n and then if n is odd, we also multiply the result
+         with -1. These changes apply only for cases where n is an
+         integer.
+      */
+      result = pow (-scm_to_double (x), scm_to_double (y));
+
+      if (scm_is_true (scm_odd_p (y)))
+        return scm_from_double (-result);
+      else
+        return scm_from_double (result);
+    }
   else
     return scm_exp (scm_product (scm_log (x), y));
 }
diff --git a/test-suite/tests/numbers.test b/test-suite/tests/numbers.test
index 3c3e14f..97c58c9 100644
--- a/test-suite/tests/numbers.test
+++ b/test-suite/tests/numbers.test
@@ -2892,7 +2892,18 @@
   (pass-if "(= 1 (expt 0 0))" (= 1 (expt 0 0)))
   (pass-if "(= 1 (expt 0 0.0))" (= 1 (expt 0 0.0)))
   (pass-if "(= 1 (expt 0.0 0))" (= 1 (expt 0.0 0)))
-  (pass-if "(= 1 (expt 0.0 0.0))" (= 1 (expt 0.0 0.0))))
+  (pass-if "(= 1 (expt 0.0 0.0))" (= 1 (expt 0.0 0.0)))
+  ;; tests for non zero values of base and exponent.
+  (pass-if (eqv-loosely? -2742638075.5 (expt -2742638075.5 1)))
+  (pass-if (eqv-loosely? (* -2742638075.5 -2742638075.5)
+                         (expt -2742638075.5 2)))
+  (pass-if (eqv-loosely? 4.0 (expt -2.0 2.0)))
+  (pass-if (eqv-loosely? -0.125 (expt -2.0 -3.0)))
+  (pass-if (eqv-loosely? -0.125 (expt -2 -3.0)))
+  (pass-if (eqv-loosely? 0.25 (expt 2.0 -2.0)))
+  (pass-if (eqv-loosely? +i (expt -1 0.5)))
+  (pass-if (eqv-loosely? +i (expt -1 1/2)))
+  (pass-if (eqv-loosely? 1.0000000000000002+1.7320508075688772i (expt -8 1/3))))
 
 ;;;
 ;;; asinh
-- 
1.7.2.3


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

* Re: fix for expt bug
  2010-11-03 16:22                 ` Mark H Weaver
@ 2010-11-03 17:53                   ` Ramakrishnan Muthukrishnan
  2010-11-03 18:25                     ` Ramakrishnan Muthukrishnan
  2010-11-03 23:27                   ` Ludovic Courtès
  1 sibling, 1 reply; 24+ messages in thread
From: Ramakrishnan Muthukrishnan @ 2010-11-03 17:53 UTC (permalink / raw)
  To: Mark H Weaver; +Cc: guile-devel

On Wed, Nov 3, 2010 at 9:52 PM, Mark H Weaver <mhw@netris.org> wrote:
> Ramakrishnan and others,
>
> I just realized that there is a better way to fix these bugs.  We don't
> need a new top-level case in expt after all.  Instead, we generalize the
> scm_integer_expt case to support inexact integer exponents.
>
> Within that case, if the exponent is an inexact integer, then we make it
> exact and make the base inexact, and then call scm_integer_expt.

Mark,

Why do we need to convert the base to inexact? is there any problem if
they are just as it is and we convert only the exponent to exact when
they are exact?

-- 
  Ramakrishnan



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

* Re: fix for expt bug
  2010-11-03 17:53                   ` Ramakrishnan Muthukrishnan
@ 2010-11-03 18:25                     ` Ramakrishnan Muthukrishnan
  0 siblings, 0 replies; 24+ messages in thread
From: Ramakrishnan Muthukrishnan @ 2010-11-03 18:25 UTC (permalink / raw)
  To: Mark H Weaver; +Cc: guile-devel

On Wed, Nov 3, 2010 at 11:23 PM, Ramakrishnan Muthukrishnan
<vu3rdd@gmail.com> wrote:
> On Wed, Nov 3, 2010 at 9:52 PM, Mark H Weaver <mhw@netris.org> wrote:
>> Ramakrishnan and others,
>>
>> I just realized that there is a better way to fix these bugs.  We don't
>> need a new top-level case in expt after all.  Instead, we generalize the
>> scm_integer_expt case to support inexact integer exponents.
>>
>> Within that case, if the exponent is an inexact integer, then we make it
>> exact and make the base inexact, and then call scm_integer_expt.
>
> Mark,
>
> Why do we need to convert the base to inexact? is there any problem if
> they are just as it is and we convert only the exponent to exact when
> they are exact?

scheme@(guile-user)> (integer-expt (exact->inexact 3/2) (inexact->exact 4.0))
$17 = 5.0625
scheme@(guile-user)> (integer-expt 3/2 (inexact->exact 4.0))
$18 = 81/16
scheme@(guile-user)> (integer-expt (exact->inexact 3/2) (inexact->exact 4.0))
$19 = 5.0625

We want an output representation in an inexact form. For that reason,
we would want to do exact to inexact conversion of base. Is that
correct?

Mark, I guess your patch solves the problem in a much more efficient way.

thanks
-- 
  Ramakrishnan



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

* Re: fix for expt bug
  2010-11-03 16:22                 ` Mark H Weaver
  2010-11-03 17:53                   ` Ramakrishnan Muthukrishnan
@ 2010-11-03 23:27                   ` Ludovic Courtès
  2010-11-04 17:27                     ` Mark H Weaver
  2010-11-05  2:18                     ` [PATCH] Fix bugs in expt and integer-expt Mark H Weaver
  1 sibling, 2 replies; 24+ messages in thread
From: Ludovic Courtès @ 2010-11-03 23:27 UTC (permalink / raw)
  To: guile-devel

Hi Mark,

Mark H Weaver <mhw@netris.org> writes:

> I just realized that there is a better way to fix these bugs.  We don't
> need a new top-level case in expt after all.  Instead, we generalize the
> scm_integer_expt case to support inexact integer exponents.

You mean “inexact number”, right?

The doc for ‘integer-expt’ makes the same mistake:

  "Return @var{n} raised to the power @var{k}.  @var{k} must be an\n"
  "exact integer, @var{n} can be any number.\n"

However, an integer is necessarily an exact number, whereas the converse
isn’t true:

--8<---------------cut here---------------start------------->8---
scheme@(guile-user) [25]> (exact? 2/3)
$4 = #t
scheme@(guile-user) [25]> (integer? 2/3)
$5 = #f
--8<---------------cut here---------------end--------------->8---

In actuality, ‘integer-expt’ expects K to be an integer (fixnum or
bignum).

So I suspect that your patch doesn’t work because ‘inexact->exact’
returns something that’s obviously not necessarily an integer:

--8<---------------cut here---------------start------------->8---
scheme@(guile-user) [25]> (inexact->exact 2.3)
$6 = 2589569785738035/1125899906842624
--8<---------------cut here---------------end--------------->8---

Does that make sense?

Thanks,
Ludo’.




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

* Re: fix for expt bug
  2010-11-03 23:27                   ` Ludovic Courtès
@ 2010-11-04 17:27                     ` Mark H Weaver
  2010-11-08 21:08                       ` Ludovic Courtès
  2010-11-05  2:18                     ` [PATCH] Fix bugs in expt and integer-expt Mark H Weaver
  1 sibling, 1 reply; 24+ messages in thread
From: Mark H Weaver @ 2010-11-04 17:27 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: guile-devel

ludo@gnu.org (Ludovic Courtès) writes:
>> I just realized that there is a better way to fix these bugs.  We don't
>> need a new top-level case in expt after all.  Instead, we generalize the
>> scm_integer_expt case to support inexact integer exponents.
>
> You mean “inexact number”, right?

No, I meant "inexact integer", for example 3.0.

> However, an integer is necessarily an exact number,

No, (integer? 3.0) returns #t, as it should, according to R5RS.
R5RS's description of "integer?" gives this precise example, and
guile's implementation agrees.

> So I suspect that your patch doesn’t work because ‘inexact->exact’
> returns something that’s obviously not necessarily an integer:

I'm only talking about inexact integers such as 3.0, so that

  (expt 2 3.0) ==> (integer-expt 2.0 3) ==> 8.0

However, I have since realized that it is not enough to convert the base
to inexact; I must also convert integer-expt's result to inexact,
because there is one case where making the base inexact is not enough:

  (expt 3 0.0) ==> (integer-expt 3.0 0) ==> 1

Though (expt 3 0.0) should reduce to 1.0.  So the code needs to coerce
the result of integer-expt to inexact.

I am working on a patch to fix these and some other problems.

    Mark



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

* [PATCH] Fix bugs in expt and integer-expt
  2010-11-03 23:27                   ` Ludovic Courtès
  2010-11-04 17:27                     ` Mark H Weaver
@ 2010-11-05  2:18                     ` Mark H Weaver
  2010-11-10 22:01                       ` Ludovic Courtès
  2011-01-20 22:47                       ` Andy Wingo
  1 sibling, 2 replies; 24+ messages in thread
From: Mark H Weaver @ 2010-11-05  2:18 UTC (permalink / raw)
  To: guile-devel

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

Here's my patch to fix some bugs in expt and integer-expt.
Please review.

     Mark


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: Patch to fix bugs in expt and integer-expt --]
[-- Type: text/x-diff, Size: 6321 bytes --]

From 19a24107af814e34fe63d02478bfb9441354625e Mon Sep 17 00:00:00 2001
From: Mark H Weaver <mhw@netris.org>
Date: Thu, 4 Nov 2010 22:10:02 -0400
Subject: [PATCH] Fix bugs in expt and integer-expt

* libguile/numbers.c (expt): Fix bug that caused expt to throw an
  exception whenever the base was exact and the exponent was an
  inexact integer, e.g. (expt 5 6.0).

  (expt): Fix bug that caused expt to introduce spurious imaginary
  parts in the result when the base was an inexact negative real and
  the exponent was an integer, e.g. (expt -1.0 2)

  (integer-expt, expt): Change behavior of (integer-expt 0 -1), and
  therefore also (expt 0 -1), to return NaN, per R6RS (actually, R6RS
  says we should throw an exception or return an "unspecified number
  object", but for now we use NaN).  Formerly we returned 0, per R5RS.
  R5RS claims that 0^x=0 for all non-zero x, but that's mathematically
  incorrect, and probably an oversight.

  (integer-expt): Consistently throw a wrong-argument-type exception
  when the exponent is inexact.  Formerly, it didn't always check this
  if the base was 0, 1, or -1.

* test-suite/tests/numbers.test ("integer-expt", "expt"): Add tests.
---
 libguile/numbers.c            |   41 ++++++++++++++++++++++++++++++----
 test-suite/tests/numbers.test |   48 ++++++++++++++++++++++++++++++++++++----
 2 files changed, 79 insertions(+), 10 deletions(-)

diff --git a/libguile/numbers.c b/libguile/numbers.c
index fbc6cc8..b699843 100644
--- a/libguile/numbers.c
+++ b/libguile/numbers.c
@@ -1780,10 +1780,20 @@ SCM_DEFINE (scm_integer_expt, "integer-expt", 2, 0, 0,
   SCM acc = SCM_I_MAKINUM (1L);
 
   SCM_VALIDATE_NUMBER (SCM_ARG1, n);
+  if (!SCM_I_INUMP (k) && !SCM_BIGP (k))
+    SCM_WRONG_TYPE_ARG (2, k);
 
-  /* 0^0 == 1 according to R5RS */
-  if (scm_is_eq (n, SCM_INUM0) || scm_is_eq (n, acc))
-    return scm_is_false (scm_zero_p(k)) ? n : acc;
+  if (scm_is_true (scm_zero_p (n)))
+    {
+      if (scm_is_true (scm_zero_p (k)))  /* 0^0 == 1 per R5RS */
+	return acc;        /* return exact 1, regardless of n */
+      else if (scm_is_true (scm_positive_p (k)))
+	return n;
+      else  /* return NaN for (0 ^ k) for negative k per R6RS */
+	return scm_nan ();
+    }
+  else if (scm_is_eq (n, acc))
+    return acc;
   else if (scm_is_eq (n, SCM_I_MAKINUM (-1L)))
     return scm_is_false (scm_even_p (k)) ? n : acc;
 
@@ -5445,8 +5455,29 @@ SCM_DEFINE (scm_expt, "expt", 2, 0, 0,
 	    "Return @var{x} raised to the power of @var{y}.") 
 #define FUNC_NAME s_scm_expt
 {
-  if (scm_is_true (scm_exact_p (x)) && scm_is_integer (y))
-    return scm_integer_expt (x, y);
+  if (scm_is_integer (y))
+    {
+      if (scm_is_true (scm_exact_p (y)))
+	return scm_integer_expt (x, y);
+      else
+	{
+	  /* Here we handle the case where the exponent is an inexact
+	     integer.  We make the exponent exact in order to use
+	     scm_integer_expt, and thus avoid the spurious imaginary
+	     parts that may result from round-off errors in the general
+	     e^(y log x) method below (for example when squaring a large
+	     negative number).  In this case, we must return an inexact
+	     result for correctness.  We also make the base inexact so
+	     that scm_integer_expt will use fast inexact arithmetic
+	     internally.  Note that making the base inexact is not
+	     sufficient to guarantee an inexact result, because
+	     scm_integer_expt will return an exact 1 when the exponent
+	     is 0, even if the base is inexact. */
+	  return scm_exact_to_inexact
+	    (scm_integer_expt (scm_exact_to_inexact (x),
+			       scm_inexact_to_exact (y)));
+	}
+    }
   else if (scm_is_real (x) && scm_is_real (y) && scm_to_double (x) >= 0.0)
     {
       return scm_from_double (pow (scm_to_double (x), scm_to_double (y)));
diff --git a/test-suite/tests/numbers.test b/test-suite/tests/numbers.test
index 3c3e14f..6ee4bbc 100644
--- a/test-suite/tests/numbers.test
+++ b/test-suite/tests/numbers.test
@@ -2889,10 +2889,32 @@
 (with-test-prefix "expt"
   (pass-if-exception "non-numeric base" exception:wrong-type-arg
                      (expt #t 0))
-  (pass-if "(= 1 (expt 0 0))" (= 1 (expt 0 0)))
-  (pass-if "(= 1 (expt 0 0.0))" (= 1 (expt 0 0.0)))
-  (pass-if "(= 1 (expt 0.0 0))" (= 1 (expt 0.0 0)))
-  (pass-if "(= 1 (expt 0.0 0.0))" (= 1 (expt 0.0 0.0))))
+  (pass-if (eqv? 1 (expt 0 0)))
+  (pass-if (eqv? 1 (expt 0.0 0)))
+  (pass-if (eqv? 1.0 (expt 0 0.0)))
+  (pass-if (eqv? 1.0 (expt 0.0 0.0)))
+  (pass-if (nan? (expt 0 -1)))
+  (pass-if (nan? (expt 0 -1.0)))
+  (pass-if (nan? (expt 0.0 -1)))
+  (pass-if (nan? (expt 0.0 -1.0)))
+  (pass-if (eqv? 0 (expt 0 3)))
+  (pass-if (= 0 (expt 0 4.0)))
+  (pass-if (eqv? 0.0 (expt 0.0 5)))
+  (pass-if (eqv? 0.0 (expt 0.0 6.0)))
+  (pass-if (eqv? -2742638075.5 (expt -2742638075.5 1)))
+  (pass-if (eqv? (* -2742638075.5 -2742638075.5)
+                 (expt -2742638075.5 2)))
+  (pass-if (eqv? 4.0 (expt -2.0 2.0)))
+  (pass-if (eqv? -1/8 (expt -2 -3)))
+  (pass-if (eqv? -0.125 (expt -2.0 -3)))
+  (pass-if (eqv? -0.125 (expt -2 -3.0)))
+  (pass-if (eqv? -0.125 (expt -2.0 -3.0)))
+  (pass-if (eqv? 0.25 (expt 2.0 -2.0)))
+  (pass-if (eqv? (* -1.0 12398 12398) (expt +12398i 2.0)))
+  (pass-if (eqv-loosely? +i (expt -1 0.5)))
+  (pass-if (eqv-loosely? +i (expt -1 1/2)))
+  (pass-if (eqv-loosely? 1.0+1.7320508075688i (expt -8 1/3))))
+
 
 ;;;
 ;;; asinh
@@ -3014,7 +3036,23 @@
   (pass-if-exception "2^-inf" exception:wrong-type-arg
     (integer-expt 2 -inf.0))
   (pass-if-exception "2^nan" exception:wrong-type-arg
-    (integer-expt 2 +nan.0)))
+    (integer-expt 2 +nan.0))
+
+  (pass-if (eqv? 1 (integer-expt 0 0)))
+  (pass-if (eqv? 1 (integer-expt 0.0 0)))
+  (pass-if (nan? (integer-expt 0 -1)))
+  (pass-if (nan? (integer-expt 0.0 -1)))
+  (pass-if (eqv? 0 (integer-expt 0 3)))
+  (pass-if (eqv? 0.0 (integer-expt 0.0 5)))
+  (pass-if (eqv? -2742638075.5 (integer-expt -2742638075.5 1)))
+  (pass-if (eqv? (* -2742638075.5 -2742638075.5)
+                 (integer-expt -2742638075.5 2)))
+  (pass-if (eqv? 4.0 (integer-expt -2.0 2)))
+  (pass-if (eqv? -1/8 (integer-expt -2 -3)))
+  (pass-if (eqv? -0.125 (integer-expt -2.0 -3)))
+  (pass-if (eqv? 0.25 (integer-expt 2.0 -2)))
+  (pass-if (eqv? (* -1.0 12398 12398) (integer-expt +12398.0i 2))))
+
 
 ;;;
 ;;; integer-length
-- 
1.5.6.5


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

* Re: fix for expt bug
  2010-11-04 17:27                     ` Mark H Weaver
@ 2010-11-08 21:08                       ` Ludovic Courtès
  2010-11-20 21:49                         ` Andy Wingo
  0 siblings, 1 reply; 24+ messages in thread
From: Ludovic Courtès @ 2010-11-08 21:08 UTC (permalink / raw)
  To: guile-devel

Hi Mark!

Mark H Weaver <mhw@netris.org> writes:

> No, (integer? 3.0) returns #t, as it should, according to R5RS.
> R5RS's description of "integer?" gives this precise example, and
> guile's implementation agrees.

Damn, I had never realized that, shame on me.

Ludo’.




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

* Re: [PATCH] Fix bugs in expt and integer-expt
  2010-11-05  2:18                     ` [PATCH] Fix bugs in expt and integer-expt Mark H Weaver
@ 2010-11-10 22:01                       ` Ludovic Courtès
  2011-01-20 22:47                       ` Andy Wingo
  1 sibling, 0 replies; 24+ messages in thread
From: Ludovic Courtès @ 2010-11-10 22:01 UTC (permalink / raw)
  To: guile-devel

Hello!

Looks good to me, now that I know about inexact integers.  ;-)

Just a couple of details:

Mark H Weaver <mhw@netris.org> writes:

> From 19a24107af814e34fe63d02478bfb9441354625e Mon Sep 17 00:00:00 2001
> From: Mark H Weaver <mhw@netris.org>
> Date: Thu, 4 Nov 2010 22:10:02 -0400
> Subject: [PATCH] Fix bugs in expt and integer-expt
>
> * libguile/numbers.c (expt): Fix bug that caused expt to throw an
>   exception whenever the base was exact and the exponent was an
>   inexact integer, e.g. (expt 5 6.0).
>
>   (expt): Fix bug that caused expt to introduce spurious imaginary
>   parts in the result when the base was an inexact negative real and
>   the exponent was an integer, e.g. (expt -1.0 2)
>
>   (integer-expt, expt): Change behavior of (integer-expt 0 -1), and
>   therefore also (expt 0 -1), to return NaN, per R6RS (actually, R6RS
>   says we should throw an exception or return an "unspecified number
>   object", but for now we use NaN).  Formerly we returned 0, per R5RS.
>   R5RS claims that 0^x=0 for all non-zero x, but that's mathematically
>   incorrect, and probably an oversight.
>
>   (integer-expt): Consistently throw a wrong-argument-type exception
>   when the exponent is inexact.  Formerly, it didn't always check this
>   if the base was 0, 1, or -1.
>
> * test-suite/tests/numbers.test ("integer-expt", "expt"): Add tests.

Please use the C function names for C code, in the log.

Also, can you add a reference to bug #31464 in the log and next to the
corresponding test cases?

Finally, can you document the change for 0^x with x != 0 in NEWS and
explain the departure from R5RS in doc/ref/api-data.texi?

Thanks!

Ludo’.




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

* Re: fix for expt bug
  2010-11-08 21:08                       ` Ludovic Courtès
@ 2010-11-20 21:49                         ` Andy Wingo
  2010-11-21 22:34                           ` Ludovic Courtès
  0 siblings, 1 reply; 24+ messages in thread
From: Andy Wingo @ 2010-11-20 21:49 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: guile-devel

Hi,

On Mon 08 Nov 2010 22:08, ludo@gnu.org (Ludovic Courtès) writes:

> Mark H Weaver <mhw@netris.org> writes:
>
>> No, (integer? 3.0) returns #t, as it should, according to R5RS.
>> R5RS's description of "integer?" gives this precise example, and
>> guile's implementation agrees.
>
> Damn, I had never realized that, shame on me.

Bill Schottstaedt has a nice rant on
https://ccrma.stanford.edu/software/snd/snd/s7.html. I think all of his
examples are taken from Guile...

    I can't find the right tone for this section; this is the 400-th
    revision; I wish I were a better writer! I think the exact/inexact
    distinction in Scheme is confused and useless, and leads to verbose
    and buggy code. In some Schemes, "rational" means "could possibly be
    expressed equally well as a ratio (floats are approximations)". In
    s7 it's: "is actually expressed as a ratio (or an integer of
    course)"; otherwise "rational?" is the same as "real?":

    (not-s7-scheme)> (rational? (sqrt 2))
    #t

    As I understand it, "inexact" originally meant "floating point", and
    "exact" meant integer or ratio of integers. But words have a life of
    their own. 0.0 somehow became an "inexact" integer (although it can
    be represented exactly in floating point). +inf.0 must be an integer
    — its fractional part is explicitly zero! But +nan.0... And then
    there's:

    (not-s7-scheme)> (integer? 9007199254740993.1)
    #t

    When does this matter? I often need to index into a vector, but the
    index is a float (a "real" in Scheme-speak: its fractional part can
    be non-zero). In one scheme:

    (not-s7-scheme)> (vector-ref #(0) (floor 0.1))
    ERROR: Wrong type (expecting exact integer): 0.0   ; [why?  "it's probably a programmer mistake"!]

    Not to worry, I'll use inexact->exact:

    (not-s7-scheme)> (inexact->exact 0.1)              ; [why? "floats are ratios"!]
    3602879701896397/36028797018963968

    So I end up using the verbose (floor (inexact->exact ...))
    everywhere, and even then I have no guarantee that I'll get a legal
    vector index. When I started work on s7, I thought perhaps "exact"
    could mean "is represented exactly in the computer". We'd have
    integers and ratios exact; reals and complex exact if they are
    exactly represented in the current floating point
    implementation. 0.0 and 0.5 might be exact if the printout isn't
    misleading, and 0.1 is inexact. "integer?" and friends would refer
    instead to the programmer's point of view. That is, if the
    programmer uses 1 or if the thing prints as 1, it is the integer 1,
    whereas 1.0 means floating point (not integer!). And to keep
    exactness in view, we'd have to monitor which operations introduce
    inexactness — a kind of interval arithmetic. But then what would
    inexact->exact do? If we discard the exact/inexact distinction, we
    can maintain backwards compatibility via:

        (define exact? rational?)
        (define (inexact? x) (not (rational? x)))
        (define inexact->exact rationalize) ; or floor
        (define (exact->inexact x) (* x 1.0))

There is also Mike Sperber's paper a few years ago about Scheme's
numeric tower being borked.

Anyway, just to say that you're in good company :)

Andy
-- 
http://wingolog.org/



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

* Re: fix for expt bug
  2010-11-20 21:49                         ` Andy Wingo
@ 2010-11-21 22:34                           ` Ludovic Courtès
  0 siblings, 0 replies; 24+ messages in thread
From: Ludovic Courtès @ 2010-11-21 22:34 UTC (permalink / raw)
  To: guile-devel

Hi,

Andy Wingo <wingo@pobox.com> writes:

> There is also Mike Sperber's paper a few years ago about Scheme's
> numeric tower being borked.
>
> Anyway, just to say that you're in good company :)

Heh, good to know, and interesting link!

Ludo’.




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

* Re: [PATCH] Fix bugs in expt and integer-expt
  2010-11-05  2:18                     ` [PATCH] Fix bugs in expt and integer-expt Mark H Weaver
  2010-11-10 22:01                       ` Ludovic Courtès
@ 2011-01-20 22:47                       ` Andy Wingo
  1 sibling, 0 replies; 24+ messages in thread
From: Andy Wingo @ 2011-01-20 22:47 UTC (permalink / raw)
  To: Mark H Weaver; +Cc: guile-devel

On Fri 05 Nov 2010 03:18, Mark H Weaver <mhw@netris.org> writes:

> Here's my patch to fix some bugs in expt and integer-expt.
> Please review.

Applied with Ludovic's suggestions, thanks to you and Ramakrishnan!

Andy
-- 
http://wingolog.org/



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

end of thread, other threads:[~2011-01-20 22:47 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-10-31 18:12 fix for expt bug Ramakrishnan Muthukrishnan
2010-11-01  0:03 ` Mark H Weaver
2010-11-01  4:03   ` Ramakrishnan Muthukrishnan
2010-11-01 18:23     ` Ramakrishnan Muthukrishnan
2010-11-02  4:55       ` Mark H Weaver
2010-11-02  5:22         ` Ramakrishnan Muthukrishnan
2010-11-02  5:36           ` Ramakrishnan Muthukrishnan
2010-11-02 19:19             ` Mark H Weaver
2010-11-02 23:00             ` Ludovic Courtès
2010-11-03  2:10             ` Mark H Weaver
2010-11-03  9:41               ` Ramakrishnan Muthukrishnan
2010-11-03 15:32                 ` Mark H Weaver
2010-11-03 16:41                   ` Ramakrishnan Muthukrishnan
2010-11-03 16:22                 ` Mark H Weaver
2010-11-03 17:53                   ` Ramakrishnan Muthukrishnan
2010-11-03 18:25                     ` Ramakrishnan Muthukrishnan
2010-11-03 23:27                   ` Ludovic Courtès
2010-11-04 17:27                     ` Mark H Weaver
2010-11-08 21:08                       ` Ludovic Courtès
2010-11-20 21:49                         ` Andy Wingo
2010-11-21 22:34                           ` Ludovic Courtès
2010-11-05  2:18                     ` [PATCH] Fix bugs in expt and integer-expt Mark H Weaver
2010-11-10 22:01                       ` Ludovic Courtès
2011-01-20 22:47                       ` Andy Wingo

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).