Skip to content

potential rounding error with use of divmod #18

Closed
@tswast

Description

@tswast

In googleapis/python-bigquery#840, I switched to using divmod as is done here and got the following test failures:

__________________________________ test_w_string_values[32-11 45 67:16:23.987654-expected30] __________________________________

mut = <module 'google.cloud.bigquery._helpers' from '/Users/swast/src/github.com/googleapis/python-bigquery/google/cloud/bigquery/_helpers.py'>
value = '32-11 45 67:16:23.987654'
expected = relativedelta(years=+32, months=+11, days=+47, hours=+19, minutes=+16, seconds=+23, microseconds=+987654)

    @pytest.mark.parametrize(
        ("value", "expected"),
        (
            ("0-0 0 0:0:0", relativedelta()),
            # SELECT INTERVAL X YEAR
            ("-10000-0 0 0:0:0", relativedelta(years=-10000)),
            ("-1-0 0 0:0:0", relativedelta(years=-1)),
            ("1-0 0 0:0:0", relativedelta(years=1)),
            ("10000-0 0 0:0:0", relativedelta(years=10000)),
            # SELECT INTERVAL X MONTH
            ("-0-11 0 0:0:0", relativedelta(months=-11)),
            ("-0-1 0 0:0:0", relativedelta(months=-1)),
            ("0-1 0 0:0:0", relativedelta(months=1)),
            ("0-11 0 0:0:0", relativedelta(months=11)),
            # SELECT INTERVAL X DAY
            ("0-0 -3660000 0:0:0", relativedelta(days=-3660000)),
            ("0-0 -1 0:0:0", relativedelta(days=-1)),
            ("0-0 1 0:0:0", relativedelta(days=1)),
            ("0-0 3660000 0:0:0", relativedelta(days=3660000)),
            # SELECT INTERVAL X HOUR
            ("0-0 0 -87840000:0:0", relativedelta(hours=-87840000)),
            ("0-0 0 -1:0:0", relativedelta(hours=-1)),
            ("0-0 0 1:0:0", relativedelta(hours=1)),
            ("0-0 0 87840000:0:0", relativedelta(hours=87840000)),
            # SELECT INTERVAL X MINUTE
            ("0-0 0 -0:59:0", relativedelta(minutes=-59)),
            ("0-0 0 -0:1:0", relativedelta(minutes=-1)),
            ("0-0 0 0:1:0", relativedelta(minutes=1)),
            ("0-0 0 0:59:0", relativedelta(minutes=59)),
            # SELECT INTERVAL X SECOND
            ("0-0 0 -0:0:59", relativedelta(seconds=-59)),
            ("0-0 0 -0:0:1", relativedelta(seconds=-1)),
            ("0-0 0 0:0:1", relativedelta(seconds=1)),
            ("0-0 0 0:0:59", relativedelta(seconds=59)),
            # SELECT (INTERVAL -1 SECOND) / 1000000
            ("0-0 0 -0:0:0.000001", relativedelta(microseconds=-1)),
            ("0-0 0 -0:0:59.999999", relativedelta(seconds=-59, microseconds=-999999)),
            ("0-0 0 -0:0:59.999", relativedelta(seconds=-59, microseconds=-999000)),
            ("0-0 0 0:0:59.999", relativedelta(seconds=59, microseconds=999000)),
            ("0-0 0 0:0:59.999999", relativedelta(seconds=59, microseconds=999999)),
            # Test with multiple digits in each section.
            (
                "32-11 45 67:16:23.987654",
                relativedelta(
                    years=32,
                    months=11,
                    days=45,
                    hours=67,
                    minutes=16,
                    seconds=23,
                    microseconds=987654,
                ),
            ),
            (
                "-32-11 -45 -67:16:23.987654",
                relativedelta(
                    years=-32,
                    months=-11,
                    days=-45,
                    hours=-67,
                    minutes=-16,
                    seconds=-23,
                    microseconds=-987654,
                ),
            ),
            # Test with mixed +/- sections.
            (
                "9999-9 -999999 9999999:59:59.999999",
                relativedelta(
                    years=9999,
                    months=9,
                    days=-999999,
                    hours=9999999,
                    minutes=59,
                    seconds=59,
                    microseconds=999999,
                ),
            ),
            # Test with fraction that is not microseconds.
            ("0-0 0 0:0:42.", relativedelta(seconds=42)),
            ("0-0 0 0:0:59.1", relativedelta(seconds=59, microseconds=100000)),
            ("0-0 0 0:0:0.12", relativedelta(microseconds=120000)),
            ("0-0 0 0:0:0.123", relativedelta(microseconds=123000)),
            ("0-0 0 0:0:0.1234", relativedelta(microseconds=123400)),
        ),
    )
    def test_w_string_values(mut, value, expected):
        got = mut._interval_from_json(value, create_field())
>       assert got == expected
E       assert relativedelta(years=+32, months=+11, days=+47, hours=+19, minutes=+16, seconds=+23, microseconds=+987653) == relativedelta(years=+32, months=+11, days=+47, hours=+19, minutes=+16, seconds=+23, microseconds=+987654)

tests/unit/helpers/test_from_json.py:135: AssertionError
________________________________ test_w_string_values[-32-11 -45 -67:16:23.987654-expected31] _________________________________

