Matlab ShortEng number format via sprintf() and fprintf()? - matlab

I like using MATLAB's shortEng notation in the interactive Command Window:
>> a = 123e-12;
>> disp(a);
1.2300e-10 % Scientific notation. Urgh!
>> format shortEng;
>> disp(a);
123.0000e-012 % Engineering notation! :-D
But I want to use fprintf:
>> format shortEng;
>> fprintf('%0.3e', a);
1.2300e-10 % Scientific. Urgh!
How do I print values with fprintf or sprintf with Engineering formatting using the MATLAB Format Operators?
I know I could write my own function to format the values into strings, but I'm looking for something already built into MATLAB.
NOTE: "Engineering" notation differs from "Scientific" in that the exponent is always a multiple of 3.
>> fprintf('%0.3e', a); % This is Scientific notation.
1.230000e-10

There is no way to use directly fprintf format specifier for the format you require. A way around is to use the output of disp as a string to be printed. But disp doesn't return a string, it writes directly to the standard output. So, how to do this?
Here's where evalc (eval with capture of output) comes to the rescue:
%// Create helper function
sdisp = #(x) strtrim(evalc(sprintf('disp(%g)', x)));
%// Test helper function
format ShortEng;
a = 123e-12;
fprintf(1, 'Test: %s', sdisp(a));
This is a workaround, of course, and can backfire in multiple ways because of the untested inputs of the helper functions. But it illustrates a point, and is one of the rare occasions where the reviled eval function family is actually irreplaceable.

You can use the following utility:
http://www.people.fas.harvard.edu/~arcrock/lib118/numutil/unpacknum.m
This will unpack the number also according to a given number N and makes sure that the exponent will be a multiple of N. By putting N=3 you have the Engineering Notation.
More into detail, unpacknum takes 3 arguments: the number x, the base (10 if you want Engineering Notation) and the value N (3 if you want Engineering Notation) and it returns the couple (f,e) which you can use in fprintf().
Check the unpacknum help for a quick example.

This function converts a value into a string in engineering notation:
function sNum = engn(value)
exp= floor(log10(abs(value)));
if ( (exp < 3) && (exp >=0) )
exp = 0; % Display without exponent
else
while (mod(exp, 3))
exp= exp - 1;
end
end
frac=value/(10^exp); % Adjust fraction to exponent
if (exp == 0)
sNum = sprintf('%+8.5G', frac);
else
sNum = sprintf('%+8.5GE%+.2d', frac, exp);
end
end
You can finetune the format to your liking. Usage in combination with fprintf is easy enough:
fprintf('%s\t%s\n', engn(543210.123), engn(-0.0000567)) % +543.21E+03 -56.7E-06
fprintf('%s\t%s\n', engn(-321.123), engn(876543210)) % -321.12 +876.54E+06

You can use the following utility posted to the MATLAB file exchange:
num2eng
It offers extensive control over the formatting of the output string and full input checking, so is more flexible and less prone to error than the simpler evalc approach suggested by user2271770.
It can also output strings using SI prefixes instead of engineering notation, if you prefer.

Related

Creating a function with variable number of inputs?

