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

3 views (last 30 days)
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'}

Accepted Answer

Stephen23
Stephen23 on 20 Feb 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'}

More Answers (2)

dpb
dpb on 20 Feb 2025
Moved: dpb on 20 Feb 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 Comments
John D'Errico
John D'Errico on 20 Feb 2025
Edited: John D'Errico on 20 Feb 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 on 21 Feb 2025
Edited: Leon on 21 Feb 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.

Sign in to comment.


Leon
Leon on 20 Feb 2025
Edited: Leon on 20 Feb 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 Comments
Stephen23
Stephen23 on 20 Feb 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 on 20 Feb 2025
Edited: Leon on 20 Feb 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.)

Sign in to comment.

Categories

Find more on Environment and Settings in Help Center and File Exchange

Products


Release

R2024a

Community Treasure Hunt

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

Start Hunting!