Matlab fprintf to keep significant figures and rightmost zeroes - matlab

Let's say I have a random variable a=1.2400, and I want to print it with four significant figures, i.e., 1.240. How would I go about that?
fprintf('%0.4g',a) % drops rightmost zero
fprintf('%0.3f',a) % give too many sig figs if a >= 10
Using '%g' drops the important zeros, and with '%f' I can only specify the number of digits after the decimal, which results in too many significant figures if, say, a=10.04. I'm not too familiar with formatting ,but there has to be a simple method. I haven't found it in my searches.

If the values to be printed are all less than 10000, you can do the following. (Sorry, only tested in octave.)
octave:62> a = 1.24
a = 1.2400
octave:63> sprintf('%.*f\n', 3-floor(log10(abs(a))), a)
ans = 1.240
octave:64> a = 234.56
a = 234.56
octave:65> sprintf('%.*f\n', 3-floor(log10(abs(a))), a)
ans = 234.6
For more about the expression floor(log10(abs(a))), see How can I get the exponent of each number in a np.array?
If you don't mind exponential notation, another alternative is to use '%.3e' to always get the same number of signficant digits:
octave:70> a = 1.24
a = 1.2400
octave:71> sprintf('%.3e\n', a)
ans = 1.240e+00
octave:72> a = 234.56
a = 234.56
octave:73> sprintf('%.3e\n', a)
ans = 2.346e+02

I decided to build on the answer by Warren, and I wrote a function that should work for both small and large numbers alike. Perhaps someone will improve on this, but I am pleased with it.
function str=sigfigstr(a,sigfigs)
numdecimal = floor(log10(abs(a)));
if sigfigs - numdecimal < 0
str=sprintf('%.0f',round(a,sigfigs,'significant'));
else
str=strip(sprintf('%.*f\n', sigfigs-floor(log10(abs(a))), a));
end
Here are a few examples if it in action in Matlab
>> sigfigstr(.000012431634,3)
ans = '0.0000124'
>> sigfigstr(26666,3)
ans = '26700'

Related

Matlab reading numbers with higher precision

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

Approximating an integral in MATLAB

I've been trying to implement the following integral in MATLAB
Given a number n, I wrote the code that returns an array with n elements, containing approximations of each integral.
First, I tried this using a 'for' loop and the recurrence relationship on the first line. But from the 20th integral and above the values are completely wrong (correct to 0 significant figures and wrong sign).
The same goes if I use the explicit formula on the second line and two 'for' loops.
As n grows larger, so does the error on the approximations.
So the main issue here is that I haven't found a way to minimize the error as much as possible.
Any ideas? Thanks in advance.
Here is an example of the code and the resulting values, using the second formula:
This integral, for positive values of n, cannot have values >1 or <0
First attempt:
I tried the iterative method and found interesting thing. The approximation may not be true for all n. In fact if I keep track of (n-1)*I(n-1) in each loop I can see
I = zeros(20,3);
I(1,1) = 1-1/exp(1);
for ii = 2:20
I(ii,2) = ii-1;
I(ii,3) = (ii-1)*I(ii-1,1);
I(ii,1) = 1-I(ii,3);
end
There is some problem around n=18. In fact, I18 = 0.05719 and 18*I18 = 1.029 which is larger than 1. I don't think there is any numerical error or number overflow in this procedure.
Second attempt:
To make sure the maths is correct (I verified twice on paper) I used trapz to numerically evaluate the integral, and n=18 didn't cause any problem.
>> x = linspace(0,1,1+1e4);
>> f = #(n) exp(-1)*exp(x).*x.^(n-1);
>> f = #(n) exp(-1)*exp(x).*x.^(n-1)*1e-4;
>> trapz(f(5))
ans =
1.708934160520510e-01
>> trapz(f(17))
ans =
5.571936009790170e-02
>> trapz(f(18))
ans =
5.277113416899408e-02
>>
A closer look is as follows. I18 is slightly different (to the 4th significant digit) between the (stable) numerical method and (unstable) iterative method. 18*I18 is therefore possible to exceed 1.
I = zeros(20,3);
I(1,1) = 1-1/exp(1);
for ii = 2:20
I(ii,2) = ii-1;
I(ii,3) = (ii-1)*I(ii-1,1);
I(ii,1) = 1-I(ii,3);
end
J = zeros(20,3);
x = linspace(0,1,1+1e4);
f = #(n) exp(-1)*exp(x).*x.^(n-1)*1e-4;
J(1,1) = trapz(f(1));
for jj = 2:20
J(jj,1) = trapz(f(jj));
J(jj,2) = jj-1;
J(jj,3) = (jj-1)*J(jj-1,1);
end
I suspect there is an error in each iterative step due to the nature of numerical computations. If the iteration is long, the error propagates and, unfortunately in this case, amplifies rapidly. In order to verify this, I combined the above two methods into a hybrid algo. For most of the time the iterative way is used, and once in a while a numerical integral is evaluated from scratch without relying on previous iterations.
K = zeros(40,4);
K(1,1) = 1-1/exp(1);
for kk = 2:40
K(kk,2) = trapz(f(kk));
K(kk,3) = (kk-1)*K(kk-1,1);
K(kk,4) = 1-K(kk,3);
if mod(kk,5) == 0
K(kk,1) = K(kk,2);
else
K(kk,1) = K(kk,4);
end
end
If the iteration lasts more than 4 steps, error amplification will be large enough to invert the sign, and starts nonrecoverable oscillation.
The code should be able to explain all the data structures. Anyway, let me put some focus here. The second column is the result of trapz, which is the numerical integral done on the non-iterative integration definition of I(n). The third column is (n-1)*I(n-1) and should be always positive and less than 1. The forth column is 1-(n-1)*I(n-1) and should always be positive. The first column is the choice I have made between the trapz result and iterative result, to be the "true" value of I(n).
As can be seen here, in each iteration there is a small error compared to the independent numerical way. The error grows in the 3rd and 4th iteration and finally breaks the thing in its 5th. This is observed around n=25, under the case that I pick the numerical result in every 5 loops since the beginning.
Conclusion: There is nothing wrong with any definition of this integral. However the numerical error when evaluating the expressions is unfortunately aggregating, hence limiting the way you can perform the computation.

