フィルターのクリア

How to fprintf of the array of floating-point numbers

136 ビュー (過去 30 日間)
Lev Vitkin
Lev Vitkin 2018 年 3 月 25 日
コメント済み: Image Analyst 2018 年 3 月 26 日
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

採用された回答

Stephen23
Stephen23 2018 年 3 月 26 日
編集済み: Stephen23 2018 年 3 月 26 日
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 件のコメント
Stephen23
Stephen23 2018 年 3 月 26 日
Well spotted. That change should make it robust against large values without decimal digits.
Image Analyst
Image Analyst 2018 年 3 月 26 日
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.

サインインしてコメントする。

その他の回答 (2 件)

Image Analyst
Image Analyst 2018 年 3 月 26 日
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 件のコメント
Lev Vitkin
Lev Vitkin 2018 年 3 月 26 日
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 2018 年 3 月 26 日
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);

サインインしてコメントする。


Walter Roberson
Walter Roberson 2018 年 3 月 26 日
>> 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 件のコメント
Lev Vitkin
Lev Vitkin 2018 年 3 月 26 日
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 2018 年 3 月 26 日
編集済み: Stephen23 2018 年 3 月 26 日
@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.

サインインしてコメントする。

カテゴリ

Help Center および File ExchangeLogical についてさらに検索

製品

Community Treasure Hunt

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

Start Hunting!

Translated by