0

I came to know about 'signed zeroes' only now as I am trying to deal with complex numbers. Here is the problem:

PROGRAM sZ
       IMPLICIT NONE
       REAL(KIND(0.d0)),PARAMETER :: r=0.2
       COMPLEX(KIND(0.d0)) :: c0
       c0=(0.0,0.5); print*,sqrt(c0**2-r)
       c0=(-0.0,0.5); print*,sqrt(c0**2-r)
       END PROGRAM sZ

The sign of the imaginary part changes.

(0.0000000000000000,0.67082039547127081)
(0.0000000000000000,-0.67082039547127081)

Any instructions/suggestions to resolve this issue are very much welcome.

Ian Bush
  • 6,996
  • 1
  • 21
  • 27
Gyrtle
  • 31
  • 5
  • 2
    Why is this an issue? They're both valid square roots of `(0.0,0.5)`. Without knowing why this is a problem it's hard to suggest solutions. – veryreverie Jan 14 '22 at 09:00
  • This question has been asked elsewhere and answered. It should be closed. – steve Jan 14 '22 at 17:35
  • Sorry for crossposting. However, a few more points and suggestions came out that and I would like to share it, in case someone is interested. https://fortran-lang.discourse.group/t/issue-with-signed-zero/2585 – Gyrtle Jan 15 '22 at 21:59

3 Answers3

3

The values you see follow from the intermediate step and the specification of the sqrt intrinsic.

The final result depends on the value of the argument to sqrt: as the result has real part the sign of the result's imaginary part is that of the sign of the argument's imaginary part.

What are the signs of the imaginary parts of the two arguments? The imaginary parts of (0.,0.5) and (-0.,0.5) squared are positive in the first case and negative in the second (with the expected implementation of signed zero). Subtracting a real entity does not affect the sign of the imaginary part of the object which becomes the argument to sqrt.


At one level, you are getting the correct result so the only thing to resolve is your expectation of getting the same result. However, consider

  implicit none

  complex :: z1=(-1.,0.), z2=(-1.,-0.)

  print*, z1==z2, SQRT(z1)==SQRT(z2)
end program

It is easy to see why this is confusing even if correct. If we don't want this to happen, we can force any zero imaginary part to be of one particular sign (say, non-negative):

! Force imaginary part away from -0.
if (z%Im==0.) z%Im = 0.

(A comment for this is very necessary.)

We can access and set the imaginary part of a complex variable using the z%Im designator, but this is not possible for general expressions. You may want to create a function for more general use:

  implicit none

  complex :: z1=(-1.,0.), z2=(-1.,-0.)

  print*, z1==z2, SQRT(z1)==SQRT(z2)
  print*, z1==z2, SQRT(design_0imag(z1))==SQRT(design_0imag(z2))

contains

  pure complex function design_0imag(z) result(zp)
    complex, intent(in) :: z
    zp = z
    if (zp%Im==0.) zp%Im = 0.
  end function design_0imag

end program

As shown by other answers, there are several ways to implement logic replacing -0 with 0 (leaving other values unchanged). Remember that you experience this only when the result is purely imaginary.

francescalus
  • 30,576
  • 16
  • 61
  • 96
  • Nice. This is very nice. Thank you for the detailed discussion. – Gyrtle Jan 15 '22 at 21:24
  • 2
    Could an optimising compiler cause problems here? Is there a case for using something like `If(zp%Im==ieee_value( Real( 0.0, Kind( zp%Im ) ), ieee_negative_zero ) zp%Im = 0.0` ? (I may well have got this wrong, never used it, but hopefully the idea is clear) – Ian Bush Jan 15 '22 at 23:49
  • @IanBush, that's a good question but not one a comment answer can do justice to. Short answer is: yes, but. Behaviour of my answer, the question, and your comment is not determined by Fortran so any "optimization" is ill-defined. (I'd happily upvote and answer a good question on this topic.) – francescalus Jan 16 '22 at 00:58
  • @IanBush, part of my function relies on: no optimizing Fortran compiler can say `-0./=0.` and still be a Fortran compiler, regardless of IEEE, or kind. – francescalus Jan 16 '22 at 01:01
  • @francescalus Thanks. I'm happy that a compiler can't say `-0.0/=0.0`, but I feel far from qualified to give any justice to the the other parts. – Ian Bush Jan 16 '22 at 06:53
0

you could do something like this, so a signed (negative) zero is just caught and replaced with zero.

PROGRAM sZ
      IMPLICIT NONE

      REAL(KIND(0.d0)),PARAMETER :: r=0.2
      COMPLEX(KIND(0.d0)) :: c0

      REAL(KIND(0.d0)),PARAMETER :: i = -0.0
      REAL(KIND(0.d0)),PARAMETER :: j = 0.5
      REAL(KIND(0.d0)),PARAMETER :: zero = 0.0

      if (i .EQ. 0) then
              c0 = (zero, j); print*,sqrt(c0**2-r)
      else
              c0=(i, j); print*,sqrt(c0**2-r)
      end if

END PROGRAM sZ
ekrall
  • 192
  • 8
0

I have devised one little hack to remove this signed zeros.

PROGRAM sZ
       IMPLICIT NONE
       REAL(KIND(0.d0)),PARAMETER :: r=0.2
       COMPLEX(KIND(0.d0)) :: c0,zero

       zero=(0.0,0.0)
       c0=(0.0,0.5); c0=c0+zero; print*,sqrt(c0**2-r)
       c0=(-0.0,0.5); c0=c0+zero; print*,sqrt(c0**2-r)

       END PROGRAM sZ

Now the results are same. :)

However, I would like to know more on this. When are signed zeroes necessary?

Gyrtle
  • 31
  • 5