Friday, September 26, 2014

Negative Zero and Complex Number Initialization in MATLAB

Background

Floating point numbers have one bit that is used to designate sign, and this bit applies when the value of the number is zero.  This is called "signed zero".  MATLAB hides this very well in most situations, but it does pop up in certain cases.  Recently this came up in a question on Stack Overflow involving complex numbers initialization.

Example

Below is a demonstration of how one complex number initialization syntax can inadvertently create a signed zero.

Create two complex numbers, a and b.  The intent it that both variables have the value (0, -1).
>> a = 0-j;
>> b = -j;
 Extract the real component and verify that they are 0, and that they are "equal".
>> ra = real(a)
ra =
     0
>> rb = real(b)
rb =
     0
>> ra==0
ans =
     1
>> isequal(ra,rb)
ans =
     1
But it's clear they are not equal with this operation:
>> 1/ra
ans =
   Inf
>> 1/rb
ans =
  -Inf
Interesting.  Why does rb clearly have a sign associated with it even though it is not visisble?



Explanation

Well, as it turns out MATLAB's isequal does not check the sign bit when the value is zero!  In this example, with b we set the sign bit for both the real and imaginary parts when we call -j = -complex(0,1) instead of a = 0-j which is equal to complex(0,-1) (more information at Creating Complex Numbers).  The difference can only be seen by inspecting the data at a bit level.  In MATLAB, it's best to use typecast, which does no conversion of the underlying data:
>> dec2bin(typecast(ra,'uint64'),64)
ans =
0000000000000000000000000000000000000000000000000000000000000000
>> dec2bin(typecast(rb,'uint64'),64)
ans =
1000000000000000000000000000000000000000000000000000000000000000
The 1 leading the second result is bit #63 in the IEEE 754 double precision floating point representation:
IEEE 754 double precision floating point representation
Of course, you can see the same with -0 or -1*0:
>> dec2bin(typecast(-0,'uint64'),64)
ans =
1000000000000000000000000000000000000000000000000000000000000000

Summary

Even though isequal and == can't tell the difference between two numbers with different sign bits, it can be set and stored.  You might not expect this since there's not much reason to worry about the sign of zero unless the sign of Inf or NaN makes a difference to you, but at least it is normally clear when it should be set.  Here's complex number initialization syntax is tricky:  0-j is not equal to -j.  This was a strange one.

2 comments:

Luis Mendo Tomás said...

Very good explanation!

chappjc said...

Thanks, Luis. I'm not planning to re-post my favorite SO posts, but this one seemed especially obscure and interesting to me.