How do I output only two numbers after a decimal place even for complex numbers? - matlab

I'm having trouble outputting a number with only 2 decimal spaces after the decimal point.
For example, you divide 46/3 and get 15.3333. How would I change the output to 15.33 if I'm using the display (disp) function?
a = 46;
b = 3;
disp(a/b) % 15.3333 <- this should be displayed as 15.33
Also this should work with complex numbers:
a = 46i;
b = 3;
disp(a/b) % 0.0000 +15.3333i but I need 0.00 +15.33i

For printing complex numbers with two decimal places in both the real and imaginary part I suggest using the fprintf with the Re and Im parts. For example, the following anonymous function implements this 2-decimal printing similar to the default Matlab format:
disp2 = #(Z) fprintf('%.2f%+.2fi\n', real(Z), imag(Z));
Usage example:
a = 10i;
b = 3;
disp2(a/b); % 0.00+3.33i
disp2(-b-a); % -3.00-10.00i
You can also replace the fprintf with sprintf and use the returned string inside a disp() function.
And this is a more complicated version of this function with nicer output:
disp2 = #(z) fprintf('% 5.2f %c% 6.2fi\n', real(z), subsref(sprintf('%+d', imag(z)),struct('type','()','subs',{{1}})), abs(imag(z)));
E.g.
disp2(a/b); % 0.00 + 3.33i
disp2(-b-a); % -3.00 - 10.00i

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

truncate decimal numbers in matlab?

