Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

I was suprised that a -0.1*0 yields a negative zero. Here is a more extensive test (perl 5.10.0 or 5.8.9). Why are we getting a negative fp zero from multiplication by a zero? Is it a bug or a (mis)feature?

perl -e 'for (-2.0,-1.9,-1.5,-1.1,-1.0,-0.9,-0.5,-0.1,-0) {$v = $_ * 0; printf("%5s -> %2s,%24.16e\n",$_,$v,$v)}'
-2 -> 0, 0.0000000000000000e+00 -1.9 -> -0, -0.0000000000000000e+00 -1.5 -> -0, -0.0000000000000000e+00 -1.1 -> -0, -0.0000000000000000e+00 -1 -> 0, 0.0000000000000000e+00 -0.9 -> -0, -0.0000000000000000e+00 -0.5 -> -0, -0.0000000000000000e+00 -0.1 -> -0, -0.0000000000000000e+00 0 -> 0, 0.0000000000000000e+00

Replies are listed 'Best First'.
Re: Negative zero from a multiplication by a zero?
by John M. Dlugosz (Monsignor) on Jun 09, 2009 at 16:25 UTC
    Floating point on most CPUs (IEEE_754-2008) has a signed zero. A negative zero typically means underflow from the left; i.e. a number too small to represent but less than true zero. It's useful in computing.
      This is right. The precision that you are asking for has no meaning.
      foreach (-2.0,-1.9,-1.5,-1.1,-1.0,-0.9,-0.5,-0.1,-0) { printf("string=%5s digits=%9f, float2big=%24.16e\n", $_, $_, $_); } __END__ prints: string= -2 digits=-2.000000, float2big=-2.0000000000000000e+000 string= -1.9 digits=-1.900000, float2big=-1.8999999999999999e+000 string= -1.5 digits=-1.500000, float2big=-1.5000000000000000e+000 string= -1.1 digits=-1.100000, float2big=-1.1000000000000001e+000 string= -1 digits=-1.000000, float2big=-1.0000000000000000e+000 string= -0.9 digits=-0.900000, float2big=-9.0000000000000002e-001 string= -0.5 digits=-0.500000, float2big=-5.0000000000000000e-001 string= -0.1 digits=-0.100000, float2big=-1.0000000000000001e-001 string= 0 digits= 0.000000, float2big= 0.0000000000000000e+000
      Update:Well Ooops..... Somehow there is a precision problem. Sorry I couldn't replicate it.
      foreach (-2.0,-1.9,-1.5,-1.1,-1.0,-0.9,-0.5,-0.1,-0) { printf("string=%5s digits=%9f, float2big=%24.16e\n", $_, $_*0, $_*0); } string= -2 digits= 0.000000, float2big= 0.0000000000000000e+000 string= -1.9 digits= 0.000000, float2big= 0.0000000000000000e+000 string= -1.5 digits= 0.000000, float2big= 0.0000000000000000e+000 string= -1.1 digits= 0.000000, float2big= 0.0000000000000000e+000 string= -1 digits= 0.000000, float2big= 0.0000000000000000e+000 string= -0.9 digits= 0.000000, float2big= 0.0000000000000000e+000 string= -0.5 digits= 0.000000, float2big= 0.0000000000000000e+000 string= -0.1 digits= 0.000000, float2big= 0.0000000000000000e+000 string= 0 digits= 0.000000, float2big= 0.0000000000000000e+000
        I've reproduced it on two (out of two) linux boxes, one a 64bit machine.
        $ perl -e 'for (-2.0,-1.9,-1.5,-1.1,-1.0,-0.9,-0.5,-0.1,-0) {$v = $_ * 0; printf("%5s -> %2s,%.100e\n",$_,$v,$v)}' -2 -> 0,0.00000000000000000000000000000000000000000000000000000000 +00000000000000000000000000000000000000000000e+00 -1.9 -> -0,-0.0000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000e+00 -1.5 -> -0,-0.0000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000e+00 -1.1 -> -0,-0.0000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000e+00 -1 -> 0,0.00000000000000000000000000000000000000000000000000000000 +00000000000000000000000000000000000000000000e+00 -0.9 -> -0,-0.0000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000e+00 -0.5 -> -0,-0.0000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000e+00 -0.1 -> -0,-0.0000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000e+00 0 -> 0,0.00000000000000000000000000000000000000000000000000000000 +00000000000000000000000000000000000000000000e+00 $ perl -v This is perl, v5.8.8 built for i486-linux-gnu-thread-multi ... $ uname -a Linux fmdev10 2.6.24-etchnhalf.1-686 #1 SMP Tue Dec 2 07:56:33 UTC 200 +8 i686 GNU/Linux

        (Debian build)

      Yes, but why would $x * 0 result in an underflow? The question stands.
        Because multiplication is sign preserving. The 0 argument might really be something that already underflowed. That is, $x * $y where $y is the canonocal representation of zero. $y might have come from another calculation. You generally don't multiply by a literal zero, since you could just leave the code out. UNLESS you wanted the sign. So that's what you got.
