summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAllan Haldane <allan.haldane@gmail.com>2018-01-15 23:47:30 -0500
committerAllan Haldane <allan.haldane@gmail.com>2018-01-20 15:53:16 -0500
commit04520b82fe9dc31775b519f44d05d12d156c8bd0 (patch)
treed2a650e84375a0afa0fbb054e75656586c98f4d6
parent2820a14d913b3f2e01bed5997e4b981b41b8907b (diff)
downloadnumpy-04520b82fe9dc31775b519f44d05d12d156c8bd0.tar.gz
BUG: complex repr has extra spaces, missing +
Fixes #10355 Fixes #10356
-rw-r--r--doc/release/1.14.1-notes.rst1
-rw-r--r--numpy/core/arrayprint.py67
-rw-r--r--numpy/core/tests/test_arrayprint.py18
3 files changed, 63 insertions, 23 deletions
diff --git a/doc/release/1.14.1-notes.rst b/doc/release/1.14.1-notes.rst
index b67772d18..cbb1f54f7 100644
--- a/doc/release/1.14.1-notes.rst
+++ b/doc/release/1.14.1-notes.rst
@@ -7,6 +7,7 @@ important fixes are:
important fixes are
+important fixes are to the spacing in the str and repr of complex values.
The Python versions supported are 2.7 and 3.4 - 3.6. The Python 3.6 wheels
available from PIP are built with Python 3.6.2 and should be compatible with
diff --git a/numpy/core/arrayprint.py b/numpy/core/arrayprint.py
index 369b3a913..53ea11571 100644
--- a/numpy/core/arrayprint.py
+++ b/numpy/core/arrayprint.py
@@ -99,8 +99,10 @@ def set_printoptions(precision=None, threshold=None, edgeitems=None,
Parameters
----------
- precision : int, optional
+ precision : int or None, optional
Number of digits of precision for floating point output (default 8).
+ May be `None` if `floatmode` is not `fixed`, to print as many digits as
+ necessary to uniquely specify the value.
threshold : int, optional
Total number of array elements which trigger summarization
rather than full repr (default 1000).
@@ -240,6 +242,8 @@ def set_printoptions(precision=None, threshold=None, edgeitems=None,
# set the C variable for legacy mode
if _format_options['legacy'] == '1.13':
set_legacy_print_mode(113)
+ # reset the sign option in legacy mode to avoid confusion
+ _format_options['sign'] = '-'
elif _format_options['legacy'] is False:
set_legacy_print_mode(0)
@@ -502,7 +506,7 @@ def array2string(a, max_line_width=None, precision=None,
max_line_width : int, optional
The maximum number of columns the string should span. Newline
characters splits the string appropriately after array elements.
- precision : int, optional
+ precision : int or None, optional
Floating point precision. Default is the current printing
precision (usually 8), which can be altered using `set_printoptions`.
suppress_small : bool, optional
@@ -782,6 +786,13 @@ def _formatArray(a, format_function, line_width, next_line_prefix,
curr_width=line_width)
+def _none_or_positive_arg(x, name):
+ if x is None:
+ return -1
+ if x < 0:
+ raise ValueError("{} must be >= 0".format(name))
+ return x
+
class FloatingFormat(object):
""" Formatter for subtypes of np.floating """
def __init__(self, data, precision, floatmode, suppress_small, sign=False,
@@ -792,17 +803,18 @@ class FloatingFormat(object):
self._legacy = kwarg.get('legacy', False)
if self._legacy == '1.13':
- sign = '-' if data.shape == () else ' '
+ # when not 0d, legacy does not support '-'
+ if data.shape != () and sign == '-':
+ sign = ' '
self.floatmode = floatmode
if floatmode == 'unique':
- self.precision = -1
+ self.precision = None
else:
- if precision < 0:
- raise ValueError(
- "precision must be >= 0 in {} mode".format(floatmode))
self.precision = precision
+ self.precision = _none_or_positive_arg(self.precision, 'precision')
+
self.suppress_small = suppress_small
self.sign = sign
self.exp_format = False
@@ -935,7 +947,6 @@ class LongFloatFormat(FloatingFormat):
DeprecationWarning, stacklevel=2)
super(LongFloatFormat, self).__init__(*args, **kwargs)
-
def format_float_scientific(x, precision=None, unique=True, trim='k',
sign=False, pad_left=None, exp_digits=None):
"""
@@ -948,9 +959,9 @@ def format_float_scientific(x, precision=None, unique=True, trim='k',
----------
x : python float or numpy floating scalar
Value to format.
- precision : non-negative integer, optional
- Maximum number of fractional digits to print. May be omitted if
- `unique` is `True`, but is required if unique is `False`.
+ precision : non-negative integer or None, optional
+ Maximum number of digits to print. May be None if `unique` is
+ `True`, but must be an integer if unique is `False`.
unique : boolean, optional
If `True`, use a digit-generation strategy which gives the shortest
representation which uniquely identifies the floating-point number from
@@ -995,9 +1006,9 @@ def format_float_scientific(x, precision=None, unique=True, trim='k',
>>> np.format_float_scientific(s, exp_digits=4)
'1.23e+0024'
"""
- precision = -1 if precision is None else precision
- pad_left = -1 if pad_left is None else pad_left
- exp_digits = -1 if exp_digits is None else exp_digits
+ precision = _none_or_positive_arg(precision, 'precision')
+ pad_left = _none_or_positive_arg(pad_left, 'pad_left')
+ exp_digits = _none_or_positive_arg(exp_digits, 'exp_digits')
return dragon4_scientific(x, precision=precision, unique=unique,
trim=trim, sign=sign, pad_left=pad_left,
exp_digits=exp_digits)
@@ -1015,9 +1026,9 @@ def format_float_positional(x, precision=None, unique=True,
----------
x : python float or numpy floating scalar
Value to format.
- precision : non-negative integer, optional
- Maximum number of digits to print. May be omitted if `unique` is
- `True`, but is required if unique is `False`.
+ precision : non-negative integer or None, optional
+ Maximum number of digits to print. May be None if `unique` is
+ `True`, but must be an integer if unique is `False`.
unique : boolean, optional
If `True`, use a digit-generation strategy which gives the shortest
representation which uniquely identifies the floating-point number from
@@ -1068,9 +1079,9 @@ def format_float_positional(x, precision=None, unique=True,
>>> np.format_float_positional(np.float16(0.3), unique=False, precision=10)
'0.3000488281'
"""
- precision = -1 if precision is None else precision
- pad_left = -1 if pad_left is None else pad_left
- pad_right = -1 if pad_right is None else pad_right
+ precision = _none_or_positive_arg(precision, 'precision')
+ pad_left = _none_or_positive_arg(pad_left, 'pad_left')
+ pad_right = _none_or_positive_arg(pad_right, 'pad_right')
return dragon4_positional(x, precision=precision, unique=unique,
fractional=fractional, trim=trim,
sign=sign, pad_left=pad_left,
@@ -1108,15 +1119,25 @@ class ComplexFloatingFormat(object):
if isinstance(sign, bool):
sign = '+' if sign else '-'
- self.real_format = FloatingFormat(x.real, precision, floatmode,
+ floatmode_real = floatmode_imag = floatmode
+ if kwarg.get('legacy', False) == '1.13':
+ floatmode_real = 'maxprec_equal'
+ floatmode_imag = 'maxprec'
+
+ self.real_format = FloatingFormat(x.real, precision, floatmode_real,
suppress_small, sign=sign, **kwarg)
- self.imag_format = FloatingFormat(x.imag, precision, floatmode,
+ self.imag_format = FloatingFormat(x.imag, precision, floatmode_imag,
suppress_small, sign='+', **kwarg)
def __call__(self, x):
r = self.real_format(x.real)
i = self.imag_format(x.imag)
- return r + i + 'j'
+
+ # add the 'j' before the terminal whitespace in i
+ sp = len(i.rstrip())
+ i = i[:sp] + 'j' + i[sp:]
+
+ return r + i
# for back-compatibility, we keep the classes for each complex type too
class ComplexFormat(ComplexFloatingFormat):
diff --git a/numpy/core/tests/test_arrayprint.py b/numpy/core/tests/test_arrayprint.py
index 97b1bd4da..033d36f44 100644
--- a/numpy/core/tests/test_arrayprint.py
+++ b/numpy/core/tests/test_arrayprint.py
@@ -433,21 +433,28 @@ class TestPrintOptions(object):
def test_sign_spacing(self):
a = np.arange(4.)
b = np.array([1.234e9])
+ c = np.array([1.0 + 1.0j, 1.123456789 + 1.123456789j], dtype='c16')
assert_equal(repr(a), 'array([0., 1., 2., 3.])')
assert_equal(repr(np.array(1.)), 'array(1.)')
assert_equal(repr(b), 'array([1.234e+09])')
assert_equal(repr(np.array([0.])), 'array([0.])')
+ assert_equal(repr(c),
+ "array([1. +1.j , 1.12345679+1.12345679j])")
np.set_printoptions(sign=' ')
assert_equal(repr(a), 'array([ 0., 1., 2., 3.])')
assert_equal(repr(np.array(1.)), 'array( 1.)')
assert_equal(repr(b), 'array([ 1.234e+09])')
+ assert_equal(repr(c),
+ "array([ 1. +1.j , 1.12345679+1.12345679j])")
np.set_printoptions(sign='+')
assert_equal(repr(a), 'array([+0., +1., +2., +3.])')
assert_equal(repr(np.array(1.)), 'array(+1.)')
assert_equal(repr(b), 'array([+1.234e+09])')
+ assert_equal(repr(c),
+ "array([+1. +1.j , +1.12345679+1.12345679j])")
np.set_printoptions(legacy='1.13')
assert_equal(repr(a), 'array([ 0., 1., 2., 3.])')
@@ -455,6 +462,8 @@ class TestPrintOptions(object):
assert_equal(repr(-b), 'array([ -1.23400000e+09])')
assert_equal(repr(np.array(1.)), 'array(1.0)')
assert_equal(repr(np.array([0.])), 'array([ 0.])')
+ assert_equal(repr(c),
+ "array([ 1.00000000+1.j , 1.12345679+1.12345679j])")
assert_raises(TypeError, np.set_printoptions, wrongarg=True)
@@ -478,6 +487,7 @@ class TestPrintOptions(object):
0.0862072768214508, 0.39112753029631175],
dtype=np.float64)
z = np.arange(6, dtype=np.float16)/10
+ c = np.array([1.0 + 1.0j, 1.123456789 + 1.123456789j], dtype='c16')
# also make sure 1e23 is right (is between two fp numbers)
w = np.array(['1e{}'.format(i) for i in range(25)], dtype=np.float64)
@@ -503,6 +513,8 @@ class TestPrintOptions(object):
" 1.e+16, 1.e+17, 1.e+18, 1.e+19, 1.e+20, 1.e+21, 1.e+22, 1.e+23,\n"
" 1.e+24])")
assert_equal(repr(wp), "array([1.234e+001, 1.000e+002, 1.000e+123])")
+ assert_equal(repr(c),
+ "array([1. +1.j , 1.123456789+1.123456789j])")
# maxprec mode, precision=8
np.set_printoptions(floatmode='maxprec', precision=8)
@@ -517,6 +529,8 @@ class TestPrintOptions(object):
assert_equal(repr(w[::5]),
"array([1.e+00, 1.e+05, 1.e+10, 1.e+15, 1.e+20])")
assert_equal(repr(wp), "array([1.234e+001, 1.000e+002, 1.000e+123])")
+ assert_equal(repr(c),
+ "array([1. +1.j , 1.12345679+1.12345679j])")
# fixed mode, precision=4
np.set_printoptions(floatmode='fixed', precision=4)
@@ -531,6 +545,8 @@ class TestPrintOptions(object):
"array([1.0000e+00, 1.0000e+05, 1.0000e+10, 1.0000e+15, 1.0000e+20])")
assert_equal(repr(wp), "array([1.2340e+001, 1.0000e+002, 1.0000e+123])")
assert_equal(repr(np.zeros(3)), "array([0.0000, 0.0000, 0.0000])")
+ assert_equal(repr(c),
+ "array([1.0000+1.0000j, 1.1235+1.1235j])")
# for larger precision, representation error becomes more apparent:
np.set_printoptions(floatmode='fixed', precision=8)
assert_equal(repr(z),
@@ -550,6 +566,8 @@ class TestPrintOptions(object):
assert_equal(repr(w[::5]),
"array([1.e+00, 1.e+05, 1.e+10, 1.e+15, 1.e+20])")
assert_equal(repr(wp), "array([1.234e+001, 1.000e+002, 1.000e+123])")
+ assert_equal(repr(c),
+ "array([1.00000000+1.00000000j, 1.12345679+1.12345679j])")
def test_legacy_mode_scalars(self):
# in legacy mode, str of floats get truncated, and complex scalars