I am trying to define the following function in MATLAB:
file = #(var1,var2,var3,var4) ['var1=' num2str(var1) 'var2=' num2str(var2) 'var3=' num2str(var3) 'var4=' num2str(var4)'];
However, I want the function to expand as I add more parameters; if I wanted to add the variable vark, I want the function to be:
file = #(var1,var2,var3,var4,vark) ['var1=' num2str(var1) 'var2=' num2str(var2) 'var3=' num2str(var3) 'var4=' num2str(var4) 'vark=' num2str(vark)'];
Is there a systematic way to do this?
Use fprintf with varargin for this:
f = #(varargin) fprintf('var%i= %i\n', [(1:numel(varargin));[varargin{:}]])
f(5,6,7,88)
var1= 5
var2= 6
var3= 7
var4= 88
The format I've used is: 'var%i= %i\n'. This means it will first write var then %i says it should input an integer. Thereafter it should write = followed by a new number: %i and a newline \n.
It will choose the integer in odd positions for var%i and integers in the even positions for the actual number. Since the linear index in MATLAB goes column for column we place the vector [1 2 3 4 5 ...] on top, and the content of the variable in the second row.
By the way: If you actually want it on the format you specified in the question, skip the \n:
f = #(varargin) fprintf('var%i= %i', [(1:numel(varargin));[varargin{:}]])
f(6,12,3,15,5553)
var1= 6var2= 12var3= 3var4= 15var5= 5553
Also, you can change the second %i to floats (%f), doubles (%d) etc.
If you want to use actual variable names var1, var2, var3, ... in your input then I can only say one thing: Don't! It's a horrible idea. Use cells, structs, or anything else than numbered variable names.
Just to be crytsal clear: Don't use the output from this in MATLAB in combination with eval! eval is evil. The Mathworks actually warns you about this in the official documentation!
How about calling the function as many times as the number of parameters? I wrote this considering the specific form of the character string returned by your function where k is assumed to be the index of the 'kth' variable to be entered. Array var can be the list of your numeric parameters.
file=#(var,i)[strcat('var',num2str(i),'=') num2str(var) ];
var=[2,3,4,5];
str='';
for i=1:length(var);
str=strcat(str,file(var(i),i));
end
If you want a function to accept a flexible number of input arguments, you need varargin.
In case you want the final string to be composed of the names of your variables as in your workspace, I found no way, since you need varargin and then it looks impossible. But if you are fine with having var1, var2 in your string, you can define this function and then use it:
function str = strgen(varargin)
str = '';
for ii = 1:numel(varargin);
str = sprintf('%s var%d = %s', str, ii, num2str(varargin{ii}));
end
str = str(2:end); % to remove the initial blank space
It is also compatible with strings. Testing it:
% A = pi;
% B = 'Hello!';
strgen(A, B)
ans =
var1 = 3.1416 var2 = Hello!

Scientific notation in MATLAB

Say I have an array that contains the following elements:
1.0e+14 *
1.3325 1.6485 2.0402 1.0485 1.2027 2.0615 1.7432 1.9709 1.4807 0.9012
Now, is there a way to grab 1.0e+14 * (base and exponent) individually?
If I do arr(10), then this will return 9.0120e+13 instead of 0.9012e+14.
Assuming the question is to grab any elements in the array with coefficient less than one. Is there a way to obtain 1.0e+14, so that I could just do arr(i) < 1.0e+14?
I assume you want string output.
Let a denote the input numeric array. You can do it this way, if you don't mind using evalc (a variant of eval, which is considered bad practice):
s = evalc('disp(a)');
s = regexp(s, '[\de+-\.]+', 'match');
This produces a cell array with the desired strings.
Example:
>> a = [1.2e-5 3.4e-6]
a =
1.0e-04 *
0.1200 0.0340
>> s = evalc('disp(a)');
>> s = regexp(s, '[\de+-\.]+', 'match')
s =
'1.0e-04' '0.1200' '0.0340'
Here is the original answer from Alain.
Basic math can tell you that:
floor(log10(N))
The log base 10 of a number tells you approximately how many digits before the decimal are in that number.
For instance, 99987123459823754 is 9.998E+016
log10(99987123459823754) is 16.9999441, the floor of which is 16 - which can basically tell you "the exponent in scientific notation is 16, very close to being 17".
Now you have the exponent of the scientific notation. This should allow you to get to whatever your goal is ;-).
And depending on what you want to do with your exponent and the number, you could also define your own method. An example is described in this thread.

x = disp(y) : "Too many output arguments"

