diff options
-rw-r--r-- | doc/release/1.14.1-notes.rst | 1 | ||||
-rw-r--r-- | numpy/core/arrayprint.py | 67 | ||||
-rw-r--r-- | numpy/core/tests/test_arrayprint.py | 18 |
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 |