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$ Commented Apr 10, 2013 at 9:52
  • $\begingroup$ Related: (5149) $\endgroup$ Commented 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$ Commented 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$ Commented 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$ Commented 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$ Commented 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$ Commented 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$ Commented Apr 10, 2013 at 20:43
  • $\begingroup$ @Szabolcs only what I illustrated here. Is there some reason you cannot use x_ /; x==0? $\endgroup$ Commented 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$ Commented 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$ Commented Apr 9, 2013 at 13:42
  • $\begingroup$ @J.M. Thanks, added! $\endgroup$ Commented 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$

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.