Re: Negative zero from a multiplication by a zero?
by syphilis (Archbishop) on Jun 10, 2009 at 01:24 UTC
    It's a little bit interesting that -2.0, -1.0 and -0.0, when multiplied by zero, are yielding 0 instead of -0.
    It looks, at first glance, like a half-arsed stuff up.

    But the very act of multiplying the NV's -2.0, -1.0 and -0.0 by any whole number sets the IV flags and places 0 in the IV (int) slot. The signedness of the zero is therefore lost. Signedness of zero, is defined only for floating point (float/double) zeroes - so the 0 in the IV slot will always be unsigned.

    The other numbers, being non-integral values, will preserve their NV flags when mutliplied, and the value printed for them will be the value that's stored in the NV (double) slot - thus allowing for the appearance of -0 (when supported by the underlying conditions).

    Cheers,
    Rob
      Interesting. I just figured they were getting stored as IVs, but that's not the case. That means you can't work around the problem by upgrading the vars to NVs since they already are!
      $ perl -MDevel::Peek -e'$x=-2.1; Dump $x; $x*=0; Dump $x' SV = NV(0x5291b8) at 0x504ca0 REFCNT = 1 FLAGS = (NOK,pNOK) NV = -2.1 SV = PVNV(0x507598) at 0x504ca0 REFCNT = 1 FLAGS = (NOK,pNOK) IV = -2 NV = -0 PV = 0 $ perl -MDevel::Peek -e'$x=-2.0; Dump $x; $x*=0; Dump $x' SV = NV(0x5291b8) at 0x504ca0 REFCNT = 1 FLAGS = (NOK,pNOK) NV = -2 SV = PVNV(0x507598) at 0x504ca0 REFCNT = 1 FLAGS = (IOK,pIOK) IV = 0 NV = -2 PV = 0
Re: Negative zero from a multiplication by a zero?
by AnomalousMonk (Archbishop) on Jun 09, 2009 at 16:51 UTC
    Two more examples, from ActiveState 5.8.2 build 808 and Strawberry 5.10.0.5, with identical output from both versions (note slightly greater precision used in  printf statement, and no negative zeros):
    >perl -wMstrict -le "for (-2.0,-1.9,-1.5,-1.1,-1.0,-0.9,-0.5,-0.1,-0) { my $v = $_ * 0; printf(qq{%5s -> %2s,%32.24e\n},$_,$v,$v); } " -2 -> 0, 0.000000000000000000000000e+000 -1.9 -> 0, 0.000000000000000000000000e+000 -1.5 -> 0, 0.000000000000000000000000e+000 -1.1 -> 0, 0.000000000000000000000000e+000 -1 -> 0, 0.000000000000000000000000e+000 -0.9 -> 0, 0.000000000000000000000000e+000 -0.5 -> 0, 0.000000000000000000000000e+000 -0.1 -> 0, 0.000000000000000000000000e+000 0 -> 0, 0.000000000000000000000000e+000
      Not all machines and libraries support negative zeroes. Perl lets the underlying layers handle this, so the results won't be the same on all machines.
        I think all the posts here are on the same track...HUGE negative or positive values can yield unpredictable results.