Skip to content

floatmode=unique sometimes giving wrong rounding #18609

@levbishop

Description

@levbishop

Let a be a special float64:

In [26]: a = np.float64.fromhex('-1p-97')

In [27]: a
Out[27]: -6.310887241768095e-30

It has uniquestra, a shortest decimal string that uniquely identifies it (thus can be eval-ed to reproduce it exactly):

In [33]: uniquestra = np.format_float_scientific(a, unique=True)

In [34]: uniquestra
Out[34]: '-6.310887241768095e-30'

In [35]: eval(uniquestra)==a
Out[35]: True

Because a is a special float, unlike most other floats, it has a correctly-rounded precision=15 decimal string that is different from uniquestra:

In [36]: roundedstra = np.format_float_scientific(a, unique=False, precision=15)

In [37]: roundedstra
Out[37]: '-6.310887241768094e-30'

In [38]: eval(roundedstra)==a
Out[38]: False

So far, so good. This kind of thing is why format_float_scientific has the unique flag, to choose between these two cases depending on which is needed in a given situation.

The problem comes with array2string, who takes a floatmode=unique option that should give uniquestr and instead gives roundedstr. The problem is that internally array2string uses FloatingFormat which in floatmode='unique' mode effectively uses format_float_scientific twice: Once with unique=True, precision=None to figure out how many digits of precision are enough to uniquely represent the float, and then a second time with unique=False, precision=foo to actually do the formatting. Most of the time this correctly gives a unique representation of the float, but for a special float like a it fails and gives roundedstra instead of uniquestra.

Reproducing code example:

import numpy as np
a = np.float64.fromhex('-1p-97')
assert eval(np.array2string(a, floatmode='unique'))==a

NumPy/Python version information:

1.20.1 3.8.8 (default, Feb 24 2021, 13:46:16) 
[Clang 10.0.0 ]

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions