I have just a little to add to @Szabolcs' answer, and this seemed an appropriate place rather than a separate Q&A. Since there is a theorem involved (see below), it should be pointed out that this theorem tends to fail to hold in Mathematica. Well, wait, it's a theorem: it must true! Then let us say that it tends to be difficult to apply this theorem to Mathematica code.
As pointed out already, a / b
is short for Times[a, Power[b, -1]]
, which involves two roundings. It also happens that sometimes Divide[a, b]
evaluates to Times[a, Power[b, -1]]
, for example, symbolically and in some cases presented below. In fact, I sometimes find it difficult to get Divide
to Divide[]
and not to multiply by the reciprocal.
There's a remarkable theorem for base-10 addicts due to W. Kahan (see David Goldberg, "What every computer scientist should know about floating-point arithmetic", ACM Computing Surveys, Vol 23, No 1, March 1991, 5–48, Theorem 7):
Suppose $x$ is an integer with $|x|<2^{p-1}$ in a binary floating-point format with precision $p$ bits and floating-point operations that are exactly rounded. Then if $n$ is an integer of the form $n = 2^i + 2^j$ (such as $n=10$), then $(x \,/\, n) \times n = x$ (exactly).
Unsurprisingly, this theorem does not apply if $x \,/\, n$ is computed as Times[x, Power[n, -1]]
.
According to the theorem, all the totals below should be zero.
(*** N.B.: Vectorized FAILS ***)
(Divide[#, 10.]*10. - #) &[
N@Range@32] // Unitize // Total
(*** WORKS on short packed arrays? ***)
(Divide[#, 10.]*10. - #) &[
N@Range@31] // Unitize // Total
(*** No, wait! Vectorized sometimes WORKS!! ***)
(Divide[#, ConstantArray[10., Length@#]]*10. - #) &[
N@Range@32] // Unitize // Total
(*** WORKS on unpacked arrays? ***)
(Divide[#, 10.]*10. - #) &[
N@Range@32 // Developer`FromPackedArray] // Unitize // Total
(*** Nope, FAILS on large arrays! Maybe gets repacked/compiled? ***)
(Divide[#, 10.]*10. - #) &[
N@Range@251 // Developer`FromPackedArray] // Unitize // Total
(*
9 -- 32 numbers: FAILURE
0 -- 31 numbers: SUCCESS
0 -- Double array: SUCCESS
0 -- Unpacked 32: SUCCESS
65 -- Unpacked >250: FAILURE
*)
Perhaps we're seeing some patterns, but the Trace
the command changes the computational environment and results:
ReleaseHold@Last@Trace[Divide[#, 10.]] - Divide[#, 10.] &[
N@Range@32] // Unitize // Total
(* 11 *)
ReleaseHold@Last@Trace[Divide[#, 10.]*10. - #] &@[
N@Range@32] // Unitize // Total
(* 0 *)
These are not consistent with the first result, although both are computed from the same input N@Range@32
. Apparently Trace
prevents Divide
from converting to multiplying by the reciprocal.
On my system (Intel i7/Macbook Pro), we can address the two failures with system options:
SetSystemOptions[
"ParallelOptions" -> (* originally {128, 32, 32} *)
"VectorVendorLengthThresholds" -> {128, 32, Infinity}]
SetSystemOptions[
"PackedArrayOptions" -> (* originally 250 *)
"ListableAutoPackLength" -> Infinity]
It's been noted elsewhere that Divide[a, b]
is faster than a/b
:
First@AbsoluteTiming[Divide[##]] & @@ RandomReal[1, {2, 10^7}]
(* 0.026914 *)
First@AbsoluteTiming[#1/#2] & @@ RandomReal[1, {2, 10^7}]
(* 0.05279 *)
It seems completely consistent with Divide[a, b]
having half as many FLOPS as a/b
. It's too bad there isn't a reliable way to implement it.
Addendum: A criticism of x * (1/y)
.
The recently well-publicized flaw in floating-point division on the Pentium chip has prompted the question whether $\tt x/y$ may simply be replaced by $\tt x*(1/y)$. This note focuses on the example $y=x$ illustrating that this proposed fix would no longer conform to the IEEE standard. --- Edelman, "When is $x*(1/x)\ne1$" (1994)
/
in a numerical expression is a bug./
should always give the most accurate result on machine numbers, but it currently does not. As it stands, I now have to useDivide
wherever/
used to be in order to get the correct result. That is a giant pain, since I suddenly lose 800 years of development of a convenient mathematical notation. $\endgroup$a*b/c*d*e/f*g
would no longer parse to something with head ofTimes
and seven arguments. $\endgroup$