Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions _python_utils_tests/test_converters.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Tests for the conversion helpers in ``python_utils.converters``."""

import math
import re

import pytest
Expand Down Expand Up @@ -33,6 +34,41 @@ def test_scale_1024_small_number(value: float) -> None:
assert converters.scale_1024(value, 9) == (value, 0)


@pytest.mark.parametrize('power', range(1, 9))
def test_scale_1024_integer_prefix_boundaries(power: int) -> None:
"""Choose prefixes from the integer value before rounding it to float."""
boundary = 1024**power
for value, expected_power in (
(boundary - 1, power - 1),
(boundary, power),
(boundary + 1, power),
):
assert converters.scale_1024(value, 9) == (
float(value) / 1024**expected_power,
expected_power,
)


@pytest.mark.parametrize('power', range(1, 9))
def test_scale_1024_float_prefix_boundaries(power: int) -> None:
"""Keep the float immediately below a prefix boundary in the lower unit."""
boundary = float(1024**power)
for value, expected_power in (
(math.nextafter(boundary, 0.0), power - 1),
(boundary, power),
(math.nextafter(boundary, math.inf), power),
):
assert converters.scale_1024(value, 9) == (
value / 1024**expected_power,
expected_power,
)


def test_scale_1024_prefix_limit() -> None:
"""Keep values in the largest available prefix even at higher powers."""
assert converters.scale_1024(1024**5, 3) == (1024.0**3, 2)


@pytest.mark.parametrize('n_prefixes', [0, -1])
def test_scale_1024_without_prefixes(n_prefixes: int) -> None:
"""Never return a negative power when there are no prefixes."""
Expand Down
8 changes: 8 additions & 0 deletions python_utils/converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,9 @@ def scale_1024(

Use to format numbers of bytes to KiB, MiB, etc.

A prefix is used only when the input reaches its power of 1024. For
integer inputs, the prefix is chosen before converting the value to float.

>>> scale_1024(310, 3)
(310.0, 0)
>>> scale_1024(2048, 3)
Expand All @@ -325,6 +328,11 @@ def scale_1024(
# Never below zero: a number under 1 has a negative logarithm, and a
# negative power would index the prefixes from the wrong end.
power = max(min(int(math.log(x, 2) / 10), n_prefixes - 1), 0)
# A logarithm can round a value just below a prefix boundary up to
# that boundary. Compare with the exact integer power before the
# input itself is rounded to float.
if power and x < 2 ** (10 * power):
power -= 1
scaled = float(x) / (2 ** (10 * power))
return scaled, power

Expand Down
Loading