How do you program the Monte Carlo Integration method in Matlab?

I am trying to figure out how to right a math based app with Matlab, although I cannot seem to figure out how to get the Monte Carlo method of integration to work. I feel that I do not have algorithm thought out correctly either. As of now, I have something like:
// For the function {integral of cos(x^3)*exp(x^(1/2))+x dx
// from x = 0 to x = 10
ans = 0;
for i = 1:100000000
x = 10*rand;
ans = ans + cos(x^3)*exp(x^(1/2))+x
end
I feel that this is completely wrong because my outputs are hardly even close to what is expected. How should I correctly write this? Or, how should the algorithm for setting this up look?
Two issues:
1) If you look at what you're calculating, "ans" is going to grow as i increases. By putting a huge number of samples, you're just increasing your output value. How could you normalize this value so that it stays relatively the same, regardless of number of samples?
2) Think about what you're trying to calculate here. Your current "ans" is giving you the sum of 100000000 independent random measurements of the output to your function. What does this number represent if you divide by the number of samples you've taken? How could you combine that knowledge with the range of integration in order to get the expected area under the curve?
I managed to solve this with the formula I found here. I ended up using:
ans = 0;
n = 0;
for i:1:100000000
x = 10*rand;
n = n + cos(x^3)*exp(x^(1/2))+x;
end
ans = ((10-0)/100000000)*n

How to add leading zeros in MatLab (number formatting)?

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

Leading zero in Matlab's scientific notation

In Matlab, when printing using e such as fprintf('%10.5e\n', pi/100) one will get the result to be 3.14159e-02. However, what if I want the number to have a leading zero such as 0.314159e-1? How can I manage this with Matlab?
The reason I ask is that I am trying to print to a file which I need to have leading zeros. Otherwise, I would not care.
Thanks.
I don't think there is any clever way to do it:
your_number = pi;
['0.' strrep(sprintf('%10.5e',your_number*10), '.', '')]
>> ans =
0.314159e+01
my solution is pretty crude but this is just to illustrate. You can do it yourself with a small function that will look for the relevant strings in the number, trim it after e, add 0. in the beginning and increse by 1 the exponent at the end, for example:
function b=fooo(a)
b=a;
k1 = strfind(a, '.');
k2 = strfind(a, 'e-');
suffix=num2str(str2num(b(k2+1:k2+3))+1);
b(k2+1:end)=[];
b(k1)=[];
b=['0.' b suffix];
where an input like
ans=fooo(sprintf('%10.5e\n', pi/100))
will yield the answer:
ans =
0.314159e-1