comparison scripts/general/num2str.m @ 21057:b0afe1993268

Overhaul num2str.m and int2str.m for Matlab compatibility (bug #46770). * num2str.m: Add multiple programming notes to docstring. Re-write algorithm to determine sprintf format to handle all zero arrays, all NaN/Inf arrays. Rename variable 'dgt' to 'ndgt' for clarity. Don't add an extra formatting space to handle the minus sign for negative numbers (Matlab compatibility). Use '%.0f' format for integers rather than '%g' as it displays slightly better. Rename variable 'tmp' to 'strtmp' for clarity. Rewrite some BIST tests to match new function behavior. * int2str.m: Replace code with the same code used for integers in num2str.m Add Programming Note to docstring.
author Rik <rik@octave.org>
date Wed, 13 Jan 2016 12:05:28 -0800
parents b51078f07886
children b433f9990452
comparison
equal deleted inserted replaced
21056:d48fdf3a8c0c 21057:b0afe1993268
50 ## num2str (1.234 + 27.3i) 50 ## num2str (1.234 + 27.3i)
51 ## @result{} "1.234+27.3i" 51 ## @result{} "1.234+27.3i"
52 ## @end group 52 ## @end group
53 ## @end example 53 ## @end example
54 ## 54 ##
55 ## Notes: 55 ## The @code{num2str} function is not very flexible. For better control
56 ## over the results, use @code{sprintf} (@pxref{Formatted Output}).
57 ##
58 ## Programming Notes:
56 ## 59 ##
57 ## For @sc{matlab} compatibility, leading spaces are stripped before returning 60 ## For @sc{matlab} compatibility, leading spaces are stripped before returning
58 ## the string. 61 ## the string.
59 ## 62 ##
60 ## The @code{num2str} function is not very flexible. For better control 63 ## Integers larger than @code{flintmax} may not be displayed correctly.
61 ## over the results, use @code{sprintf} (@pxref{Formatted Output}).
62 ## 64 ##
63 ## For complex @var{x}, the format string may only contain one output 65 ## For complex @var{x}, the format string may only contain one output
64 ## conversion specification and nothing else. Otherwise, results will be 66 ## conversion specification and nothing else. Otherwise, results will be
65 ## unpredictable. 67 ## unpredictable.
68 ##
69 ## Any optional @var{format} specified by the programmer is used without
70 ## modification. This is in contrast to @sc{matlab} which tampers with the
71 ## @var{format} based on internal heuristics.
66 ## @seealso{sprintf, int2str, mat2str} 72 ## @seealso{sprintf, int2str, mat2str}
67 ## @end deftypefn 73 ## @end deftypefn
68 74
69 ## Author: jwe 75 ## Author: jwe
70 76
89 else 95 else
90 error ("num2str: PRECISION must be a scalar integer >= 0"); 96 error ("num2str: PRECISION must be a scalar integer >= 0");
91 endif 97 endif
92 else 98 else
93 if (isnumeric (x)) 99 if (isnumeric (x))
94 ## Setup a suitable format string, ignoring inf entries 100 ## Set up a suitable format string while ignoring Inf/NaN entries
95 dgt = floor (log10 (max (abs (x(! isinf (x(:))))))); 101 valid = isfinite (x(:));
96 if (isempty (dgt)) 102 ndgt = floor (log10 (max (abs (x(valid)))));
97 ## If the whole input array is inf... 103 if (isempty (ndgt) || ndgt == -Inf)
98 dgt = 1; 104 ndgt = 0; # All Inf or all zero array
99 endif 105 endif
100 106
101 if (any (x(:) != fix (x(:)))) 107 if (any (x(valid) != fix (x(valid))))
102 ## Floating point input 108 ## Floating point input
103 dgt = max (dgt + 4, 5); # Keep 4 sig. figures after decimal point 109 ndgt = max (ndgt + 5, 5); # Keep at least 5 significant digits
104 dgt = min (dgt, 16); # Cap significant digits at 16 110 ndgt = min (ndgt, 16); # Cap significant digits at 16
105 fmt = sprintf ("%%%d.%dg", dgt+7+any (x(:) < 0), dgt); 111 fmt = sprintf ("%%%d.%dg", ndgt+7, ndgt);
106 else 112 else
107 ## Integer input 113 ## Integer input
108 dgt = max (dgt + 1, 1); 114 ndgt += 3;
115 if (any (! valid))
116 ndgt = max (ndgt, 5); # Allow space for Inf/NaN
117 endif
109 ## FIXME: Integers should be masked to show only 16 significant digits 118 ## FIXME: Integers should be masked to show only 16 significant digits
110 ## See %!xtest below 119 ## See %!xtest with 1e23 below.
111 fmt = sprintf ("%%%d.%dg", dgt+2+any (x(:) < 0), dgt); 120 fmt = sprintf ("%%%d.0f", ndgt);
112 endif 121 endif
113 else 122 else
114 ## Logical input 123 ## Logical input
115 fmt = "%3d"; 124 fmt = "%3d";
116 endif 125 endif
120 nc = columns (x) * (nd - 1); # ND-arrays are expanded in columns 129 nc = columns (x) * (nd - 1); # ND-arrays are expanded in columns
121 x = permute (x, [2, 3:nd, 1]); 130 x = permute (x, [2, 3:nd, 1]);
122 if (! (sum (fmt == "%") > 1 || any (strcmp (fmt, {"%s", "%c"})))) 131 if (! (sum (fmt == "%") > 1 || any (strcmp (fmt, {"%s", "%c"}))))
123 fmt = [deblank(repmat (fmt, 1, nc)), "\n"]; 132 fmt = [deblank(repmat (fmt, 1, nc)), "\n"];
124 endif 133 endif
125 tmp = sprintf (fmt, x); 134 strtmp = sprintf (fmt, x);
126 retval = strtrim (char (ostrsplit (tmp, "\n", true))); 135 retval = strtrim (char (ostrsplit (strtmp, "\n", true)));
127 else # Complex matrix input 136 else # Complex matrix input
128 if (nargin == 2) 137 if (nargin == 2)
129 if (ischar (arg)) 138 if (ischar (arg))
130 fmt = [deblank(arg) "%-+" arg(2:end) "i"]; 139 fmt = [deblank(arg) "%-+" arg(2:end) "i"];
131 elseif (isnumeric (arg) && isscalar (arg) && arg >= 0 && arg == fix (arg)) 140 elseif (isnumeric (arg) && isscalar (arg) && arg >= 0 && arg == fix (arg))
132 fmt = sprintf ("%%%d.%dg%%-+%d.%dgi", arg+7, arg, arg+7, arg); 141 fmt = sprintf ("%%%d.%dg%%-+%d.%dgi", arg+7, arg, arg+7, arg);
133 else 142 else
134 error ("num2str: PRECISION must be a scalar integer >= 0"); 143 error ("num2str: PRECISION must be a scalar integer >= 0");
135 endif 144 endif
136 else 145 else
137 ## Setup a suitable format string 146 ## Set up a suitable format string while ignoring Inf/NaN entries
138 dgt = floor (log10 (max (max (abs (real (x(! isinf (real (x(:))))))), 147 valid_real = isfinite (real (x(:)));
139 max (abs (imag (x(! isinf (imag (x(:)))))))))); 148 valid_imag = isfinite (imag (x(:)));
140 if (isempty (dgt)) 149 ndgt = floor (log10 (max (max (abs (real (x(valid_real)))),
141 ## If the whole input array is inf... 150 max (abs (imag (x(valid_imag)))))));
142 dgt = 1; 151 if (isempty (ndgt) || ndgt == -Inf)
143 endif 152 ndgt = 0; # All Inf or all zero array
144 153 endif
145 if (any (x(:) != fix (x(:)))) 154
155 if (any (x(valid_real & valid_imag) != fix (x(valid_real & valid_imag))))
146 ## Floating point input 156 ## Floating point input
147 dgt = max (dgt + 4, 5); # Keep 4 sig. figures after decimal point 157 ndgt = max (ndgt + 5, 5); # Keep at least 5 significant digits
148 dgt = min (dgt, 16); # Cap significant digits at 16 158 ndgt = min (ndgt, 16); # Cap significant digits at 16
149 fmt = sprintf ("%%%d.%dg%%-+%d.%dgi", dgt+7, dgt, dgt+7, dgt); 159 fmt = sprintf ("%%%d.%dg%%-+%d.%dgi", ndgt+7, ndgt, ndgt+7, ndgt);
150 else 160 else
151 ## Integer input 161 ## Integer input
152 dgt = max (1 + dgt, 1); 162 ndgt += 3;
153 ## FIXME: Integers should be masked to show only 16 significant digits 163 ## FIXME: Integers should be masked to show only 16 significant digits
154 ## See %!xtest below 164 ## See %!xtest below
155 fmt = sprintf ("%%%d.%dg%%-+%d.%dgi", dgt+2, dgt, dgt+2, dgt); 165 fmt = sprintf ("%%%d.0f%%-+%d.0fi", ndgt, ndgt);
156 endif 166 endif
157 endif 167 endif
158 168
159 ## Manipulate the complex value to have real values in the odd 169 ## Manipulate the complex value to have real values in the odd
160 ## columns and imaginary values in the even columns. 170 ## columns and imaginary values in the even columns.
190 %!assert (num2str (19440606), "19440606") 200 %!assert (num2str (19440606), "19440606")
191 %!assert (num2str (2^33), "8589934592") 201 %!assert (num2str (2^33), "8589934592")
192 %!assert (num2str (-2^33), "-8589934592") 202 %!assert (num2str (-2^33), "-8589934592")
193 %!assert (num2str (2^33+1i), "8589934592+1i") 203 %!assert (num2str (2^33+1i), "8589934592+1i")
194 %!assert (num2str (-2^33+1i), "-8589934592+1i") 204 %!assert (num2str (-2^33+1i), "-8589934592+1i")
205 %!assert (num2str ([0 0 0]), "0 0 0")
195 %!assert (num2str (inf), "Inf") 206 %!assert (num2str (inf), "Inf")
196 %!assert (num2str ([inf -inf]), "Inf -Inf") 207 %!assert (num2str ([inf -inf]), "Inf -Inf")
208 %!assert (num2str ([inf NaN -inf]), "Inf NaN -Inf")
197 %!assert (num2str ([complex(Inf,0), complex(0,-Inf)]), "Inf+0i 0-Infi") 209 %!assert (num2str ([complex(Inf,0), complex(0,-Inf)]), "Inf+0i 0-Infi")
198 %!assert (num2str (complex(Inf,1)), "Inf+1i") 210 %!assert (num2str (complex(Inf,1)), "Inf+1i")
199 %!assert (num2str (complex(1,Inf)), "1+Infi") 211 %!assert (num2str (complex(1,Inf)), "1+Infi")
200 %!assert (num2str (nan), "NaN") 212 %!assert (num2str (nan), "NaN")
201 %!assert (num2str (complex (NaN, 1)), "NaN+1i") 213 %!assert (num2str (complex (NaN, 1)), "NaN+1i")
211 223
212 ## real case 224 ## real case
213 %!test 225 %!test
214 %! y = num2str (x); 226 %! y = num2str (x);
215 %! assert (rows (y) == 3); 227 %! assert (rows (y) == 3);
216 %! assert (y, ["8 1 6 -8 -1 -6" 228 %! assert (y, ["8 1 6 -8 -1 -6"
217 %! "3 5 7 -3 -5 -7" 229 %! "3 5 7 -3 -5 -7"
218 %! "4 9 2 -4 -9 -2"]); 230 %! "4 9 2 -4 -9 -2"]);
219 231
220 ## complex case 232 ## complex case
221 %!test 233 %!test
222 %! x(1,1,2) = -8+2i; 234 %! x(1,1,2) = -8+2i;
223 %! y = num2str (x); 235 %! y = num2str (x);
233 ## 16 digits of precision. 245 ## 16 digits of precision.
234 %!xtest 246 %!xtest
235 %! assert (num2str (1e23), "100000000000000000000000"); 247 %! assert (num2str (1e23), "100000000000000000000000");
236 248
237 ## Test for bug #44864, extra rows generated from newlines in format 249 ## Test for bug #44864, extra rows generated from newlines in format
238 %!assert (rows (num2str (magic (3), '%3d %3d %3d\n')), 3) 250 %!assert (rows (num2str (magic (3), "%3d %3d %3d\n")), 3)
239 251
240 ## Test for bug #45174 252 ## Test for bug #45174
241 %!assert (num2str ([65 66 67], '%s'), "ABC") 253 %!assert (num2str ([65 66 67], "%s"), "ABC")
242 254
243 %!error num2str () 255 %!error num2str ()
244 %!error num2str (1, 2, 3) 256 %!error num2str (1, 2, 3)
245 %!error <X must be a numeric> num2str ({1}) 257 %!error <X must be a numeric> num2str ({1})
246 %!error <PRECISION must be a scalar integer> num2str (1, {1}) 258 %!error <PRECISION must be a scalar integer .= 0> num2str (1, {1})
247 %!error <PRECISION must be a scalar integer> num2str (1, ones (2)) 259 %!error <PRECISION must be a scalar integer .= 0> num2str (1, ones (2))
248 %!error <PRECISION must be a scalar integer> num2str (1, -1) 260 %!error <PRECISION must be a scalar integer .= 0> num2str (1, -1)
249 %!error <PRECISION must be a scalar integer> num2str (1+1i, {1}) 261 %!error <PRECISION must be a scalar integer .= 0> num2str (1, 1.5)
250 %!error <PRECISION must be a scalar integer> num2str (1+1i, ones (2)) 262 %!error <PRECISION must be a scalar integer .= 0> num2str (1+1i, {1})
251 %!error <PRECISION must be a scalar integer> num2str (1+1i, -1) 263 %!error <PRECISION must be a scalar integer .= 0> num2str (1+1i, ones (2))
252 264 %!error <PRECISION must be a scalar integer .= 0> num2str (1+1i, -1)
265 %!error <PRECISION must be a scalar integer .= 0> num2str (1+1i, 1.5)
266