We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Sampling Bug
Gym Version: 0.15.3
issue: Box samples numbers above the `high` parameter.

```
from gym.spaces import Box
observation_space = Box(low=-3, high=-1, shape=(9,), dtype='int')
print(observation_space.sample())
>> [ 0 -2  0 -2  0 -1  0 -2  0]
```
The current implementation samples float numbers from uniform distribution of [`low`, `high`] and then converts the resulting samples to desired `dtype`. This runs into the problem of sampling `low` parameter very rarely(and not uniformly) when `dtype` is `int`(as we are converting the floats back to int which results in ceil operation in case of negative numbers) i.e in the above example -3 is almost never sampled as most of the low sampled floats like -2.85, -2.9 get converted to -2.
https://github.com/openai/gym/blob/0cd9266d986d470ed9c0dd87a41cd680b65cfe1c/gym/spaces/box.py#L93-L97


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `gym/spaces/box.py`
Content:
```
1 import numpy as np
2 
3 from .space import Space
4 
5 
6 class Box(Space):
7     """
8     A (possibly unbounded) box in R^n. Specifically, a Box represents the
9     Cartesian product of n closed intervals. Each interval has the form of one
10     of [a, b], (-oo, b], [a, oo), or (-oo, oo).
11     
12     There are two common use cases:
13     
14     * Identical bound for each dimension::
15         >>> Box(low=-1.0, high=2.0, shape=(3, 4), dtype=np.float32)
16         Box(3, 4)
17         
18     * Independent bound for each dimension::
19         >>> Box(low=np.array([-1.0, -2.0]), high=np.array([2.0, 4.0]), dtype=np.float32)
20         Box(2,)
21 
22     """
23     def __init__(self, low, high, shape=None, dtype=np.float32):
24         assert dtype is not None, 'dtype must be explicitly provided. '
25         self.dtype = np.dtype(dtype)
26 
27         if shape is None:
28             assert low.shape == high.shape, 'box dimension mismatch. '
29             self.shape = low.shape
30             self.low = low
31             self.high = high
32         else:
33             assert np.isscalar(low) and np.isscalar(high), 'box requires scalar bounds. '
34             self.shape = tuple(shape)
35             self.low = np.full(self.shape, low)
36             self.high = np.full(self.shape, high)
37 
38         self.low = self.low.astype(self.dtype)
39         self.high = self.high.astype(self.dtype)
40 
41         # Boolean arrays which indicate the interval type for each coordinate
42         self.bounded_below = -np.inf < self.low
43         self.bounded_above = np.inf > self.high
44 
45         super(Box, self).__init__(self.shape, self.dtype)
46 
47     def is_bounded(self, manner="both"):
48         below = np.all(self.bounded_below)
49         above = np.all(self.bounded_above)
50         if manner == "both":
51             return below and above
52         elif manner == "below":
53             return below
54         elif manner == "above":
55             return above
56         else:
57             raise ValueError("manner is not in {'below', 'above', 'both'}")
58 
59     def sample(self):
60         """
61         Generates a single random sample inside of the Box. 
62 
63         In creating a sample of the box, each coordinate is sampled according to
64         the form of the interval:
65         
66         * [a, b] : uniform distribution 
67         * [a, oo) : shifted exponential distribution
68         * (-oo, b] : shifted negative exponential distribution
69         * (-oo, oo) : normal distribution
70         """
71         high = self.high if self.dtype.kind == 'f' \
72                 else self.high.astype('int64') + 1
73         sample = np.empty(self.shape)
74 
75         # Masking arrays which classify the coordinates according to interval
76         # type
77         unbounded   = ~self.bounded_below & ~self.bounded_above
78         upp_bounded = ~self.bounded_below &  self.bounded_above
79         low_bounded =  self.bounded_below & ~self.bounded_above
80         bounded     =  self.bounded_below &  self.bounded_above
81         
82 
83         # Vectorized sampling by interval type
84         sample[unbounded] = self.np_random.normal(
85                 size=unbounded[unbounded].shape)
86 
87         sample[low_bounded] = self.np_random.exponential(
88             size=low_bounded[low_bounded].shape) + self.low[low_bounded]
89         
90         sample[upp_bounded] = -self.np_random.exponential(
91             size=upp_bounded[upp_bounded].shape) - self.high[upp_bounded]
92         
93         sample[bounded] = self.np_random.uniform(low=self.low[bounded], 
94                                             high=high[bounded],
95                                             size=bounded[bounded].shape)
96 
97         return sample.astype(self.dtype)
98         
99     def contains(self, x):
100         if isinstance(x, list):
101             x = np.array(x)  # Promote list to array for contains check
102         return x.shape == self.shape and np.all(x >= self.low) and np.all(x <= self.high)
103 
104     def to_jsonable(self, sample_n):
105         return np.array(sample_n).tolist()
106 
107     def from_jsonable(self, sample_n):
108         return [np.asarray(sample) for sample in sample_n]
109 
110     def __repr__(self):
111         return "Box" + str(self.shape)
112 
113     def __eq__(self, other):
114         return isinstance(other, Box) and (self.shape == other.shape) and np.allclose(self.low, other.low) and np.allclose(self.high, other.high)
115
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/gym/spaces/box.py b/gym/spaces/box.py
--- a/gym/spaces/box.py
+++ b/gym/spaces/box.py
@@ -93,6 +93,8 @@
         sample[bounded] = self.np_random.uniform(low=self.low[bounded], 
                                             high=high[bounded],
                                             size=bounded[bounded].shape)
+        if self.dtype.kind == 'i':
+            sample = np.floor(sample)
 
         return sample.astype(self.dtype)

{"golden_diff": "diff --git a/gym/spaces/box.py b/gym/spaces/box.py\n--- a/gym/spaces/box.py\n+++ b/gym/spaces/box.py\n@@ -93,6 +93,8 @@\n         sample[bounded] = self.np_random.uniform(low=self.low[bounded], \n                                             high=high[bounded],\n                                             size=bounded[bounded].shape)\n+        if self.dtype.kind == 'i':\n+            sample = np.floor(sample)\n \n         return sample.astype(self.dtype)\n", "issue": "Sampling Bug\nGym Version: 0.15.3\r\nissue: Box samples numbers above the `high` parameter.\r\n\r\n```\r\nfrom gym.spaces import Box\r\nobservation_space = Box(low=-3, high=-1, shape=(9,), dtype='int')\r\nprint(observation_space.sample())\r\n>> [ 0 -2  0 -2  0 -1  0 -2  0]\r\n```\r\nThe current implementation samples float numbers from uniform distribution of [`low`, `high`] and then converts the resulting samples to desired `dtype`. This runs into the problem of sampling `low` parameter very rarely(and not uniformly) when `dtype` is `int`(as we are converting the floats back to int which results in ceil operation in case of negative numbers) i.e in the above example -3 is almost never sampled as most of the low sampled floats like -2.85, -2.9 get converted to -2.\r\nhttps://github.com/openai/gym/blob/0cd9266d986d470ed9c0dd87a41cd680b65cfe1c/gym/spaces/box.py#L93-L97\r\n\n", "before_files": [{"content": "import numpy as np\n\nfrom .space import Space\n\n\nclass Box(Space):\n    \"\"\"\n    A (possibly unbounded) box in R^n. Specifically, a Box represents the\n    Cartesian product of n closed intervals. Each interval has the form of one\n    of [a, b], (-oo, b], [a, oo), or (-oo, oo).\n    \n    There are two common use cases:\n    \n    * Identical bound for each dimension::\n        >>> Box(low=-1.0, high=2.0, shape=(3, 4), dtype=np.float32)\n        Box(3, 4)\n        \n    * Independent bound for each dimension::\n        >>> Box(low=np.array([-1.0, -2.0]), high=np.array([2.0, 4.0]), dtype=np.float32)\n        Box(2,)\n\n    \"\"\"\n    def __init__(self, low, high, shape=None, dtype=np.float32):\n        assert dtype is not None, 'dtype must be explicitly provided. '\n        self.dtype = np.dtype(dtype)\n\n        if shape is None:\n            assert low.shape == high.shape, 'box dimension mismatch. '\n            self.shape = low.shape\n            self.low = low\n            self.high = high\n        else:\n            assert np.isscalar(low) and np.isscalar(high), 'box requires scalar bounds. '\n            self.shape = tuple(shape)\n            self.low = np.full(self.shape, low)\n            self.high = np.full(self.shape, high)\n\n        self.low = self.low.astype(self.dtype)\n        self.high = self.high.astype(self.dtype)\n\n        # Boolean arrays which indicate the interval type for each coordinate\n        self.bounded_below = -np.inf < self.low\n        self.bounded_above = np.inf > self.high\n\n        super(Box, self).__init__(self.shape, self.dtype)\n\n    def is_bounded(self, manner=\"both\"):\n        below = np.all(self.bounded_below)\n        above = np.all(self.bounded_above)\n        if manner == \"both\":\n            return below and above\n        elif manner == \"below\":\n            return below\n        elif manner == \"above\":\n            return above\n        else:\n            raise ValueError(\"manner is not in {'below', 'above', 'both'}\")\n\n    def sample(self):\n        \"\"\"\n        Generates a single random sample inside of the Box. \n\n        In creating a sample of the box, each coordinate is sampled according to\n        the form of the interval:\n        \n        * [a, b] : uniform distribution \n        * [a, oo) : shifted exponential distribution\n        * (-oo, b] : shifted negative exponential distribution\n        * (-oo, oo) : normal distribution\n        \"\"\"\n        high = self.high if self.dtype.kind == 'f' \\\n                else self.high.astype('int64') + 1\n        sample = np.empty(self.shape)\n\n        # Masking arrays which classify the coordinates according to interval\n        # type\n        unbounded   = ~self.bounded_below & ~self.bounded_above\n        upp_bounded = ~self.bounded_below &  self.bounded_above\n        low_bounded =  self.bounded_below & ~self.bounded_above\n        bounded     =  self.bounded_below &  self.bounded_above\n        \n\n        # Vectorized sampling by interval type\n        sample[unbounded] = self.np_random.normal(\n                size=unbounded[unbounded].shape)\n\n        sample[low_bounded] = self.np_random.exponential(\n            size=low_bounded[low_bounded].shape) + self.low[low_bounded]\n        \n        sample[upp_bounded] = -self.np_random.exponential(\n            size=upp_bounded[upp_bounded].shape) - self.high[upp_bounded]\n        \n        sample[bounded] = self.np_random.uniform(low=self.low[bounded], \n                                            high=high[bounded],\n                                            size=bounded[bounded].shape)\n\n        return sample.astype(self.dtype)\n        \n    def contains(self, x):\n        if isinstance(x, list):\n            x = np.array(x)  # Promote list to array for contains check\n        return x.shape == self.shape and np.all(x >= self.low) and np.all(x <= self.high)\n\n    def to_jsonable(self, sample_n):\n        return np.array(sample_n).tolist()\n\n    def from_jsonable(self, sample_n):\n        return [np.asarray(sample) for sample in sample_n]\n\n    def __repr__(self):\n        return \"Box\" + str(self.shape)\n\n    def __eq__(self, other):\n        return isinstance(other, Box) and (self.shape == other.shape) and np.allclose(self.low, other.low) and np.allclose(self.high, other.high)\n", "path": "gym/spaces/box.py"}], "after_files": [{"content": "import numpy as np\n\nfrom .space import Space\n\n\nclass Box(Space):\n    \"\"\"\n    A (possibly unbounded) box in R^n. Specifically, a Box represents the\n    Cartesian product of n closed intervals. Each interval has the form of one\n    of [a, b], (-oo, b], [a, oo), or (-oo, oo).\n    \n    There are two common use cases:\n    \n    * Identical bound for each dimension::\n        >>> Box(low=-1.0, high=2.0, shape=(3, 4), dtype=np.float32)\n        Box(3, 4)\n        \n    * Independent bound for each dimension::\n        >>> Box(low=np.array([-1.0, -2.0]), high=np.array([2.0, 4.0]), dtype=np.float32)\n        Box(2,)\n\n    \"\"\"\n    def __init__(self, low, high, shape=None, dtype=np.float32):\n        assert dtype is not None, 'dtype must be explicitly provided. '\n        self.dtype = np.dtype(dtype)\n\n        if shape is None:\n            assert low.shape == high.shape, 'box dimension mismatch. '\n            self.shape = low.shape\n            self.low = low\n            self.high = high\n        else:\n            assert np.isscalar(low) and np.isscalar(high), 'box requires scalar bounds. '\n            self.shape = tuple(shape)\n            self.low = np.full(self.shape, low)\n            self.high = np.full(self.shape, high)\n\n        self.low = self.low.astype(self.dtype)\n        self.high = self.high.astype(self.dtype)\n\n        # Boolean arrays which indicate the interval type for each coordinate\n        self.bounded_below = -np.inf < self.low\n        self.bounded_above = np.inf > self.high\n\n        super(Box, self).__init__(self.shape, self.dtype)\n\n    def is_bounded(self, manner=\"both\"):\n        below = np.all(self.bounded_below)\n        above = np.all(self.bounded_above)\n        if manner == \"both\":\n            return below and above\n        elif manner == \"below\":\n            return below\n        elif manner == \"above\":\n            return above\n        else:\n            raise ValueError(\"manner is not in {'below', 'above', 'both'}\")\n\n    def sample(self):\n        \"\"\"\n        Generates a single random sample inside of the Box. \n\n        In creating a sample of the box, each coordinate is sampled according to\n        the form of the interval:\n        \n        * [a, b] : uniform distribution \n        * [a, oo) : shifted exponential distribution\n        * (-oo, b] : shifted negative exponential distribution\n        * (-oo, oo) : normal distribution\n        \"\"\"\n        high = self.high if self.dtype.kind == 'f' \\\n                else self.high.astype('int64') + 1\n        sample = np.empty(self.shape)\n\n        # Masking arrays which classify the coordinates according to interval\n        # type\n        unbounded   = ~self.bounded_below & ~self.bounded_above\n        upp_bounded = ~self.bounded_below &  self.bounded_above\n        low_bounded =  self.bounded_below & ~self.bounded_above\n        bounded     =  self.bounded_below &  self.bounded_above\n        \n\n        # Vectorized sampling by interval type\n        sample[unbounded] = self.np_random.normal(\n                size=unbounded[unbounded].shape)\n\n        sample[low_bounded] = self.np_random.exponential(\n            size=low_bounded[low_bounded].shape) + self.low[low_bounded]\n        \n        sample[upp_bounded] = -self.np_random.exponential(\n            size=upp_bounded[upp_bounded].shape) - self.high[upp_bounded]\n        \n        sample[bounded] = self.np_random.uniform(low=self.low[bounded], \n                                            high=high[bounded],\n                                            size=bounded[bounded].shape)\n        if self.dtype.kind == 'i':\n            sample = np.floor(sample)\n\n        return sample.astype(self.dtype)\n        \n    def contains(self, x):\n        if isinstance(x, list):\n            x = np.array(x)  # Promote list to array for contains check\n        return x.shape == self.shape and np.all(x >= self.low) and np.all(x <= self.high)\n\n    def to_jsonable(self, sample_n):\n        return np.array(sample_n).tolist()\n\n    def from_jsonable(self, sample_n):\n        return [np.asarray(sample) for sample in sample_n]\n\n    def __repr__(self):\n        return \"Box\" + str(self.shape)\n\n    def __eq__(self, other):\n        return isinstance(other, Box) and (self.shape == other.shape) and np.allclose(self.low, other.low) and np.allclose(self.high, other.high)\n", "path": "gym/spaces/box.py"}]}