Repository: ManimCommunity/manim

Issue:
include_numbers does not consider step size
## Description of bug / unexpected behavior
<!-- Add a clear and concise description of the problem you encountered. -->
When using NumberLine the step size is not taken into consideration when adding numbers, as decimal config defaults to 0 decimal places it results in axis with duplicate numbers:

![AxesExample_ManimCE_v0 6 0](https://user-images.githubusercontent.com/32387857/121186653-3adde100-c85f-11eb-9be3-334fb1907995.png)

## Expected behavior
<!-- Add a clear and concise description of what you expected to happen. -->
I would expect NumberLine to consider the step size when outputting numbers, as a user is unlikely to use a step size of 0.5 but still want the numbers to be output with 0 decimal places

## How to reproduce the issue
<!-- Provide a piece of code illustrating the undesired behavior. -->
Leave the decimal config to the default values, set the include_numbers flag to True and step size to a decimal value.

<details><summary>Code for reproducing the problem</summary>

```py
class AxesExample(Scene):

    def construct(self):
        axes = Axes(
            x_range=[-2, 2, 0.5],
            y_range=[-2, 2, 1],
            x_length=10,
            axis_config={"color": GREEN},
            x_axis_config={
                "include_numbers": True
            },
            y_axis_config={
                "include_numbers": True
            },
            tips=False,
        )
        self.add(axes)
```

</details>


## Additional media files
<!-- Paste in the files manim produced on rendering the code above. -->

<details><summary>Images/GIFs</summary>

<!-- PASTE MEDIA HERE -->

</details>


## Logs
<details><summary>Terminal output</summary>
<!-- Add "-v DEBUG" when calling manim to generate more detailed logs -->

```
PASTE HERE OR PROVIDE LINK TO https://pastebin.com/ OR SIMILAR
```

<!-- Insert screenshots here (only when absolutely necessary, we prefer copy/pasted output!) -->

</details>


## System specifications

<details><summary>System Details</summary>

- OS (with version, e.g Windows 10 v2004 or macOS 10.15 (Catalina)):
- RAM:
- Python version (`python/py/python3 --version`):
- Installed modules (provide output from `pip list`):
```
PASTE HERE
```
</details>

<details><summary>LaTeX details</summary>

+ LaTeX distribution (e.g. TeX Live 2020):
+ Installed LaTeX packages:
<!-- output of `tlmgr list --only-installed` for TeX Live or a screenshot of the Packages page for MikTeX -->
</details>

<details><summary>FFMPEG</summary>

Output of `ffmpeg -version`:

```
PASTE HERE
```
</details>

## Additional comments
<!-- Add further context that you think might be relevant for this issue here. -->



Discussion:
The easy fix for this would be dynamically setting the `num_decimal_places` default but there's a comment that is kind of against that. So maybe it's intended: 
```py
# avoid mutable arguments in defaults
if decimal_number_config is None:
    decimal_number_config = {
        "num_decimal_places": 0,
        ...
```
The purpose of the comment was to explain why values are being initialized in that manner (setting them in the `__init__` and not the class definition). My editor was raising some warnings about it and I looked into it a bit and it can hypothetically cause some issues (not sure how applicable they are to manim though). Dynamically setting `num_decimal_places` shouldn't cause any issues

Having `num_decimal_places` default to the number of decimal places in the `x_step` would be a good idea.
Ok great, I will raise a PR for this soon

Resolving patch:
diff --git a/manim/mobject/coordinate_systems.py b/manim/mobject/coordinate_systems.py
--- a/manim/mobject/coordinate_systems.py
+++ b/manim/mobject/coordinate_systems.py
@@ -71,7 +71,7 @@ def __init__(
     ):
         self.dimension = dimension
 
-        default_step = 1.0
+        default_step = 1
         if x_range is None:
             x_range = [
                 round(-config["frame_x_radius"]),
diff --git a/manim/mobject/number_line.py b/manim/mobject/number_line.py
--- a/manim/mobject/number_line.py
+++ b/manim/mobject/number_line.py
@@ -53,7 +53,8 @@ class NumberLine(Line):
     tip_height : :class:`float`
         The height of the tip.
     include_numbers : :class:`bool`
-        Determines whether numbers are added to tick marks.
+        Determines whether numbers are added to tick marks. The number of decimal places is determined
+        by the step size, this default can be overridden by ``decimal_number_config``.
     label_direction : Union[:class:`list`, :class:`numpy.ndarray`]
         The specific position to which number mobjects are added on the line.
     line_to_number_buff : :class:`float`
@@ -104,11 +105,6 @@ def __init__(
         **kwargs
     ):
         # avoid mutable arguments in defaults
-        if decimal_number_config is None:
-            decimal_number_config = {
-                "num_decimal_places": 0,
-                "font_size": 24,
-            }  # font_size does nothing
         if numbers_to_exclude is None:
             numbers_to_exclude = []
         if numbers_with_elongated_ticks is None:
@@ -118,13 +114,19 @@ def __init__(
             x_range = [
                 round(-config["frame_x_radius"]),
                 round(config["frame_x_radius"]),
-                1.0,
+                1,
             ]
         elif len(x_range) == 2:
             # adds x_step if not specified. not sure how to feel about this. a user can't know default without peeking at source code
             x_range = [*x_range, 1]
 
         self.x_min, self.x_max, self.x_step = x_range
+        if decimal_number_config is None:
+            decimal_number_config = {
+                "num_decimal_places": self.decimal_places_from_step(),
+                "font_size": 24,
+            }  # font_size does nothing
+
         self.length = length
         self.unit_size = unit_size
         # ticks
@@ -300,6 +302,12 @@ def add_numbers(self, x_values=None, excluding=None, font_size=24, **kwargs):
         self.numbers = numbers
         return numbers
 
+    def decimal_places_from_step(self):
+        step_as_str = str(self.x_step)
+        if "." not in step_as_str:
+            return 0
+        return len(step_as_str.split(".")[-1])
+
 
 class UnitInterval(NumberLine):
     def __init__(


Test patch:
diff --git a/tests/test_number_line.py b/tests/test_number_line.py
--- a/tests/test_number_line.py
+++ b/tests/test_number_line.py
@@ -10,3 +10,43 @@ def test_unit_vector():
     axis2 = NumberLine(width=12, x_min=-2, x_max=5)
     for axis in (axis1, axis2):
         assert np.linalg.norm(axis.get_unit_vector()) == axis.unit_size
+
+
+def test_decimal_determined_by_step():
+    """Checks that step size is considered when determining the number of decimal
+    places."""
+    axis = NumberLine(x_range=[-2, 2, 0.5])
+    expected_decimal_places = 1
+    actual_decimal_places = axis.decimal_number_config["num_decimal_places"]
+    assert actual_decimal_places == expected_decimal_places, (
+        "Expected 1 decimal place but got " + actual_decimal_places
+    )
+
+    axis2 = NumberLine(x_range=[-1, 1, 0.25])
+    expected_decimal_places = 2
+    actual_decimal_places = axis2.decimal_number_config["num_decimal_places"]
+    assert actual_decimal_places == expected_decimal_places, (
+        "Expected 1 decimal place but got " + actual_decimal_places
+    )
+
+
+def test_decimal_config_overrides_defaults():
+    """Checks that ``num_decimal_places`` is determined by step size and gets overridden by ``decimal_number_config``."""
+    axis = NumberLine(
+        x_range=[-2, 2, 0.5], decimal_number_config={"num_decimal_places": 0}
+    )
+    expected_decimal_places = 0
+    actual_decimal_places = axis.decimal_number_config["num_decimal_places"]
+    assert actual_decimal_places == expected_decimal_places, (
+        "Expected 1 decimal place but got " + actual_decimal_places
+    )
+
+
+def test_whole_numbers_step_size_default_to_0_decimal_places():
+    """Checks that ``num_decimal_places`` defaults to 0 when a whole number step size is passed."""
+    axis = NumberLine(x_range=[-2, 2, 1])
+    expected_decimal_places = 0
+    actual_decimal_places = axis.decimal_number_config["num_decimal_places"]
+    assert actual_decimal_places == expected_decimal_places, (
+        "Expected 1 decimal place but got " + actual_decimal_places
+    )