Avoid negative sign with compose() when output is zero, e.g. '-0' or '-0.0'

3 vues (au cours des 30 derniers jours)
Leon
Leon le 20 Fév 2025
Modifié(e) : Leon le 21 Fév 2025
When compose outputs zero, it includes a negative sign if the number was negative, giving, e.g. -0 or -0.0. How can I avoid this? (Other than writing another function that does a string compare for every element output by compose and removes the negative sign if there are only zeros.)
num = -0.04;
compose('%.1f', num)
ans = 1x1 cell array
{'-0.0'}

Réponse acceptée

Stephen23
Stephen23 le 20 Fév 2025
num = -0.04;
txt = compose('%.1f', num)
txt = 1x1 cell array
{'-0.0'}
txt = regexprep(txt,'^-(0+(\.0+)?)$','$1')
txt = 1x1 cell array
{'0.0'}
  1 commentaire
Leon
Leon le 20 Fév 2025
Modifié(e) : Leon le 20 Fév 2025
Thanks! Regular expressions to the rescue.

Connectez-vous pour commenter.

Plus de réponses (2)

dpb
dpb le 20 Fév 2025
Déplacé(e) : dpb le 20 Fév 2025
num = -0.04;
fprintf('%.1f\n', num)
-0.0
This is the normal and expected behavior when the value is rounded because it IS negative.
Changing it would be deceiving to the user who would then presume it was positive.
But, if one is adamant, then to post process just a string substitution is all that is required since the precision of the output is given, one doesn't have to do anything exotic.
  8 commentaires
John D'Errico
John D'Errico le 20 Fév 2025
Modifié(e) : John D'Errico le 20 Fév 2025
No, you completely misunderstand me, but perhaps I could have said it differently.
It is a terribly bad idea for MATLAB to do something like that automatically in any way. Those negative numbers were generated by some process, some computation that apparently did something you do not like. That minus sign should be a flag to you that something happened, that there is something down there, NOT just a zero. I'm sorry though. Ignoring a problem, and pretending it does not exist is something serious.
Far better is to understand where they came from, and fix THAT. Fix the problem, rather than patching the symptom.
However, if you want to remove them anyway, without understanding why they happened, then that is YOUR choice to make. All you need do is essentially round those small values up to zero. And that is trivially done, with a simple test. You can write a function that checks for small negatives, and replaces them with zero, and you can do that without any string parsing. Your choice, and not at all difficult to do.
Leon
Leon le 21 Fév 2025
Modifié(e) : Leon le 21 Fév 2025
@John D'Errico, I say this with a calm tone: I don't know how you can make such strong statements without knowing anything about my data or experiments. Negative numbers are actually good in the specific context I was asking about, and I know exactly "why they happened". I'm not "ignoring a problem and pretending it doesn't exist", I just don't conisder it appropriate to use -0 in the tables I am generating, and -0 == 0 anyway.
I want to be clear that I'm not disagreeing with the default behaviour being the default behaviour; I understand that there are times when the sign would be of interest. However, I still disagree that it would be "a terribly bad idea" for there to be a parameter in Matlab to allow conversion of numbers to strings without having negative zeros. Having an optional parameter something like NoNegZero=true for compose() or a similar function would be useful in a variety of contexts and would have saved this whole discussion. You have your opinion, I have mine.
I was only asking if there was a better way to do it than writing my own function. Applying a tolerance before conversion is a fine solution and computationally faster than regexprep but I would need to parse the required precision from the formatSpec string if I wanted to be able to just create a replacement for the compose function that I'm using in various places; sometimes I want to output with different numbers of decimal places or significant figures. There is no problem though. Either rounding to a tolerance or regexprep after works fine, but I'm using the regexprep solution because it is a drop-in solution that requires less programming time & testing and I don't mind my code taking less than half a second extra to execute due to regexprep being slower than checking a tolerance and clamping to zero before.

Connectez-vous pour commenter.


Leon
Leon le 20 Fév 2025
Modifié(e) : Leon le 20 Fév 2025
Since it seems MATLAB doesn't have a function for this, here's the one I made. I also like Stephen23's idea of using a regular expression:
[Update: Sorry, my code here is clearly broken for more than one decimal place, as Stephen23 has pointed out below]
composezero('%.0f', [0.1 -0.1 0.01 -0.01 3 -3])
ans = 1x6 cell array
{'0'} {'0'} {'0'} {'0'} {'3'} {'-3'}
composezero('%.1f', [0.1 -0.1 0.01 -0.01 3 -3])
ans = 1x6 cell array
{'0.1'} {'0.1'} {'0.0'} {'0.0'} {'3.0'} {'-3.0'}
function c = composezero(formatSpec, A)
c = compose(formatSpec, A);
for ii = 1:numel(A)
val = A(ii);
str = c{ii};
if str(1) == '-' && round(val) == 0
c{ii} = str(2:end);
end
end
end
  3 commentaires
Stephen23
Stephen23 le 20 Fév 2025
Note that the numeric rounding and the compiled text may diverge from each other, which leads to incorrect outputs for many values:
compose('%.2f', -0.025) % correct
ans = 1x1 cell array
{'-0.03'}
zerocompose('%.2f', -0.025) % wrong
ans = 1x1 cell array
{'0.03'}
function c = zerocompose(formatSpec, A)
c = compose(formatSpec, A);
for ii = 1:numel(A)
val = A(ii);
str = c{ii};
if str(1) == '-' && round(val) == 0
c{ii} = str(2:end);
end
end
end
Leon
Leon le 20 Fév 2025
Modifié(e) : Leon le 20 Fév 2025
@Stephen23, thanks for pointing this out. My code is actually wrong for anything more than one decimal place.
@Steven Lord, thanks. That's also a very nice solution, and should be much faster than regexp. What I like about Stephen23's regexp one is that it lets me make a drop-in replacement function, e.g.
function c = compose_u0(formatSpec, A)
c = compose(formatSpec, A);
c = regexprep(c, '^-(0+(\.0+)?)$', '$1');
end
rather than having to specify a tolerance each time, or have the function parse the formatSpec and work it out. This time I'm not having to process so many numbers that the regexp would slow things down noticeably.
And, as Steven23 said, perhaps there could be rounding-type issues, such as if I set the threshold at 0.5 or 0.05 (for nearest integer or one decimal place, respectively), there might be an edge case where compose outputs -0... but the value hadn't been changed to zero. (But maybe that can't happen under this circumstance; I don't know.)

Connectez-vous pour commenter.

Catégories

En savoir plus sur Environment and Settings dans Help Center et File Exchange

Produits


Version

R2024a

Community Treasure Hunt

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

Start Hunting!

Translated by