I'm looking for a completely general way to convert any value to a string in MATLAB.
Basically, I want to be able to write something like
x = disp(y);
The above fails with the error Too many output arguments. (I was not able to find the source code for disp.)
Is there a single MATLAB function for converting any value into a string?
(Note that this function should behave like the identity when passed a string.)
Basically I'm looking for MATLAB's equivalent of Python's str. I thought it might be char, but (for example) char(Inf) fails to produce anything like the string 'Inf'. (Note: that was just an example. It does not begin to cover all the possibilities.)
pm89's answer has the right idea, but doesn't work because evalc requires a string as input. I suggest making your own function like so:
function str = anything2string(thing)
str = evalc('disp(thing)');
It works for anything that Matlab can display:
>> anything2string(3)
ans =
3
>> anything2string(Inf)
ans =
Inf
>> anything2string('hi')
ans =
hi
>> anything2string(1:4)
ans =
1 2 3 4
It's not quite the same as Python's str, but num2str works with Inf and handles strings as input.
num2str(Inf)
ans = Inf
num2str('some string')
ans = some string
You could get the exact same string as you see in your command window using evalc (evaluate and capture the result):
x = evalc('disp(y)'); % y could be anything displayable by Matlab!

How can I read a simple txt file in Matlab in the fortran way (i.e. I want to keep reading after the newline)

I have to read the simple text file I write on the end of this post (it is just a sctructured grid). In fortran it is so easy to do this, you just have to do:
read(fileunit,*)
read(fileunit,*) mc,nc
do j = 1, nc
read (fileunit, *) dummy, dummy, (xcor(j,i), i=1,mc)
enddo
is there an equivalent function in matlab that reads element by element and keeps reading after the newline like in fortran? I could not find it, all the function as fscanf, textscan etc read line by line and then i have to parse each line. Here is the file. thanks for any help A.
Gridfile version 8.675.44
8 3
eta= 1 0.00000000000000000E+00 1.50000000000000000E+02
4.50000000000000000E+02 6.00000000000000000E+02
4.50000000000000000E+02 6.00000000000000000E+02
4.50000000000000000E+02 6.00000000000000000E+02
eta= 2 0.00000000000000000E+00 1.50000000000000000E+02
3.00000000000000000E+02 4.50000000000000000E+02
7.50000000000000000E+02 9.00000000000000000E+02
4.50000000000000000E+02 6.00000000000000000E+02
eta= 3 0.00000000000000000E+00 1.50000000000000000E+02
3.00000000000000000E+02 4.50000000000000000E+02
7.50000000000000000E+02 9.00000000000000000E+02
4.50000000000000000E+02 6.00000000000000000E+02
There are many ways to do this, but perhaps you will like the way fscanf works, as in this example. After the file is opened by something like fin = fopen('gridfile.txt') and the header swallowed, you can use fscanf(f, 'x= %d'), and then fscanf(f, '%f'), which will read the entire block. fscanf does not stop at the end of a line if not instructed to do so. Taken together, a solution could look like
fin = fopen('gridfile.txt');
fgetl(fin);
% read data counts
cnt = fscanf(fin, '%d %d', 2);
mc = cnt(1);
nc = cnt(2);
xcor = zeros(nc, mc);
% read blocks of data
for j = 1 : nc
fscanf(fin, '%s %s', 2);
xcor(j, :) = fscanf(fin, '%f', mc)';
end
fclose(fin);
fscanf keeps matching the format specifier as long as possible, and returns only when no further consecutive matches can be found. The above examples uses this in two places. First, to extract the dimensionality cnt, in your example (8, 3), and second, to read eight consecutive floating point values per record.

MATLAB console output

Say I had a variable called "x" and x=5.
I would like to do:
disp('x is equal to ' + x +'.');
and have that code print:
x is equal to 5.
This is how I am used to doing things in Java, so their must be a similar way to do this in MATLAB.
Thanks
If you want to use disp, you can construct the string to display like so:
disp(['x is equal to ',num2str(x),'.'])
I personally prefer to use fprintf, which would use the following syntax (and gives me some control over formatting of the value of x)
fprintf('x is equal to %6.2f.\n',x);
You can, of course, also supply x as string, and get the same output as disp (give or take a few line breaks).
fprintf('x is equal to %s\n',num2str(x))
printing out a few scalar variables in matlab is a mess (see answer above). having a function like this in your search path helps:
function echo(varargin)
str = '';
for k=1:length(varargin)
str = [str ' ' num2str(varargin{k})];
end
disp(str)
just nest a sprintf() inside the disp().
disp(sprintf("X is equal to %d.",x));