# 2021/08/26

## Negative zero

Gauche supports IEEE754 negative zero -0.0. It simply wraps an IEEE754 double as a scheme object, so mostly it just works as specified in IEEE754 (and as supported by the underlying math library). Or, so we thought.

Let's recap the behavior of -0.0.
It's numerically indistinguishable from 0.0 (so, it is *not*
"an infinitely small value less than zero"):

(= -0.0 0.0) ⇒ #t (< -0.0 0.0) ⇒ #f (zero? -0.0) ⇒ #t

But it can make a difference when there's a functon *f(x)* such that it is
discontinuous at *x* = 0, and
*f(x)* goes to different values when *x* approaches to zero from positive
side or negative side.

(/ 0.0) ⇒ +inf.0 (/ -0.0) ⇒ -inf.0

For arithmetic primitive procedures, we simply pass unboxed double to the underlying math functions, so we didn't think we need to handle -0.0 specially.

The first wakeup call was this article via HackerNews:

One does not simply calculate the absolute value

It talks about writing `abs`

in Java, but every time I saw articles like
this I just try it out on Gauche, and alas!

;; Gauche 0.9.10 (abs -0.0) ⇒ -0.0 ; Ouch!

Yeah, the culprit was the C implementation of `abs`

, the gist of which was:

if (x < 0.0) return -x; else return x;

-0.0 doesn't satisfy `x < 0.0`

so it was returned without negation.

The easy fix is to use `signbit`

.

if (signbit(x)) return -x;

I reported the fix on Twitter, then somebody raised an issue: What about
`(eqv? -0.0 0.0)`

?

My initial reaction was that it should be `#t`

, since `(= -0.0 0.0)`

is `#t`

. In fact, R5RS states this:

The

`eqv?`

procedure returns`#t`

if: ...obj1andobj2are both numbers, are numerically equal (see`=`

...), and are either both exact or both inexact.

However, I realized that R7RS has more subtle definition.

The

`eqv?`

procedure returns`#f`

if: ...obj1andobj2are both inexact numbers such that either they are numerically unequal (in the sense of`=`

), or they do not yield the same results (...) when passed as arguments to any other procedure that can be defined as a finite composition of Scheme's standard arithmetic procedures, ...

Clearly, -0.0 and 0.0 don't yield the same results when passed to `/`

,
so it should return `#f`

. (It is also mentioned in 6.2.4
that -0.0 is distinct from 0.0 in a sense of `eqv?`

.)

Fix for this is a bit involved. When I fixed `eqv?`

, a bunch of
tests started failing. It looks like some
inexact integer division routines in the tests yield -0.0, and
are compared to 0.0 with `equal?`

, which should follow `eqv?`

if arguments are numbers.

It turned out that the root cause was rounding primitives returning -0.0:

;; Gauche 0.9.10 (ceiling -0.5) ⇒ -0.0

Although this itself is plausible, in most of the cases when you're thinking of integers (exact or inexact), you want to treat zero as zero. Certainly you don't want to deal with two distint zeros in quotients or remainders. The choices would be either leave the rounding primitives as are and fix the integer divisions, or change the rounding primitives altogether. I choose the latter.

The fixes are in the HEAD now.

;; Gauche 0.9.11 (eqv? -0.0 0.0) ⇒ #f (ceiling -0.5) ⇒ 0.0

## Andreas (2021/08/27 22:19:10):

## shiro (2021/08/28 09:29:25):