Is there an easy way to format numbers to 3 significant figures?

Is there an easy way to format numbers into scientific notation with 3 significant figures?
Such as:
4.53
or
1.03e+09

1 commentaire

round(var,digit,'significant')
where var is the variable of interest and digit is number of significant digits.

Connectez-vous pour commenter.

Réponses (5)

If "3 significant figures" mean before and after the decimal point:
fprintf('%.3g', pi)
3.14
fprintf('%.3g', pi * 1e9)
3.14e+09

4 commentaires

Unfortunately, while the documentation suggests that the precision option with %g will set the number of significant digits, it does not always do it the way we might expect:
fprintf('%.3g', 1.00)
1
This is consistent with the description for %g which is "The more compact form of %e or %f with no trailing zeros"
A more-correct method would be to use the # flag:
fprintf('%#.3g', 1.00)
1.00
The only annoying edge case here is the trailing decimal remains, even if it is not needed:
fprintf('%#.3g', 100)
100.
For what it's worth, this is the same behaviour exhibited by C/C++ (and if I were to guess, Mathworks is most likely just passing the arguments of MATLAB's fprintf to the C implementation).
As far as I can tell, there is no simple usage of *printf in C or MATLAB to correctly print to n sigfigs for all possible values, but %#.3g gets the closest, I think, and is not technically wrong with the trailing decimal with out any digits, but can be confusing when used in a sentence as it looks like a period.
The annoying edge case can be fixed with a regex replacement.
regexprep(sprintf('%#.3g',100),'\.$','')
ans = '100'
The regex ( '.$' ) matches a dot at the end of the string and replaces it with nothing.
Updated with correction given by Walter Roberson below.
.$ matches any character at the end of the string. To match dot specifically use \.$
Thanks for pointing out that oversight, I have amended my original post.

Connectez-vous pour commenter.

Use the round function with two input arguments. If this does not work, tell us which release of MATLAB you're using.
round(pi, 2)
See the documentation on the sprintf (link) function.
A = 6.022140857E+23;
N = sprintf('Avogadro''s number = %9.2E', A)
N =
'Avogadro's number = 6.02E+23'

2 commentaires

Does this mean I have to display as a string and not a number?
I currently have my values in a matrix and convert it into a table.
Star Strider
Star Strider le 30 Jan 2018
Modifié(e) : Star Strider le 28 Avr 2021
‘Does this mean I have to display as a string and not a number?’
To display it, yes.
MATLAB retains full internal precision, so you lose nothing. The format (link) function is the only way you can control the Command Window and tooltip format.
‘I currently have my values in a matrix and convert it into a table.’
If by ‘table’ you intend the table (link) data type, those appear to have their own internal (and unchangable) format. Formatting options are not listed among the ‘Properties’ in a table, although there may be an undocumented way to change it.
—————
EDIT — (28 Apr 2021 at 20:20)
For the last few MATLAB versions, the round function has had a number of new options added to it, one of which is Round Elements to Specified Number of Significant Digits. It is likely worth upgrading to get these and other enhancements to various functions.
.

Connectez-vous pour commenter.

John Olesik
John Olesik le 8 Jan 2019
Strickly speaking neither command gives the proper number of significant digits. The correct result for pi in 2 significant digits would be 3.1 with no 0s to the right of the 1. round(pi,2,'significant') gives 3.1000 which as written has 5 significant digits. If you ignore the 0s then it has the correct number of significant digits. round(pi,2) gives 3.1400 which again has 5 significant digits as written (even though the 0s are not correct). round(pi,2) specifies using two non-zero digits to the right of the decimal point, not two significant digits.

1 commentaire

"round(pi,2,'significant') gives 3.1000 which as written has 5 significant digits"
Strickly speaking you are confusing two related but very different things: the precision of the numeric class (which is fixed and cannot be changed) with how numeric values are displayed (which for binary floating point number is often an approximation of the real binary value, as in all of your example values). Floating point numbers are introduced here (and in thousands of tutorials on the internet):
Those five decimal digits (those nice trailing zeros) are simply caused by the current format setting, and are totally independent of any rounding or anything else you might have done to your values beforehand.
Your claim that "the 0s are not correct" is not correct: for the values 3.1 and 3.14 that you show cannot be stored exactly as binary floating point numbers, so the values that you see displayed are approximations of the values that are actually stored in memory: the trailing zeros correctly represent that binary floating point value as a decimal to the precision shown by the zeros:
>> N = round(100*pi)/100
N = 3.1400
>> fprintf('%.40f\n',N)
3.1400000000000001243449787580175325274467
If you want to see the real values stored by the binary floating point class then download James Tursa's excellent num2strExact.

Connectez-vous pour commenter.

Real User
Real User le 28 Avr 2021
Modifié(e) : Real User le 29 Avr 2021
[Fixed] Use this function to format a number to sig significant digits (if you want fixed format, not exponential, and not loose any accuracy of the integer part, or you want to set the maxlength, or the like). For example,
>> fprintf('%s', str_significant(12.34567, 4))
12.35>> fprintf('%s', str_significant(12387654321.987, 3))
12387654322>> fprintf('%s', str_significant(-0.01289, 3, 0, 6)) % 6 = maxlength of the string
-0.013>> fprintf('%s', str_significant(0.01289, 3, 0, 6))
0.0129>>
See further examples below, including the comparison to %.3f and %.3g.
Note: IF YOU WANT TO ROUND ALSO THE INTEGER PART (when it happens to have too many significant digits), uncomment the line above "Uncomment the above line ..." at the end of the comment section.
function str = str_significant(value, sig, minlength, maxlength)
% str = "sprintf(value)" except for minlength and adjusting the number of decimals.
% if round(value) has at least sig digits, use it.
% else: round to so many decimals that you have sig significant digits
% (OR LESS if maxlength requires). (maxlength is used only for this purpose)
% Always: length(str) >= minlength. (Adds leading spaces if necessary.)
% Uses "fixed format", never "exponential format".
%
% N.B. What you see is correctly rounded, if rounding is used
% (but only decimals are rounded away, as many as necessary).
% maxlength is violated iff the integer part is longer than maxlength.
% Matlab may add rounding errors, e.g., after 17 correct digits, so you only see them if you require too many digits.
%
% EXAMPLES: str_significant(12387654321.987, 3) = '12387654322'.
% str_significant(0.00001238, 3) = '0.0000124'.
% str_significant(0.12387654321, 3) = '0.124'. sprintf('%.3g', 12.387654321) = '12.4'.
% str_significant(-0.01289, 3, 8, 6) = ' -0.013'. % 6 = maxlength of the string, 8 = minlength
% str_significant(0.01289, 3, 0, 6) = '0.0129'. str_significant(-35.2987, 7, 0, 6)) = '-35.30'.
% sprintf('%.3f', 0.00001238) = '0.000'. sprintf('%.3g', 1.238) = '1.24'.
% sprintf('%.3g', 1.238e9) = '1.24e+09'. sprintf('%.3g', 1238345.49) = '1.24e+06'.
% sprintf('%.3g', 0.0001238) = '0.000124'. sprintf('%.3g', 0.00001238) = '1.24e-05'.
%
% % value = round(value, sig, 'significant');
% % Uncomment the above line IF YOU WANT TO ROUND ALSO THE INTEGER PART, when it is too accurate.
if (nargin < 4)
maxlength = 999;
if (nargin < 3)
minlength = 0;
end
end
if (value==0)
str = '0';
return;
end
lenint = length(sprintf('%d', round(abs(value)))); % length of the integer part
lenintm = lenint + (value<0); % -"- plus 1 if minus sign
maxdec = max(maxlength-lenintm-1, 0); % lenint+point+decimals <= maxlength required.
if (value >= 1 || value <= -1)
decimals = max(min(sig-lenint, maxdec), 0); % sig-lenint decimals needed.
else
Nzeros = ceil(-log10(abs(value))) - 1; % # zeros after the decimal point before the first number
decimals = min(maxdec, sig + Nzeros);
end
str = sprintf('%*.*f', minlength, decimals, value);

3 commentaires

Real User
Real User le 28 Avr 2021
Modifié(e) : Real User le 28 Avr 2021
As said in the comments, only the decimals are rounded away to have sig significant figures.
So just round(value) is printed if it has at least sig numbers (it may have more).
If you want to round the integers, too, so as not to exceed sig significant figures, uncomment the line
value = round(value, sig, 'significant');
as said in its comment. Note the "However" below it.
The usual definition of the term "significant digits" include the digits before the decimal point also.
Real User
Real User le 29 Avr 2021
Modifié(e) : Real User le 30 Avr 2021
Yes. If you always want at most sig significant digits (even if it does not save any space), uncomment the line above "Uncomment the above line...". (I now repeated this in capitals in the leading text to avoid people missing this.)
Without uncommenting, the above code rounds down to sig digits only as far as it shortens the output. So you minimize the output by rounding but require at least sig significant digits (unless maxlength forces you to go below that). This is how I needed it to have (to maximize the info but minimize the space), but somebody else might uncomment that line to make the information clearer (but less accurate) even if it does not save any space.

Connectez-vous pour commenter.

Commenté :

le 21 Nov 2024

Community Treasure Hunt

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

Start Hunting!

Translated by