Is there a quick and easy way to truncate a decimal number, say beyond 4 digits, in MATLAB?
round() isn't helping, it's still rounding off. I have to use it in for loop, so the quickest way is appreciated.
Thanks for your inputs.
Here's one method to truncate d digits after the decimal.
val = 1.234567;
d = 4;
val_trunc = fix(val*10^d)/10^d
Result
val_trunc =
1.2345
If you know that val is positive then floor() will work in place of fix().
Yet another option:
x = -3.141592653;
x_trun = x - rem(x,0.0001)
x_trun =
-3.1415
Kudos to gnovice for the update.
In general, for n decimal places:
x_trun = x - rem(x,10^-n)
Truncating is like rounding if you subtract 5 from the decimal after the last you want to keep.
So, to truncate x to n decimal figures use
round(x - sign(x)*.5/10^n, n)
(Thanks to #gnovice for noticing the need of sign(x) in order to deal with negative numbers.)
For example,
format long
x = 3.141592653589793;
for n = 2:5
result = round(x - sign(x)*.5/10^n, n);
disp(result)
end
gives
3.140000000000000
3.141000000000000
3.141500000000000
3.141590000000000
As you asked for the fastest method, I've put together a quick benchmark of the top 3 truncation methods currently answered here. Please see the code below. I increased the size of the x vector to be rounded, using the timeit function for timing.
function benchie()
% Set up iteration variables
K = 17; n = 4; T = zeros(K,3);
for k = 1:K
x = rand(2^k,1);
% Define the three truncation functions
LuisRound = #() round(x - 0.5/10^n, n);
JodagFix = #() fix(x*10^n)/10^n;
InfoRem = #() x - rem(x,10^-n);
% Time each function
T(k,1) = timeit(LuisRound);
T(k,2) = timeit(JodagFix);
T(k,3) = timeit(InfoRem);
end
% Plot results
figure
plot(2.^(1:K), T); legend('LuisRound', 'JodagFix', 'InfoRem');
grid on; xlabel('number of elements in x'); ylabel('time taken');
end
The resulting plot can be seen here:
According to this test, the fix method suggested by jodag is significantly quicker, so you should use something like this for a custom truncation function to n decimal places:
function y = trunc(x, n)
%% Truncate matrix/scalar x to n decimal places
if nargin < 2; n = 0; end; % default to standard fix behaviour if no n given
y = fix(x*10^n)/10^n; % return value truncated to n decimal places
end
tests:
>> trunc([pi, 10.45, 1.9], 4)
>> ans = [3.1415 10.4500 1.9000]
>> trunc([pi, 10.45, 1.9], 1)
>> ans = [3.1 10.4 1.9]
Use my round2 function:
function result = round2(x,accuracy)
if nargin<2, accuracy=1; end %default accuracy 1 same as 'round'
if accuracy<=0, accuracy=1e-6; end
result=round(x/accuracy)*accuracy;
end
Usual use: round2(3.14159,0.01) But you can also use it to round to every other multipler, for example: round2(number,2) will round to even number, or round2(number,10) etc..

Quantizing Double Type Input to Double Type Output in MATLAB

I'm trying to quantize a set of double type samples with 128 level uniform quantizer and I want my output to be double type aswell. When I try to use "quantize" matlab gives an error: Inputs of class 'double' are not supported. I tried "uencode" as well but its answer was nonsense. I'm quite new to matlab and I've been working on this for hours. Any help appriciated. Thanks
uencode is supposed to give integer results. Thats the point of it. but the key point is that it assumes a symmetric range. going from -x to +x where x is the largest or smallest value in your data set. So if your data is from 0-10 your result looks like nonsense because it quantizes the values on the range -10 to 10.
In any event, you actually want the encoded value and the quantized value. I wrote a simple function to do this. It even has little help instructions (really just type "help ValueQuantizer"). I also made it very flexible so it should work with any data size (assuming you have enough memory) it can be a vector, 2d array, 3d, 4d....etc
here is an example to see how it works. Our number is a Uniform distribution from -0.5 to 3.5 this shows that unlike uencode, my function works with nonsymmetric data, and that it works with negative values
a = 4*rand(2,4,2) - .5
[encoded_vals, quant_values] = ValueQuantizer(a, 3)
produces
a(:,:,1) =
0.6041 2.1204 -0.0240 3.3390
2.2188 0.1504 1.4935 0.8615
a(:,:,2) =
1.8411 2.5051 1.5238 3.0636
0.3952 0.5204 2.2963 3.3372
encoded_vals(:,:,1) =
1 4 0 7
5 0 3 2
encoded_vals(:,:,2) =
4 5 3 6
1 1 5 7
quant_values(:,:,1) =
0.4564 1.8977 -0.0240 3.3390
2.3781 -0.0240 1.4173 0.9368
quant_values(:,:,2) =
1.8977 2.3781 1.4173 2.8585
0.4564 0.4564 2.3781 3.3390
so you can see it returns the encoded values as integers (just like uencode but without the weird symmetric assumption). Unlike uencode, this just returns everything as doubles rather than converting to uint8/16/32. The important part is it also returns the quantized values, which is what you wanted
here is the function
function [encoded_vals, quant_values] = ValueQuantizer(U, N)
% ValueQuantizer uniformly quantizes and encodes the input into N-bits
% it then returns the unsigned integer encoded values and the actual
% quantized values
%
% encoded_vals = ValueQuantizer(U,N) uniformly quantizes and encodes data
% in U. The output range is integer values in the range [0 2^N-1]
%
% [encoded_vals, quant_values] = ValueQuantizer(U, N) uniformly quantizes
% and encodes data in U. encoded_vals range is integer values [0 2^N-1]
% quant_values shows the original data U converted to the quantized level
% representing the number
if (N<2)
disp('N is out of range. N must be > 2')
return;
end
quant_values = double(U(:));
max_val = max(quant_values);
min_val = min(quant_values);
%quantizes the data
quanta_size = (max_val-min_val) / (2^N -1);
quant_values = (quant_values-min_val) ./ quanta_size;
%reshapes the data
quant_values = reshape(quant_values, size(U));
encoded_vals = round(quant_values);
%returns the original numbers in their new quantized form
quant_values = (encoded_vals .* quanta_size) + min_val;
end
As far as I can tell this should always work, but I haven't done extensive testing, good luck

Matlab nchoosek got difference answer using int64 and sym

This is a question about the function nchoosek in Matlab.
I want to find nchoosek(54,25), which is the same as 54C25. Since the answer is about 10^15, I originally use int64. However the answer is wrong with respect to the symbolic one.
Input:
nchoosek(int64(54),int64(25))
nchoosek(sym(54),sym(25))
Output:
1683191473897753
1683191473897752
You can see that they differ by one. This is not really an urgent problem since I now use sym. However can someone tell me why this happens?
EDIT:
I am using R2013a.
I take a look at the nchoosek.m, and find that if the input are in int64, the code can be simplified into
function c = nchoosek2(v,k)
n = v; % rename v to be n. the algorithm is more readable this way.
classOut = 'int64';
nd = double(n);
kd = double(k);
nums = (nd-kd+1):nd;
dens = 1:kd;
nums = nums./dens; %%
c = round(prod(nums));
c = cast(c,classOut);
end
However, the outcome of int64(prod(nums./dens)) is different from prod(sym(nums)./sym(dens)) for me. Is this the same for everyone?
I don't have this problem on R2014a:
Numeric
>> n = int64(54);
>> k = int64(25);
>> nchoosek(n,k)
ans =
1683191473897752 % class(ans) == int64
Symbolic
>> nn = sym(n);
>> kk = sym(k);
>> nchoosek(nn,kk)
ans =
1683191473897752 % class(ans) == sym
% N!/((N-K)! K!)
>> factorial(nn) / (factorial(nn-kk) * factorial(kk))
ans =
1683191473897752 % class(ans) == sym
If you check the source code of the function edit nchoosek.m, you'll see it specifically handles the case of 64-bit integers using a separate algorithm. I won't reproduce the code here, but here are the highlights:
function c = nchoosek(v,k)
...
if int64type
% For 64-bit integers, use an algorithm that avoids
% converting to doubles
c = binCoef(n,k,classOut);
else
% Do the computation in doubles.
...
end
....
end
function c = binCoef(n,k,classOut)
% For integers, compute N!/((N-K)! K!) using prime factor cancellations
...
end
In 2013a this can be reproduced...
There is as #Amro shows a special case in nchoosek for classOut of int64 or unit64,
however in 2013a this is only applied when the answer is between
flintmax (with no argument) and
double(intmax(classOut)) + 2*eps(double(intmax(classOut)))
which for int64 gives 9007199254740992 & 9223372036854775808, which the solution does not lie between...
If the solution had fallen between these values it would be recalculated using the subfunction binCoef
for which the help states: For integers, compute N!/((N-K)! M!) using prime factor cancellations
The binCoef function would have produced the right answer for the given int64 inputs
In 2013a with these inputs binCoef is not called
Instead the "default" pascals triangle method is used in which:
Inputs are cast to double
The product of the vector ((n-k+1):n)./(1:k) is taken
this vector contains k double representations of fractions.
So what we have is almost certainly floating point error.
What can be done?
Two options I can see;
Make your own function based on the code in binCoef,
Modify nchoosek and remove && c >= flintmax from line 81
Removing this expression will force Matlab to use the more accurate integer based calculation for inputs of int64 and uint64 for any values within their precision. This will be slightly slower but will avoid floating point errors, which are rightfully unexpected when working with integer types.
Option one - should be fairly straight forward...
Option two - I recommend keeping an unchanged backup of the original function, or makeing a copy of the function with the modification and use that instead.

Matlab fprintf to keep significant figures and rightmost zeroes

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'