mut = <module 'google.cloud.bigquery._helpers' from '/Users/swast/src/github.com/googleapis/python-bigquery/google/cloud/bigquery/_helpers.py'>
value = '-32-11 -45 -67:16:23.987654'
expected = relativedelta(years=-32, months=-11, days=-47, hours=-19, minutes=-16, seconds=-23, microseconds=-987654)

    @pytest.mark.parametrize(
        ("value", "expected"),
        (
            ("0-0 0 0:0:0", relativedelta()),
            # SELECT INTERVAL X YEAR
            ("-10000-0 0 0:0:0", relativedelta(years=-10000)),
            ("-1-0 0 0:0:0", relativedelta(years=-1)),
            ("1-0 0 0:0:0", relativedelta(years=1)),
            ("10000-0 0 0:0:0", relativedelta(years=10000)),
            # SELECT INTERVAL X MONTH
            ("-0-11 0 0:0:0", relativedelta(months=-11)),
            ("-0-1 0 0:0:0", relativedelta(months=-1)),
            ("0-1 0 0:0:0", relativedelta(months=1)),
            ("0-11 0 0:0:0", relativedelta(months=11)),
            # SELECT INTERVAL X DAY
            ("0-0 -3660000 0:0:0", relativedelta(days=-3660000)),
            ("0-0 -1 0:0:0", relativedelta(days=-1)),
            ("0-0 1 0:0:0", relativedelta(days=1)),
            ("0-0 3660000 0:0:0", relativedelta(days=3660000)),
            # SELECT INTERVAL X HOUR
            ("0-0 0 -87840000:0:0", relativedelta(hours=-87840000)),
            ("0-0 0 -1:0:0", relativedelta(hours=-1)),
            ("0-0 0 1:0:0", relativedelta(hours=1)),
            ("0-0 0 87840000:0:0", relativedelta(hours=87840000)),
            # SELECT INTERVAL X MINUTE
            ("0-0 0 -0:59:0", relativedelta(minutes=-59)),
            ("0-0 0 -0:1:0", relativedelta(minutes=-1)),
            ("0-0 0 0:1:0", relativedelta(minutes=1)),
            ("0-0 0 0:59:0", relativedelta(minutes=59)),
            # SELECT INTERVAL X SECOND
            ("0-0 0 -0:0:59", relativedelta(seconds=-59)),
            ("0-0 0 -0:0:1", relativedelta(seconds=-1)),
            ("0-0 0 0:0:1", relativedelta(seconds=1)),
            ("0-0 0 0:0:59", relativedelta(seconds=59)),
            # SELECT (INTERVAL -1 SECOND) / 1000000
            ("0-0 0 -0:0:0.000001", relativedelta(microseconds=-1)),
            ("0-0 0 -0:0:59.999999", relativedelta(seconds=-59, microseconds=-999999)),
            ("0-0 0 -0:0:59.999", relativedelta(seconds=-59, microseconds=-999000)),
            ("0-0 0 0:0:59.999", relativedelta(seconds=59, microseconds=999000)),
            ("0-0 0 0:0:59.999999", relativedelta(seconds=59, microseconds=999999)),
            # Test with multiple digits in each section.
            (
                "32-11 45 67:16:23.987654",
                relativedelta(
                    years=32,
                    months=11,
                    days=45,
                    hours=67,
                    minutes=16,
                    seconds=23,
                    microseconds=987654,
                ),
            ),
            (
                "-32-11 -45 -67:16:23.987654",
                relativedelta(
                    years=-32,
                    months=-11,
                    days=-45,
                    hours=-67,
                    minutes=-16,
                    seconds=-23,
                    microseconds=-987654,
                ),
            ),
            # Test with mixed +/- sections.
            (
                "9999-9 -999999 9999999:59:59.999999",
                relativedelta(
                    years=9999,
                    months=9,
                    days=-999999,
                    hours=9999999,
                    minutes=59,
                    seconds=59,
                    microseconds=999999,
                ),
            ),
            # Test with fraction that is not microseconds.
            ("0-0 0 0:0:42.", relativedelta(seconds=42)),
            ("0-0 0 0:0:59.1", relativedelta(seconds=59, microseconds=100000)),
            ("0-0 0 0:0:0.12", relativedelta(microseconds=120000)),
            ("0-0 0 0:0:0.123", relativedelta(microseconds=123000)),
            ("0-0 0 0:0:0.1234", relativedelta(microseconds=123400)),
        ),
    )
    def test_w_string_values(mut, value, expected):
        got = mut._interval_from_json(value, create_field())
>       assert got == expected
E       assert relativedelta(years=-32, months=-11, days=-47, hours=-19, minutes=-16, seconds=-23, microseconds=-987653) == relativedelta(years=-32, months=-11, days=-47, hours=-19, minutes=-16, seconds=-23, microseconds=-987654)

tests/unit/helpers/test_from_json.py:135: AssertionError
=================================================== short test summary info ===================================================
FAILED tests/unit/helpers/test_from_json.py::test_w_string_values[32-11 45 67:16:23.987654-expected30] - assert relativedelt...
FAILED tests/unit/helpers/test_from_json.py::test_w_string_values[-32-11 -45 -67:16:23.987654-expected31] - assert relatived...

I think the problem is the conversion to floating point isn't exact due to difference between base-2 and base-10 representation.

Metadata

Metadata

Assignees

Labels

api: bigqueryIssues related to the googleapis/python-db-dtypes-pandas API.priority: p1Important issue which blocks shipping the next release. Will be fixed prior to next release.type: bugError or flaw in code with unintended results or allowing sub-optimal usage patterns.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions