Borrar filtros
Borrar filtros

How to fprintf of the array of floating-point numbers

135 visualizaciones (últimos 30 días)
Lev Vitkin
Lev Vitkin el 25 de Mzo. de 2018
Comentada: Image Analyst el 26 de Mzo. de 2018
I have an array of the floating-point numbers, let say
A = [ 1 2.2 3.33; 44 55.5 6.666];
The number of row/colums as well as the number of digits before and after the decimal point are vary.
I need to find the "format" %X.Yf, to properly fprintf this array:
fprintf(%X.Yf, A).
It is alright to print the array uniformly: all data have the same number of digits after the decimal point:
A =
1.000 2.200 3.330
44.000 55.500 6.666
I can find the maximum number of digits across array's data:
tmp1 = arrayfun(@num2str,A,'UniformOutput', false);
tmp2 = cellfun((@numel),tmp1);
max_length = max(tmp2(:));
max_length defines X+Y in the "format", but I need to find the Y.
Do I over-complicate the implementation?
Any help, please?
Thank you,
Lev

Respuesta aceptada

Stephen23
Stephen23 el 26 de Mzo. de 2018
Editada: Stephen23 el 26 de Mzo. de 2018
Of course there is no correct answer because these are binary floating point values, but here is one solution that gives the "least unexpected" output. It assumes double values and upto 15 significant figures.
arr = [ 1 2.2 3.33; 44 55.5 6.666];
chr = reshape(sprintf('%.14e',abs(arr)),[],numel(arr)).';
sgf = sum(cumsum('0'~=chr(:,[16:-1:3,1]),2)>0,2); % significant figures for each value
pwr = 1+sscanf(chr(:,17:end).','e%d'); % 1 + value magnitude
dgt = max(sgf-pwr); % longest decimal digits
len = max(pwr)+dgt+1; % longest string length
fmt = sprintf(' %%%d.%df',len,dgt); % core format string
fmt = [repmat(fmt,1,size(arr,2)),'\n']; % repeat format string
fprintf(fmt,arr.')
Which prints this:
1.000 2.200 3.330
44.000 55.500 6.666
  3 comentarios
Stephen23
Stephen23 el 26 de Mzo. de 2018
Well spotted. That change should make it robust against large values without decimal digits.
Image Analyst
Image Analyst el 26 de Mzo. de 2018
But I thought he didn't want that. He already had that. He started with 3 decimal places and trailing zeros, but I thought he wanted
A =
1. 2.2 3.33
44. 55.5 6.666
so that the number of decimal places on the right varied, just exactly like he had in the definition of A:
A = [ 1 2.2 3.33; 44 55.5 6.666];
which has no trailing zeros. I thought that when he said he needed Y to vary in %X.Yf that it meant that Y should be 0 for some numbers, 1 for other numbers, and 2 or 3 for other numbers depending on when the zeros started for each number.

Iniciar sesión para comentar.

Más respuestas (2)

Image Analyst
Image Analyst el 26 de Mzo. de 2018
Will this work for you?
A = [ 1 2.2 3.33; 44 55.5 6.666]
for row = 1 : size(A, 1)
fprintf('%7.3f ', A(row, :));
fprintf('\n');
end
You'll see
1.000 2.200 3.330
44.000 55.500 6.666
  2 comentarios
Lev Vitkin
Lev Vitkin el 26 de Mzo. de 2018
Not really... The array above was just an example. For the array's data, the number of digits after the decimal point vary. But in your solution '%7.3f that number is "3" and fixed. I am looking for how to find this number parsing the array...
Image Analyst
Image Analyst el 26 de Mzo. de 2018
It doesn't vary. It always uses the full mantissa length. See Walter's example where you might think that 3.33 has only two decimal places, but it really has many more than that if you print them all out.
If you want to find where you have a certain number of zeros in a row, like 3 or something, and then chop off the number starting at the first zeros, then you'll have to convert the number to a string and look for zeros, and print the string. For example:
str = sprintf('%.999g', 3.33) % 3.3300000000000000710542735760100185871124267578125
threeZeros = strfind(str, '000');
if ~isempty(threeZeros)
str = str(1:threeZeros-1)
end
fprintf('%s\n', str);

Iniciar sesión para comentar.


Walter Roberson
Walter Roberson el 26 de Mzo. de 2018
>> fprintf('%.999g\n', 3.33)
3.3300000000000000710542735760100185871124267578125
that is 49 digits after the decimal place.
All of the entries with value between 2 and 4 will have the same number of digits after the decimal place, except the entries that happen to involve powers of 2 denominators in the fractions, such as 3.3125 (3 + 5/16) as happens for the 44 and 55.5 entries. For entries with value between 1 and 2, add one decimal place (i.e, 50 after the decimal point); for each power of 2 above that subtract 1 decimal place. You can use log2() to calculate the grouping. Basically, floor(log2(value)) + 50 is the number of decimal places after the decimal point, except for the cases that happen to involve M/2^N for some integer M and N.
  2 comentarios
Lev Vitkin
Lev Vitkin el 26 de Mzo. de 2018
Walter, based on one of your answers to similar topic, do you think the approach below works:
tmp = ceil(log10(max(1,abs(A)*(1+eps)))); % get numbers in int parts
lInt = max(tmp(:));
tmp = 1000*rem(A,1); %get decimal parts for short format (*1000)
tmp = ceil(log10(max(1,abs(tmp)*(1+eps)))); % get numbers in fract parts
lFract = max(tmp(:));
fmt = ['%',num2str(lInt +lFract + 2),'.',num2str(lFract),'f']; % +1 for dot, +1 for space
for row = 1 : size(A, 1)
fprintf(fmt, A(row, :));
fprintf('\n');
end
The results is:
1.000 2.200 3.330
44.000 55.500 6.666
Thank you, Lev
Stephen23
Stephen23 el 26 de Mzo. de 2018
Editada: Stephen23 el 26 de Mzo. de 2018
@Lev Vitkin: the code you wrote is limited to 3 decimal places, so what is the point in that? Your code does not resolve your original question "I need to find the "format" %X.Yf... I need to find the Y." You just fixed Y==3, so if you are happy with that then what is the point of the question?
Your code is not very robust, and could be simplified, e.g.:
ceil(log10(max(1,abs(A)*(1+eps))))
better replaced with
1+fix(log10(max(1,abs(A))))
Note also that your code does not calculate Y (of your original question) anyway, because your concept for calculating lFrac is flawed. Try it with intermediate zeros and you will see that it will not work:
>> A = [1.001,22.002,333.003]
>> tmp = 1000*rem(A,1)2.00000 3.00000
>> tmp = ceil(log10(max(1,abs(tmp)*(1+eps))))
>> lFract = max(tmp(:))
lFract = 1
Do those numbers require one decimal digit, or three? Note that my answer correctly identifies that these require three decimal digits:
fmt = '%7.3f %7.3f %7.3f\n'
and prints this:
1.001 22.002 333.003
If you want code that actually adjusts to the precision of the numbers then see my answer.

Iniciar sesión para comentar.

Categorías

Más información sobre Logical en Help Center y File Exchange.

Etiquetas

Productos

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by