GNU bug report logs - #30426
division inconsistency?

Previous Next

Package: guile;

Reported by: bil <at> ccrma.Stanford.EDU

Date: Sun, 11 Feb 2018 23:15:02 UTC

Severity: normal

To reply to this bug, email your comments to 30426 AT debbugs.gnu.org.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to bug-guile <at> gnu.org:
bug#30426; Package guile. (Sun, 11 Feb 2018 23:15:02 GMT) Full text and rfc822 format available.

Acknowledgement sent to bil <at> ccrma.Stanford.EDU:
New bug report received and forwarded. Copy sent to bug-guile <at> gnu.org. (Sun, 11 Feb 2018 23:15:02 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: bil <at> ccrma.Stanford.EDU
To: bug-guile <at> gnu.org
Subject: division inconsistency?
Date: Sun, 11 Feb 2018 14:56:54 -0800
A possible inconsistency:

scheme@(guile-user)> (version)
$1 = "2.0.13"

scheme@(guile-user)> (/ 1 (* 0 +nan.0))
$2 = +nan.0
scheme@(guile-user)> (/ 1 0 +nan.0)
<unnamed port>:3:0: In procedure #<procedure 5557c36be9c0 at <current 
input>:3:0 ()>:
<unnamed port>:3:0: Throw to key `numerical-overflow' with args `("/" 
"Numerical overflow" #f #f)
scheme@(guile-user)> (/ 1 +nan.0 0)
<unnamed port>:5:0: In procedure #<procedure 55ff47f4ad00 at <current 
input>:5:0 ()>:
<unnamed port>:5:0: Throw to key `numerical-overflow' with args `("/" 
"Numerical overflow" #f #f)

scheme@(guile-user)> (* +nan.0 0)
$1 = +nan.0
scheme@(guile-user)> (/ 1 +nan.0)
$2 = +nan.0

similarly with +inf.0:

scheme@(guile-user)> (/ 1 (* +inf.0 0))
$3 = +nan.0
scheme@(guile-user)> (/ 1 +inf.0 0)
<unnamed port>:6:0: In procedure #<procedure 5557c36f7740 at <current 
input>:6:0 ()>:
<unnamed port>:6:0: Throw to key `numerical-overflow' with args `("/" 
"Numerical overflow" #f #f)






Information forwarded to bug-guile <at> gnu.org:
bug#30426; Package guile. (Mon, 12 Feb 2018 21:03:02 GMT) Full text and rfc822 format available.

Message #8 received at 30426 <at> debbugs.gnu.org (full text, mbox):

From: Mark H Weaver <mhw <at> netris.org>
To: bil <at> ccrma.Stanford.EDU
Cc: 30426 <at> debbugs.gnu.org
Subject: Re: bug#30426: division inconsistency?
Date: Mon, 12 Feb 2018 16:01:39 -0500
Hi,

bil <at> ccrma.Stanford.EDU writes:

> scheme@(guile-user)> (help '/)
> - Scheme Procedure: / [x [y .  rest]]
>      Divide the first argument by the product of the remaining
>      arguments.  If called with one argument Z1, 1/Z1 is returned.

This help text is indeed incorrect.  In fact, (/ x y z) is evaluated as
(/ (/ x y) z), which is not necessarily the same as (/ x (* y z)) when
using inexact arithmetic.

> A possible inconsistency:
>
> scheme@(guile-user)> (version)
> $1 = "2.0.13"
>
> scheme@(guile-user)> (/ 1 (* 0 +nan.0))
> $2 = +nan.0

Many years ago, I concluded that (* 0 +nan.0) and (* 0 +inf.0) should be
+nan.0, and that's what Guile does.  I now believe that (* 0 <anything>)
should be 0.  Both of these behaviors are allowed by the R6RS.  Note
that when I write '0', I mean an _exact_ zero.  So, the above expression
may raise an exception in a future version of Guile.

> scheme@(guile-user)> (/ 1 0 +nan.0)
> <unnamed port>:3:0: In procedure #<procedure 5557c36be9c0 at <current
> input>:3:0 ()>:
> <unnamed port>:3:0: Throw to key `numerical-overflow' with args `("/"
> "Numerical overflow" #f #f)

The R6RS specifies that if all arguments to '/' are exact, then the
divisors must all be nonzero.  That does not apply to the above '/' call
because of the inexact +nan.0 argument.  However, our compiler
transforms (/ x y z) to (/ (/ x y) z) in an early pass.

> scheme@(guile-user)> (/ 1 +nan.0 0)
> <unnamed port>:5:0: In procedure #<procedure 55ff47f4ad00 at <current
> input>:5:0 ()>:
> <unnamed port>:5:0: Throw to key `numerical-overflow' with args `("/"
> "Numerical overflow" #f #f)

This should probably return +nan.0, and that's what happens in Guile 2.2
for compiled code.  For some of these edge cases, our compiler has
different behavior than our core numeric procedures.

Our core '/' operator, as defined in numbers.c, raises an exception for
(/ x 0), for any 'x'.  This does not conform to the R6RS, which
specifies that (/ 0.0 0) => +inf.0.  I don't think that rule makes sense
because the sign of the result cannot be justified.  (/ 1 0.0) => +inf.0
and (/ 1 -0.0) => -inf.0 are more justifiable because of the signed
inexact zeroes, but an exact zero is not signed.

However, it may be that we should change this to conform to the R6RS,
and certainly it would be good for our compiler and interpreter to agree
on all of these edge cases.

> scheme@(guile-user)> (* +nan.0 0)
> $1 = +nan.0
> scheme@(guile-user)> (/ 1 +nan.0)
> $2 = +nan.0
>
> similarly with +inf.0:
>
> scheme@(guile-user)> (/ 1 (* +inf.0 0))
> $3 = +nan.0

If we change Guile so that (* 0 x) => 0 for all x, then the expression
above will raise an exception in a future version of Guile.

> scheme@(guile-user)> (/ 1 +inf.0 0)
> <unnamed port>:6:0: In procedure #<procedure 5557c36f7740 at <current
> input>:6:0 ()>:
> <unnamed port>:6:0: Throw to key `numerical-overflow' with args `("/"
> "Numerical overflow" #f #f)

As with the (/ 1 +nan.0 0) case above, this should probably return
+nan.0, and that's what happens in Guile 2.2 for compiled code.

What do you think?

    Regards,
      Mark




Information forwarded to bug-guile <at> gnu.org:
bug#30426; Package guile. (Mon, 12 Feb 2018 21:17:01 GMT) Full text and rfc822 format available.

Message #11 received at 30426 <at> debbugs.gnu.org (full text, mbox):

From: Mark H Weaver <mhw <at> netris.org>
To: bil <at> ccrma.Stanford.EDU
Cc: 30426 <at> debbugs.gnu.org
Subject: Re: bug#30426: division inconsistency?
Date: Mon, 12 Feb 2018 16:15:51 -0500
Mark H Weaver <mhw <at> netris.org> writes:

> Our core '/' operator, as defined in numbers.c, raises an exception for
> (/ x 0), for any 'x'.  This does not conform to the R6RS, which
> specifies that (/ 0.0 0) => +inf.0.  I don't think that rule makes sense

Sorry, I meant to write (/ 1.0 0) => +inf.0, which is one of the
examples given in the R6RS.

> because the sign of the result cannot be justified.  (/ 1 0.0) => +inf.0
> and (/ 1 -0.0) => -inf.0 are more justifiable because of the signed
> inexact zeroes, but an exact zero is not signed.
>
> However, it may be that we should change this to conform to the R6RS,
> and certainly it would be good for our compiler and interpreter to agree
> on all of these edge cases.

        Mark




Information forwarded to bug-guile <at> gnu.org:
bug#30426; Package guile. (Mon, 12 Feb 2018 22:00:02 GMT) Full text and rfc822 format available.

Message #14 received at 30426 <at> debbugs.gnu.org (full text, mbox):

From: bil <at> ccrma.Stanford.EDU
To: Mark H Weaver <mhw <at> netris.org>
Cc: 30426 <at> debbugs.gnu.org
Subject: Re: bug#30426: division inconsistency?
Date: Mon, 12 Feb 2018 13:59:24 -0800
Thanks very much for the informative answer!  I am not sure what
(* 0 +nan.0) should return.  I lean toward +nan.0 mainly because
I assume NaNs exist to indicate an error somewhere, and you want
that to be returned. For (* 0 +inf.0) I have no druthers.
For (/ 0.0 0) and (/ +nan.0 0) s7 throws a divide-by-zero error,
but I have no good reason for doing this.  In s7, I'd prefer
to say (/ x y ...) is equal to (/ x (* y ...)) in all cases.






Information forwarded to bug-guile <at> gnu.org:
bug#30426; Package guile. (Wed, 14 Feb 2018 20:56:01 GMT) Full text and rfc822 format available.

Message #17 received at 30426 <at> debbugs.gnu.org (full text, mbox):

From: Mark H Weaver <mhw <at> netris.org>
To: bil <at> ccrma.Stanford.EDU
Cc: 30426 <at> debbugs.gnu.org
Subject: Re: bug#30426: division inconsistency?
Date: Wed, 14 Feb 2018 15:54:28 -0500
bil <at> ccrma.Stanford.EDU writes:

> Thanks very much for the informative answer!  I am not sure what
> (* 0 +nan.0) should return.  I lean toward +nan.0 mainly because
> I assume NaNs exist to indicate an error somewhere, and you want
> that to be returned.

It's a sensible position.  FWIW, my rationale is that for all exact
integers k, (* k x) => (+ x ...) with k copies of x in the arguments.
By that rule, (* 0 x) => (+).

It's certainly true that NaNs exist to indicate errors, but they might
not be propagated to the final result due to control flow, and for a
typical formal specification of multiplication-by-exact-integer
equivalent to the rule above, the NaN would not be propagated.

More generally, if the result would be the same for _any_ exact real
substituted in place of the NaN, then I believe it's justifiable to drop
the NaN.  R7RS-small states a similar rule in section 6.2.4:

  An arithmetic operation where one operand is NaN returns NaN, unless
  the implementation can prove that the result would be the same if the
  NaN were replaced by any rational number.

> For (* 0 +inf.0) I have no druthers.

> For (/ 0.0 0) and (/ +nan.0 0) s7 throws a divide-by-zero error,
> but I have no good reason for doing this.

For lack of a more comprehensive framework to decide corner cases
involving a mixture of exact and inexact arguments, I've found it useful
to use limits to decide the results of numerical operations involving
inexact zeroes or infinities.  Based on my reading of IEEE 754 and
related articles by Kahan, this is the approach that I've found is most
justifiable and consistent with IEEE 754.

For example, for (<op> 0.0 y ...), I take the limit of (<op> x y ...) as
x approaches zero from above, and similarly for 'x' in other argument
positions.  For -0.0, I take the limit as x approaches zero from below.
Similarly for the infinities.  This rule does not cover cases where more
than one argument is an inexact zero or infinity.

If one accepts this approach, then (/ 0.0 0) is undefined and
(* 0 +inf.0) is 0.

> In s7, I'd prefer to say (/ x y ...) is equal to (/ x (* y ...)) in
> all cases.

FWIW, I believe this is in violation of all RnRS at least as far back as
R3RS, which all specify that '/' returns the quotient of its arguments,
associating to the left.  On the other hand, I've not been shy to
deviate from RnRS when I thought it made sense to do so, so I can't
complain :)

     Regards,
       Mark




Information forwarded to bug-guile <at> gnu.org:
bug#30426; Package guile. (Wed, 14 Feb 2018 22:11:02 GMT) Full text and rfc822 format available.

Message #20 received at 30426 <at> debbugs.gnu.org (full text, mbox):

From: bil <at> ccrma.Stanford.EDU
To: Mark H Weaver <mhw <at> netris.org>
Cc: 30426 <at> debbugs.gnu.org
Subject: Re: bug#30426: division inconsistency?
Date: Wed, 14 Feb 2018 14:10:52 -0800
But if (* 0 x) is 0, you lose the notion that
(* exact inexact) is inexact.  So (* 0 +inf.0)
should be 0.0 or maybe +nan.0.  Similarly with
+nan.0, I suppose.






Information forwarded to bug-guile <at> gnu.org:
bug#30426; Package guile. (Thu, 15 Feb 2018 07:36:02 GMT) Full text and rfc822 format available.

Message #23 received at 30426 <at> debbugs.gnu.org (full text, mbox):

From: Mark H Weaver <mhw <at> netris.org>
To: bil <at> ccrma.Stanford.EDU
Cc: 30426 <at> debbugs.gnu.org
Subject: Re: bug#30426: division inconsistency?
Date: Thu, 15 Feb 2018 02:35:14 -0500
Hi Bill,

bil <at> ccrma.Stanford.EDU writes:

> But if (* 0 x) is 0, you lose the notion that
> (* exact inexact) is inexact.  So (* 0 +inf.0)
> should be 0.0 or maybe +nan.0.  Similarly with
> +nan.0, I suppose.

No, because (* 0 x) is equivalent to (+), where the x is not an input.
In fact, this specific case (multiplication by exact 0) is given as an
example where exact 0 may be returned even if the other argument is
inexact, by R4RS, R5RS, R6RS, and R7RS.

R4RS section 6.5.2, and R5RS section 6.2.2 (Exactness), state:

  With the exception of 'inexact->exact', the operations described in
  this section must generally return inexact results when given any
  inexact arguments.  An operation may, however, return an exact result
  if it can prove that the value of the result is unaffected by the
  inexactness of its arguments.  For example, multiplication of any
  number by an exact zero may produce an exact zero result, even if the
  other argument is inexact.

R6RS section 11.7.1 (Propagation of exactness and inexactness) states:

  One general exception to the rule above is that an implementation may
  return an exact result despite inexact arguments if that exact result
  would be the correct result for all possible substitutions of exact
  arguments for the inexact ones.  An example is (* 1.0 0) which may
  return either 0 (exact) or 0.0 (inexact).

R7RS section 6.2.2 (Exactness) states:

  Except for exact, the operations described in this section must
  generally return inexact results when given any inexact arguments.  An
  operation may, however, return an exact result if it can prove that
  the value of the result is unaffected by the inexactness of its
  arguments.  For example, multiplication of any number by an exact zero
  may produce an exact zero result, even if the other argument is
  inexact.

  Specifically, the expression (* 0 +inf.0) may return 0, or +nan.0, or
  report that inexact numbers are not supported, or report that
  non-rational real numbers are not supported, or fail silently or
  noisily in other implementation-specific ways.

I'm quite sensitive to this issue, so sensitive that I decided to change
Guile several years ago so that (* 0 1.0) would return 0.0 instead of 0.
My rationale was that if the 1.0 were replaced by +inf.0 or +nan.0, then
by IEEE 754 the result should be +nan.0, and therefore that the result
was not the same regardless of the value of the inexact argument.  I
didn't care that R[4567]RS specifically gave this as an example where an
exact 0 may be returned, because I judged that it violated the
principles of the exactness propagation, and I don't want to return an
exact result unless it could in principle be _proved_ to be correct.

The new language in R6RS is what changed my mind.  In R6RS, you may
return an exact result if it "would be the correct result for all
possible substitutions of _exact_ arguments for the inexact ones."  So,
we needn't consider what would happen if +inf.0 or +nan.0 were put in
place of the 1.0 in (* 0 1.0), because +inf.0 and +nan.0 are not exact.

I think this is the right principle.  In mathematics, all real numbers
are finite; there are no infinities and no NaNs.  I regard the inexact
infinities as merely inexact representations of very large finite real
numbers.

What do you think?

     Regards,
       Mark




This bug report was last modified 7 years and 210 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.