Skip to content
Open
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
15 changes: 13 additions & 2 deletions algorithms/math/base_conversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,29 @@ def base_to_int(str_to_convert: str, base: int) -> int:
"""Convert a string in a given base to a base-10 integer.

Args:
str_to_convert: The string representation of the number.
str_to_convert: The string representation of the number, optionally
prefixed with a minus sign as produced by int_to_base.
base: The base of the input string (2-36).

Returns:
The base-10 integer value.

Raises:
ValueError: If the input contains no digits after removing its sign.

Examples:
>>> base_to_int('101', 2)
5
>>> base_to_int('FF', 16)
255
>>> base_to_int('-FF', 16)
-255
"""
is_negative = str_to_convert.startswith("-")
if is_negative:
str_to_convert = str_to_convert[1:]
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
if not str_to_convert:
raise ValueError("number must contain at least one digit")
digit = {}
for ind, char in enumerate(string.digits + string.ascii_uppercase):
digit[char] = ind
Expand All @@ -74,4 +85,4 @@ def base_to_int(str_to_convert: str, base: int) -> int:
for char in str_to_convert[::-1]:
res += digit[char] * multiplier
multiplier *= base
return res
return -res if is_negative else res
30 changes: 30 additions & 0 deletions tests/test_base_conversion_signed.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"""Check that signed encodings can be decoded in every supported base."""

import pytest

from algorithms.math.base_conversion import base_to_int, int_to_base


@pytest.mark.parametrize("base", range(2, 37))
def test_signed_conversion_matches_builtin(base):
"""Use int as an independent oracle for positive and negative outputs."""
for number in [-2**128, -255, -31, -1, 0, 1, 31, 255, 2**128]:
encoded = int_to_base(number, base)
assert int(encoded, base) == number
assert base_to_int(encoded, base) == number


@pytest.mark.parametrize(
"encoded,base,expected",
[("-101", 2, -5), ("-FF", 16, -255), ("-Z", 36, -35), ("-0", 10, 0)],
)
def test_decode_negative_literal(encoded, base, expected):
"""Decoding also works on literals not produced by the encoder."""
assert base_to_int(encoded, base) == expected


@pytest.mark.parametrize("encoded", ["", "-"])
def test_missing_digits_are_rejected(encoded):
"""Neither an empty string nor a sign alone represents zero."""
with pytest.raises(ValueError, match="at least one digit"):
base_to_int(encoded, 16)