normalize between -1 > x > 1 (not equal to -1 or 1)

How to normalize values so that they fall within -1 and 1 (but no values should be equal either -1 or 1). Thank you.

 Accepted Answer

You can't do this. Not effectively. Ok, let me say that you cannot do this well. Strict inequalities are not easily used when working with floating point numbers. You should essentially never test for something exactly equal to a number anyway in floating point arithmetic.
So I don't know what you are trying to do. But it is a bad idea in general. Have I convinced you?
Sigh, since I know someone will give you an answer anyway, I might as well offer a decent one, something that should work. Are you asking for a linear normalization? Thus a linear shift and scale of the vector elements?
x= randn(1,100);
format long g
min(x)
ans =
-2.0648600364807
max(x)
ans =
3.56986777687433
Now, how to normalize x as you apparently wish. This would normalize a vector to +/- 1. But floating point arithmetic being involved, there is no assurance that a strict inequality will exist. In fact, it might even result in something less than -1 or greater than +1 by some amount on the order of eps.
xnorm = (x - min(x))/(max(x) - min(x))*2 - 1;
min(xnorm) == -1
ans =
logical
1
max(xnorm) == 1
ans =
logical
1
In fact, in this case, it hit +/-1 on the nose. To try to yield a strict inequality, I'll do one extra step.
xnorm = ((x - min(x))/(max(x) - min(x))*2 - 1)*(1-eps);
This time, I multiplied by 1-eps. That contracts the result by just enough that the min and max elements will be closer to zero by the smallest amount possible.
min(xnorm) == -1
ans =
logical
0
max(xnorm) == 1
ans =
logical
0
min(xnorm) +1
ans =
2.22044604925031e-16
max(xnorm) - 1
ans =
-2.22044604925031e-16
So the min is just above -1 by eps. The max is just below 1 by eps. All is good in the world, at least for today. To safer, I might perhaps have chosen to contract things by a factor of (1-2*eps) instead of (1-eps).
Regardless, trying to work with strict inequalities like this will lead you into trouble one day. One day, not far into the future we may see an anguished question from you, asking why your code does not work properly.

11 Comments

Thanks, this is an in-depth answer. I wanted to save an audio file without clipping (this is, avoiding values == abs(-1)). So I wanted to normalize the audio chanells to a bit below 1 to prevent them from clipping, if it makes sense.
You can always control the amount of contraction that I did. I multiplied by (1-eps). That contracted the limits from +/-1 to +/1 (1-eps).
So if you wanted to contract by one part in 1000 instead, then multiply by (1-0.001) = 0.999.
Thank you, although I don't really understand what you did here: xnorm = (x - min(x))/(max(x) - min(x))*2 - 1;
(x - min(x))/(max(x) - min(x)) is the percentage of the way x is between the min and the max. So this ranges from 0 to 1. Then multiplying by 2 and subtracting 1 will make it range from -1 to +1 instead of 0-1.
When you don't follow something, break it down. Work backwards. Start on the inside, and work out.
0. The vector x lives in the interval: [min(x),max(x)]. That seems a tautology of sorts.
1. x - min(x) does what?
It translates the data so that it now lives in the interval [0,max(x) - min(x)]
2. Now, if you divide by (max(x)-min(x)), it lives in the interval [0,1].
3. Multiply by 2, subtract 1?
That remaps the interval [0,1] into [-1,1]. But, remember that you wanted something just slightly inside. So the final solution I offered was:
xnorm = ((x - min(x))/(max(x) - min(x))*2 - 1) * (1-eps);
That extra (1-eps) on there is a contraction of everything towards zero. Positive numbers get just slightly smaller. Negative numbers also get smaller, in an absolute sense, so also towards zero.
Current versions of audiowrite() accept -1 <= y <= 1
The older wavwrite() required -1 <= y < 1 if the output was 8, 16, or 24 bits
Note that John D'Errico's code normalizes mean(x) to 0. If the data you have is signed but potentially outside the range -1 < x < 1 then you might not be wanting to translate the mean to 0. In such a case,
x ./ max(abs(x))
would normalize to -1 <= x <= 1 without changing the mean, and one of the two sides might not reach as far as 1. You can multiply by (1-eps) or (1-2*eps) afterwards if you need to avoid +/- 1.0 exactly.
Nuchto
Nuchto on 30 Nov 2017
Edited: Nuchto on 30 Nov 2017
@John D'Errico, thanks! Clear way to understand it!
@Walter, thank you for the reply: "audiowrite" accepts values greater than 1 if the bitdepth is adjusted to more than 16 bits. But then I can't be sure the audio will be reproduced in a proper system that can handle that bit depth (if I understand correctly). Audiowrite just gives you a warning that the audio will be clipped. Regarding your last message, I don't understand what you mean by not wanting to have mean = 0 when the data is signed.
audiwrite() accepts exactly +/- 1.
The older wavwrite() accepted exactly -1 but not exactly +1 for bit depths 8, 16, or 24, but did accept _exactly +1 for bit depth 32.
"I don't understand what you mean by not wanting to have mean = 0"
Suppose your data happened to be in the range -50 to +25. John's code would normalize that to -1*(1-eps) to +1*(1-eps) as a linear mapping, so -50 would map to -1*(1-eps) and +25 would map to +1*(1-eps) and everything would be linear between. The 0 point of the result would be half way between the min and the max, which would correspond to -12.5 in the original data.
Sometimes that kind of linear mapping is what you want, but sometimes it is important that the sign of the original data be preserved. If you take max(abs()) of this -50 to +25 range, then that would be 50; divide the data by 50 to get -1 exactly to +1/2 exactly; multiply by (1-eps) to get -1*(1-eps) to +1/2*(1-eps) . Now that fits within (-1, +1) exclusive but preserves the sign of the data.
@John D'Errico: One question: in your answer https://www.mathworks.com/matlabcentral/answers/369292-normalize-between-1-x-1-not-equal-to-1-or-1#comment_509253, you said that the last step of multiplying by (1-eps) contracts the restful by that amount. I wonder if one could just subtract eps from the resulting vector instead of multiplying. It feels more intuitive to me. Let me know whether this is wrong. Thank you!
Consider the signal that is exactly -1 . If you subtract eps then you get -1-eps which is less than -1 and so would be outside the range.
You could do a substitution on two values,
mapped_signal(mapped_signal == -1) = -1+eps;
mapped_signal(mapped_signal == 1) = 1-eps;
Another way of writing this would be
mapped_signal = min( max(mapped_signal, -1+eps), 1-eps );
This is not mathematically linear but it might be acceptable for your purposes.
But the mathematically linear version is easy to write
mapped_signal = mapped_signal * (1-eps);

Sign in to comment.

More Answers (0)

Tags

Community Treasure Hunt

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

Start Hunting!