The field width parameter of sprintf and fprintf (e.g., "n" in "sprintf('%nf',1234)") specifies the minimum width of the field. This means that shorter numbers will be padded but longer numbers will be allowed to extend beyond the specified field width when this is necessary to maintain the specified precision. I am looking for a way to specify the maximum field width such that width supersedes precision.
For example, I would want 1234.56, 1234.56789, and 1.2345 to have the width of 8 characters (including the decimal) and therefore print as 1234.560, 1234.567, and 1.234500, respectively.
Here is a similar question, but without a MATLAB-specific solution
Specifying maximum printf field width for numbers (truncating if necessary)?
I feel like someone has to have encountered this problem before, but I was unable to locate anything relevant. Please provide a link if a similar question exists. Thanks
You can use floor(log10()) to determine how many non floating digits you have:
X = [1234.56, 1234.56789, 1.2345]
n = 8 % number of total digits.
% We create an array of string containing the format:
%[ '%.4f\n'
% '%.4f\n'
% '%.7f\n' ]
form = strcat('%.',num2str(n-1-floor(real(log10(X))).'),'f\n')
% We concatenate this array into one single string and we print the result:
fprintf(reshape(form.',1,[]),X)
Or you can use a for loop (using the same logic):
for x = X
form = strcat('%.',num2str(7-floor(real(log10(x)))),'f\n');
fprintf(form,x)
end
And we obtain:
1234.5600
1234.5679
1.2345000
Edit:
I use floor(log10()) instead of ceil(log10()) so the code won't fail if a number is part of the set 10^n (1,10,100...).
And now this snippet can deal with negative floating number since I only take the real part of the log10 output.
if you don't mind truncating instead of rounding the hidden digits, try
S = num2str( X(:), '%-10.9f'); % creates a left-justified character-array
fprintf( '%s\n', string( S(:,1:7) ) )
(X would need to be checked for values too big.)
Related
I need to batch-process text files of mixed units, i.e. integer ratios and floating-point numbers (which are scaled logarithmic approximations of unknown rational or irrational numbers). How can Matlab detect which input is which? Would scanning for a '.' or '/' character be best?
252.63
4/3
757.89
2/1
In this example I recognize that the numbers represent values in increasing order (but in mixed units, which is typical in my field of study), and I would process 252.63 and 757.89 differently from 4/3 and 2/1.
I haven't found a function in Matlab like isa(x, 'rat') where x is any one of the lines in the above list, and 'rat' would be ratio.
Matlab can search strings for specific characters pretty trivially.
slashmask = str == '/'; % returns false for every character in str that's not a slash, and true for every one that is.
slashdetected = any(slashmask); % returns false if no character is a slash.
if all you need to do is take the ratio and evaluate it and then use it in the same way as the floats, you could just simply use the "eval" function to retrieve the float equivalent.
Thank you for your tips. With your help I settled on this (per line of data file):
x = fgetl(fileId);
if isnan(str2double(x)) == true
% Interpret string as ratio number
x = str2num(x);
% then convert to musical cents,
s(i) = log(x) / log(2) * 1200;
else
% convert string to float, already in cents.
s(i) = str2double(x);
end
I have made this scripts that calculates the frequency of a given dataset, but matlab is not precise enough, is it possible to make matlab read in more accurat numbers and not cut off the numbers? I want it to use 8 digits (0.12345678) instead of 4 (0.1234) that is does now
fid = fopen('forceCoeffs.dat','rt');
A = textscan(fid, '%f%f%f%f%f%f', 'HeaderLines',9,'Collect', 9);
A = A{1};
fclose(fid);
t = A(:,1);
Fs = 1/(A(1,1));
x = A(:,2)
x = detrend(x,0);
xdft = fft(x);
freq = 0:Fs/length(x):Fs/2;
xdft = xdft(1:length(x)/2+1);
plot(freq,abs(xdft));
[~,I] = max(abs(xdft));
fprintf('Maximum occurs at %d Hz.\n',freq(I));
File: https://drive.google.com/file/d/0B9CEsYCSSZUSb1JmcHRkbFdWYUU/view?usp=sharing
Thank you for including the forceCoeffs.dat file as it allowed me to run your code. Here is an explanation of what you are seeing.
First I want to point out that MATLAB is not rounding anything. You can check the data type of A to ensure you have enough precision.
>> class(A)
ans =
double
And since you are reading in the file using %f for each column, MATLAB will use all the bits provided by the double type. Ok, now take a look at the contents of your file. The first column has only 2 decimals of precision at most.
0.05 -7.013874e-09 1.410717e+02 -6.688450e-02 -3.344226e-02 -3.344224e-02
...
349.95 -1.189524e-03 1.381022e+00 -2.523909e-01 -1.273850e-01 -1.250059e-01
350 -1.423947e-03 1.380908e+00 -2.471767e-01 -1.250123e-01 -1.221644e-01
Since no more is needed MATLAB only prints four decimal places when you look at the variable in the variable explorer. Try looking at one of the other columns to see what I am talking about. I commented out the A = A{1} part of your code and looked at the second column. When clicking on the number you see the full precision.
You can use a long type to display 16 digits
To get more than 4 digits precision, you can use
format long
However, to get exactly 8 digits, you need to round it. If your number is a then let use:
format long
round(1e8*a)*1e-8
I want to calculate the percentage of accuracy. I have the code below. But it give unexpected output like this "The accuracy is 2.843137e+01x37".
While expected result is "The accuracy is 28.43%"
y %Amount of correct data
j %Amount of all data
a = 'The accuracy is %dx%d.';
percent = '%.0f%%';
format short
acc = 100 * double(y/j);
sprintf (a,acc)
How to fix it?
Any help would be so much appreciated.
Thank you.
You almost have what you expected, just put it together the right way.
The correct format specifier for 28.43% is %.2f%%. This gives you two digits after the decimal point and adds the %-sign at the end. You have that defined in the variable percent, except that .0 should be .2 for two digits as you have written in the expected result. If you look closely, you'll realize that percent is never used.
Let's come to the conclusion. Change the format specifier to the following:
a = 'The accuracy is %.2f%%';
That's all you need to do. The line defining percent can be omitted as well as format short unless you need this for something later on.
Something important regarding the cast to double: What you currently have just casts the result. If necessary, do the cast individually to y and/or j before the division. Probably you don't need any casting in your case.
The whole code with an assumption for y and j is:
y = 28.43137; %// Amount of correct data
j = 100; %// Amount of all data
a = 'The accuracy is %.2f%%';
acc = 100 * (y/j); %// no cast
% acc = 100 * (double(y)/double(j)); %// with cast
sprintf(a,acc);
Output:
ans =
The accuracy is 28.43%
Try,
a = 'The accuracy is %f.';
acc = 100 * double(y/j);
sprintf (a,acc)
Here is a very specific example
>> S = num2str(12345,'%6.0e')
S =
1e+04
and that's just great since I want only my first digit and an exponential notation. However I also want to add leading zeros to the exponent in order to fill the width, but I cannot quite find the way to get the following...
1e+004
Meanwhile it's very straighforward to pad the significant digits with leading zeros
>> S = num2str(12345,'%06.0e')
S =
01e+04
So is there an appropriate formatting for what I want? Or a trick to accomplish it quickly?
The exponent is always a zero-padded two-digit value. To add, say, two zeros you can use
regexprep(num2str(12345, '%6.0e'), '\+', '\+00')
and achieve
ans =
1e+0004
Edit: To cover negative exponents you may use
regexprep(num2str(0.12345, '%6.0e'), '(\+|\-)', '$100')
to achieve
ans =
1e-0001
And, to cover three-digit exponents
regexprep(num2str(1e-100, '%6.0e'), '(\+|\-)(\d{2,3})$', {'$10$2', '$10$2'})
ans =
1e-0100
regexprep(num2str(1e-10, '%6.0e'), '(\+|\-)(\d{2,3})$', {'$10$2', '$10$2'})
ans =
1e-0010
Well, I think you have to edit, what you say you want is wat you get :D
however, if I understood correctly what you are looking for, this function will help you
function printX(x, digits)
format = sprintf('\t%%.%de', digits - 1);
strcat(inputname(1), ' = ', sprintf(format, x))
end
I have a homework problem, I think I did it correctly but need to make sure 100%. Can anyone check for me, before I hand it in?
Thank you.
Question:
Plot the function given by f (x) = 2 sin(2x) − 3 cos(x/2) over the in-
terval [0, 2π] using steps of length .001 (How?). Use the commands max and min to estimate the maximum and minimum points. Include the maximum and minimum points as tick marks on the x-axis and the maximum and minimum values as tick marks on the y-axis.
My code:
x=linspace(0,2*pi,6280);
f=#(x)...
2.*sin(2.*x)-3.*cos(x./2);
%f = #(x)2.*sin(2.*x)-3.*cos(x./2)
g=#(x)...
-1*(2.*sin(2.*x)-3.*cos(x./2));
%g = #(x)-1*(2.*sin(2.*x)-3.*cos(x./2))
[x3,y5]=fminbnd(g,0,2*pi);
%x3 = 4.0968
%y3 = -3.2647
[x2,y4]=fminbnd(f,0,2*pi);
%x2 =2.1864
%y2 = -3.2647
y2=max(f(x));
y3=min(f(x));
plot(x,f(x));
set(gca,'XTick',[x2 x3]);
set(gca,'YTick',[y2 y3]);
(*after I paste this code here, it appeared not as nice as I had it in my program, don't know why)
To create a vector with certain step do
x=0:0.001:2*pi;
Why do you have g(x) function and why are you using fminbind? Use MIN and MAX, return index of those values and find related x values.
[ymin, minindex] = min(f(x));
xmin = x(minindex);
For general case if you have multiple min/max values, index will contain only the first occurrence. Instead you can do:
minindex = find(y==ymin);
Or for real values to avoid precision error:
minindex = find(abs(y-ymin)<=eps);
Also your last statement returns error Values must be monotonically increasing. To avoid it sort your tick values.
set(gca,'XTick',sort([xmin xmax]));
set(gca,'YTick',sort([ymin ymax]));