23
$\begingroup$

I have a function for which 0 is a special case:

f[A___, 0, B___] := 0

But since I am doing numerics, sometimes in the course of things f gets called with 0. instead of 0. Of course I can add the line

f[A___, 0., B___] := 0

But I don't understand why I need to because I don't understand the difference between 0 and 0.. Can anyone illuminate this for me?

(In addition to saving a line of code, or as Jens points out, a couple of characters, I also want to know the difference between them so I don't run into hard-to-debug problems in the future because of something I didn't know about.)

$\endgroup$
2
  • 3
    $\begingroup$ Mathematica knows the differnece: typing 0.-0 gives the definitive answer: 0. $\endgroup$
    – bill s
    Apr 10, 2013 at 9:52
  • $\begingroup$ Related: (5149) $\endgroup$
    – Mr.Wizard
    May 8, 2014 at 16:53

5 Answers 5

28
$\begingroup$

0. is an approximate real number that is very close to zero, while 0 is exactly zero. They aren't the same object, as 0 === 0. is False, and 0. has a head of Real, while 0 has a head of Integer.

If you want to turn 0. into 0, you can use Chop, although that will replace all approximate numbers within some tolerance (by default 10^(-10)) of zero with 0.

$\endgroup$
4
  • 4
    $\begingroup$ +1 - I was writing essentially the same thing while you posted... $\endgroup$
    – Jens
    Jun 18, 2012 at 20:00
  • 3
    $\begingroup$ @Jens, @Pillsy, thanks. I arbitrarily choose this as the correct answer. I have chosen to add the line F[A___, a_?(Equal[#, 0] &), B___] to be safe: I do not want to cut off at less than 10^-10. $\endgroup$
    – Ian Hincks
    Jun 18, 2012 at 20:08
  • 8
    $\begingroup$ @Ian I recommend a_ /; a==0 in place of a_?(Equal[#, 0] &) -- it is cleaner, and usually a bit faster. $\endgroup$
    – Mr.Wizard
    Jun 18, 2012 at 23:24
  • 2
    $\begingroup$ We also have 0.0*I which is a complex number that is approximately zero. Also have 0.0''200 which is a real number known to have absolute value less than 10^-200. I find it inconsistent that Head[Re[0.0 I]] is Real, but Head[Re[1.0''200 I ]] is Integer. $\endgroup$
    – Ted Ersek
    Jun 18, 2012 at 23:45
20
$\begingroup$

To match all sorts of zero, you can use

F[A___, zero_ /; zero==0, B___] := 0

Another possibility which catches more cases, but also matches some non-zero expressions is to use PossibleZeroQ:

F[A___, _?PossibleZeroQ, B___] := 0
$\endgroup$
4
  • $\begingroup$ I favor this method as well. Don't forget the underscore on the LHS though. $\endgroup$
    – Mr.Wizard
    Jun 18, 2012 at 21:45
  • 1
    $\begingroup$ @Mr.Wizard Are you aware of any issues with 0|0.? (I need to use this right now.) $\endgroup$
    – Szabolcs
    Apr 10, 2013 at 20:43
  • $\begingroup$ @Szabolcs only what I illustrated here. Is there some reason you cannot use x_ /; x==0? $\endgroup$
    – Mr.Wizard
    Apr 10, 2013 at 20:46
  • $\begingroup$ @Mr.Wizard Performance concerns (it's for a package so it's difficult to assess the performance impact). Your MatchQ[0.000000000000000000, 0 | 0.] example convinced me, thanks! $\endgroup$
    – Szabolcs
    Apr 10, 2013 at 21:19
16
$\begingroup$

How about this pattern instead:

F[A___, 0 | 0., B___] := 0

Now you get zero in both cases.

Regarding the explanation: You obviously know that the 0. comes about when doing numerics, and the reason is that in numerics we're working with approximate real or complex numbers. These are to be distinguished from exact numbers of which 0 is an example.

Both are numbers,

{NumericQ[0], NumericQ[0.]}

{True, True}

but they're different animals as represented by their Head:

{Head[0], Head[0.]}

{Integer, Real}

Therefore, in defining a pattern test, 0 and 0. show up as non-identical.

$\endgroup$
0
13
$\begingroup$

I think it is worthwhile to include a table of how different methods compare when deciding about possible zero value. My advice is to use PossibleZeroQ, but always make sure to handle/be prepared to all extrema. Let me quote the documentation:

The general problem of determining whether an expression has value zero is undecidable; PossibleZeroQ provides a quick but not always accurate test.

ClearAll[x];
expr = {0, 0., 0.*10^-1, 1.`1 - 1, 0.0000000000000000000, 
        0 + $MachineEpsilon, 0.0001, 0. I, x};

Panel@TableForm[Transpose@{
     FullForm /@ expr,
     Replace[expr, {0 | 0. -> True, _ -> False}, {1}],
     MatchQ[#, 0 | 0.] & /@ expr, (* same as above *)
     (# === 0 \[Or] # === 0.) & /@ expr ,(* 
     not identical to MatchQ above! *)
     # == 0 & /@ expr,
     (NumericQ@# \[And] # == 0) & /@ expr,
     PossibleZeroQ /@ expr (* same as NumericQ && Equal *)
     }, TableHeadings -> {expr, {FullForm, "rule matching", MatchQ, 
      SameQ, Equal, NumericQ && Equal, PossibleZeroQ}}] /. 
 False -> Item[False, Background -> [email protected]]

enter image description here

Note that returning False for PossibleZeroQ[x] is not correct mathematically (e.g. x could be zero), but sometimes one really only wants to know whether an expression has a numeric value of zero or not (and x doesn't have a value).

Please feel free to add further methods/corrections to this table!

$\endgroup$
2
  • $\begingroup$ Maybe consider 1.`1 - 1 as well... $\endgroup$ Apr 9, 2013 at 13:42
  • $\begingroup$ @J.M. Thanks, added! $\endgroup$ Apr 10, 2013 at 9:26
7
$\begingroup$

Sometimes I define my pattern using a tolerance value to consider zero:

tolerance = 10.^-15;
F[A___, zero_ /; Abs[zero] <= tolerance, B___] := 0;

As said before, using Chop is possible too (and you can use the second argument to set the tolerance):

F[A___, zero_ /; Chop[zero, tolerance]==0, B___] := 0;

And if you want to take more cases (like the cases in PossibleZeroQ), you can wrap your zero by Simplify: Chop[Simplify[zero], tolerance]==0.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.