dan
dan

Reputation: 471

Float division returning -0.0 instead of 0.0

I'm working on the exercises from Chapter 5 of How to Think Like a Computer Scientist, and am trying to solve the 3rd exercise:

Write a function slope(x1, y1, x2, y2) that returns the slope of the line through the points (x1, y1) and (x2, y2). Be sure your implementation of slope can pass the following doctests:

def slope(x1, y1, x2, y2):
    """
      >>> slope(5, 3, 4, 2)
      1.0
      >>> slope(1, 2, 3, 2)
      0.0
      >>> slope(1, 2, 3, 3)
      0.5
      >>> slope(2, 4, 1, 2)
      2.0
    """

This is the code I've come up with:

def slope(x1, y1, x2, y2):
    """
        >>> slope(5, 3, 4, 2)
        1.0
        >>> slope(1, 2, 3, 2)
        0.0
    """
    sy = float(y1 - y2)
    sx = float(x1 - x2)
    return sy / sx

On the 2nd doctest I'm getting -0.0 instead of 0.0 … which is confusing, since I didn't know -0.0 was a thing. I know I can use abs() to get the absolute value but, then if I do have parameters that should come out to a negative number, it won't work.

Also know I can set a condition on the result so it uses the absolute value or doesn't, but I'm wondering if I'm just doing something wrong here.

Upvotes: 1

Views: 287

Answers (1)

schesis
schesis

Reputation: 59108

You can write a version of slope() that returns positive 0.0 for the arguments in that doctest by swapping the order of y1 - y2 and x1 - x2 in your code:

def slope(x1, y1, x2, y2):
    """
        >>> slope(5, 3, 4, 2)
        1.0
        >>> slope(1, 2, 3, 2)
        0.0
    """
    sy = float(y2 - y1)
    sx = float(x2 - x1)
    return sy / sx

>>> slope(1, 2, 3, 2)
0.0

You'll still get -0.0 if the arguments are reversed:

>>> slope(3, 2, 1, 2)
-0.0

… but outside of doctests that's unlikely to matter, since 0.0 and -0.0 are equal by definition. However, if you want to be sure of always returning a positive zero, you can just add zero to your return value:

def slope(x1, y1, x2, y2):
    """
        >>> slope(5, 3, 4, 2)
        1.0
        >>> slope(1, 2, 3, 2)
        0.0
    """
    sy = float(y2 - y1)
    sx = float(x2 - x1)
    return sy / sx + 0  # prevent -0.0

>>> slope(1, 2, 3, 2)
0.0
>>> slope(3, 2, 1, 2)
0.0

The "add positive zero" trick is arbitrary and essentially magical behaviour, and works simply because the IEEE 754 floating point standard says it must:

When the sum of two operands with opposite signs (or the difference of two operands with like signs) is exactly zero, the sign of that sum (or difference) shall be +0 in all rounding-direction attributes except roundTowardNegative; under that attribute, the sign of an exact zero sum (or difference) shall be −0. However, x + x = x − (−x) retains the same sign as x even when x is zero.

— IEEE 754, Section 6.3

More information about signed zero in floating point arithmetic can be found at What Every Computer Scientist Should Know About Floating-Point Arithmetic.

Upvotes: 2

Related Questions