13

I stumbled upon a floor division result of a np.float32 or np.float64 , which I do not understand

I'm using numpy 1.15.4 in Python 3.6.7

>>> import numpy as np
>>> np.float32(0)//1
-0.0

I know, that i can workaround by abs() and such, but why am I getting a "-0.0" instead of "0.0" in the first place??

CDspace
  • 2,639
  • 18
  • 30
  • 36
Michal
  • 163
  • 1
  • 7
  • 1
    https://stackoverflow.com/a/4083431/6361531 – Scott Boston Jan 23 '19 at 16:40
  • @ScottBoston I suggest you to flag it as duplicate instead of just adding the link – DDS Jan 23 '19 at 16:41
  • 2
    Possible duplicate of [negative zero in python](https://stackoverflow.com/questions/4083401/negative-zero-in-python) – Matthieu Brucher Jan 23 '19 at 16:42
  • 3
    @DDS I am not 100% sure this addresses OP's concerns. – Scott Boston Jan 23 '19 at 16:43
  • @DDS I don't know, that does not seem to be the same question. I don't see how that would explain how "normal" 0 divided by a positive number yields negative zero. I don't think IEEE 754 mandates (or even allows) that. – Baum mit Augen Jan 23 '19 at 16:44
  • 3
    @DDS the link doesn't explain the fact that the vanilla Python equivalent `0.0//1 == 0.0` instead of `-0.0`. – meowgoesthedog Jan 23 '19 at 16:44
  • 2
    @ScottBoston It is related, but I think it doesn't explain why `(+0.0) // 1 == -0.0`... especially considering it is something that happens with NumPy but not with Python floating point values. – jdehesa Jan 23 '19 at 16:44
  • For discussion about `numpy` negative zero, see this thread: https://stackoverflow.com/questions/26782038/how-to-eliminate-the-extra-minus-sign-when-rounding-negative-numbers-towards-zer – Daweo Jan 23 '19 at 17:03

1 Answers1

9

I suspect that numpy uses the divmod function to compute the floor division and the line causing this is here:

/* if div is zero ensure correct sign */
floordiv = (a / b > 0) ? 0.0@c@ : -0.0@c@;

Example:

>>> a = np.zeros(1)
>>> b = 1
>>> np.where(a/b > 0, 0.0, -0.0)
array([-0.])

Python's divmod function seems to handle this correctly, so they must be using different algorithms:

>>> divmod(0.0,1)
(0.0, 0.0)
>>> divmod(-0.0,1)
(-0.0, 0.0)

I looked into this a bit more and here is how python's divmod works for floating point numbers when div is zero (link):

/* div is zero - get the same sign as the true quotient */
floordiv = copysign(0.0, vx / wx); /* zero w/ sign of vx/wx */

and copysign() is defined as:

double
copysign(double x, double y)
{
    /* use atan2 to distinguish -0. from 0. */
    if (y > 0. || (y == 0. && atan2(y, -1.) > 0.)) {
        return fabs(x);
    } else {
        return -fabs(x);
    }
}

so the reason python is able to get this right and numpy isn't is that python uses atan2() to distinguish between -0.0 and +0.0.

Update: This issue will be fixed in the 1.17.0 release of numpy. You can see the release notes here.

user545424
  • 15,713
  • 11
  • 56
  • 70