Mercurial > octave
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 |