Does "math.floor(x)" and "int(x)" produce different results for positive real numbers in Python?

StackOverflow https://stackoverflow.com/questions/16071306

  •  04-04-2022
  •  | 
  •  

Question

I have checked a few examples on the Python shell and they seem to spit out same numbers. But in a program where large set of numbers are supposed to be approximated, they apparently produce different results.

I am trying to write a little program that simulates movement of an object on a rectangular plane. For that, I had to write a class named "RectangularRoom" that takes in a width and a height and creates a grid:

class RectangularRoom(object):
"""
A RectangularRoom represents a rectangular region containing clean or dirty
tiles.

A room has a width and a height and contains (width * height) tiles. At any
particular time, each of these tiles is either clean or dirty.
"""
def __init__(self, width, height):
    """
    Initializes a rectangular room with the specified width and height.

    Initially, no tiles in the room have been cleaned.

    width: an integer > 0
    height: an integer > 0
    """
    self.width = width
    self.height = height
    self.room_coordinates = []
    for m in range(self.width):
        for n in range(self.height):
            self.room_coordinates.append((m,n))
    self.cleaned = []
def cleanTileAtPosition(self, pos):
    """
    Mark the tile under the position POS as cleaned.

    Assumes that POS represents a valid position inside this room.

    pos: a Position
    """
    self.cleaned.append((int(pos.getX()), int(pos.getY())))

def isTileCleaned(self, m, n):
    """
    Return True if the tile (m, n) has been cleaned.

    Assumes that (m, n) represents a valid tile inside the room.

    m: an integer
    n: an integer
    returns: True if (m, n) is cleaned, False otherwise
    """
    assert type (m)== int and type (n) == int
    return (m,n) in self.cleaned

def getNumTiles(self):
    """
    Return the total number of tiles in the room.

    returns: an integer
    """
    return self.width*self.height

def getNumCleanedTiles(self):
    """
    Return the total number of clean tiles in the room.

    returns: an integer
    """
    return len(self.cleaned)

def getRandomPosition(self):
    """ 
    Return a random position inside the room.

    returns: a Position object.
    """
    return Position (random.randrange(0 , self.width), random.randrange(0 , self.height))

def isPositionInRoom(self, pos):
    """
    Return True if pos is inside the room.

    pos: a Position object.
    returns: True if pos is in the room, False otherwise.
    """
    return (int(pos.getX()), int(pos.getY())) in self.room_coordinates

As you see I implemented it using the int() method and the random generator "random.randrange".

In the solution, the teacher has implemented this class using the math.floor() function and the random generator random.random():

class RectangularRoom(object):
"""
A RectangularRoom represents a rectangular region containing clean or dirty
tiles.

A room has a width and a height and contains (width * height) tiles. At any
particular time, each of these tiles is either clean or dirty.
"""
def __init__(self, width, height):
    """
    Initializes a rectangular room with the specified width and height.

    Initially, no tiles in the room have been cleaned.

    width: an integer > 0
    height: an integer > 0
    """
    self.width = width
    self.height = height
    self.tiles = {}
    for x in range(self.width):
        for y in range(self.height):
            self.tiles[(x, y)] = False

def cleanTileAtPosition(self, pos):
    """
    Mark the tile under the position POS as cleaned.

    Assumes that POS represents a valid position inside this room.

    pos: a Position
    """
    x = math.floor(pos.getX())
    y = math.floor(pos.getY())
    self.tiles[(x, y)] = True

def isTileCleaned(self, m, n):
    """
    Return True if the tile (m, n) has been cleaned.

    Assumes that (m, n) represents a valid tile inside the room.

    m: an integer
    n: an integer
    returns: True if (m, n) is cleaned, False otherwise
    """
    return self.tiles[(m, n)]

def getNumTiles(self):
    """
    Return the total number of tiles in the room.

    returns: an integer
    """
    return self.width * self.height

def getNumCleanedTiles(self):
    """
    Return the total number of clean tiles in the room.

    returns: an integer
    """
    return sum(self.tiles.values())

def getRandomPosition(self):
    """
    Return a random position inside the room.

    returns: a Position object.
    """
    return Position(random.random() * self.width,
                    random.random() * self.height)

def isPositionInRoom(self, pos):
    """
    Return True if pos is inside the room.

    pos: a Position object.
    returns: True if pos is in the room, False otherwise.
    """
    return ((0 <= pos.getX() < self.width)
            and (0 <= pos.getY() < self.height))

Surprisingly, these two chunks of code produce completely different results. I want to know why this happens. int() and floor() should have the same effect on a positive number and the two random functions seem to produce similar numbers.

Was it helpful?

Solution

Well, I don't know the answer to your question - I suspect they return the exact same thing. However, I think there is a problem with your solution that might explain the output:

In 'cleanTileAtPosition'

self.cleaned.append((int(pos.getX()), int(pos.getY())))

In 'getNumCleanedTiles'

return len(self.cleaned)

This code seems to allow a tile to be cleaned more than once. Which is not what your teachers code does, as the tile can only be set to 'True' once.

(by the way, since random.randrange returns an integer.. integer conversion should do nothing!)

EDIT: Also - I think the type difference is worth considering. But all your types are 'ints' so it shouldn't be a problem.

OTHER TIPS

There is one big difference between your approaches. random.randrange returns an integer, and you call int() on the positions. You are working with integers. Meanwhile, random.random() * something returns a float, and math.floor also returns a float. Your teacher is working with floats all the time.

That is the at least the difference between int and math.floor, but I don't know why that would produce completely different results. Could you be a little more concrete about that?

Consider the following example:

>>> a = 1.2
>>> b = int(a)
>>> c = math.floor(a)
>>> b
1
>>> c
1.0
>>> type(c)
<type 'float'>
>>> type(a)
<type 'float'>
>>> type(b)
<type 'int'>

Even though int() and floor() do the "same thing" for positive integers, the result is of a different type. This can result in all kinds of different interactions depending on the operations you do with them later on. Python will figure out how to make it "work" from its perspective, though it won't necessarily give you the results you want.

Generally when doing any kind of math in programming, you want to be consistent in the types you use so that you don't have to worry about typecasting and promotion and all that nasty stuff.

Using "integers" instead of math.floor terhe would be better - still, Python has enough black magic in float <-> integer comparisons that this difference alone should be no problem in the code above. The code however, in both versions has a distinct Java or C++ "accent" making it much more complictaed than simple Python code can be. It is likely the difference arises in some other point of the code - not on this rounding. For example - try using random.randint instead of getting a float and later rounding it, to start with.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top