8
$\begingroup$

Something very strange is happening as I evaluate the Floor function:

(9.2 - 8)/1.2
Floor[(9.2 - 8)/1.2]

returns:

1.

0

so I did the following:

Floor[1]
Floor[1.0]
Floor[1.2/1.2]

returning

1

1

1

Does anybody know what is happening? Am I doing something wrong here? I also tried the following and got the same confusing result:

x = (9.2 - 8)/1.2
Floor[x]
1.

0

and

In[57]:= Floor[(10.2 - 9.0)/1.2]

Out[57]= 0

but

In[59]:= Floor[(10.4 - 9.0)/1.4]

Out[59]= 1

I'm getting so confused that sometimes I doubt myself when doing this simple operations in my head... Any information about this will be highly appreciated.

$\endgroup$
4
  • 4
    $\begingroup$ Examine the output of (9.2 - 8)/1.2 // FullForm. $\endgroup$
    – Carl Woll
    Commented Mar 11, 2021 at 21:48
  • 2
    $\begingroup$ You might find (9.2 - 8)/1.2 // FullForm illuminating—Mathematica does this occasionally annoying thing where it rounds numerical output in output cells by default. As to why (9.2 - 8)/1.2 is less than one, I'm not totally sure beyond "implementation details", and that's still worth getting an explanation for. EDIT: I see @CarlWoll beat me to it by 15 seconds... :) $\endgroup$
    – thorimur
    Commented Mar 11, 2021 at 21:49
  • $\begingroup$ Oh, so it's related to the order of evaluation... Thank you very very much @CarlWoll and @thorimur, I think Round[x,0.1] will do the trick for me :D $\endgroup$ Commented Mar 11, 2021 at 21:58
  • $\begingroup$ Welcome to Mathematica.SE! I suggest the following: 1) As you receive help, try to give it too, by answering questions in your area of expertise. 2) Take the tour! 3) When you see good questions and answers, vote them up by clicking the gray triangles, because the credibility of the system is based on the reputation gained by users sharing their knowledge. Also, please remember to accept the answer, if any, that solves your problem, by clicking the checkmark sign! $\endgroup$
    – Michael E2
    Commented Mar 12, 2021 at 5:28

3 Answers 3

8
$\begingroup$

As it was suggested by @CarlWoll and @thorimur in the comments, I evaluated:

(9.2 - 8)/1.2 // FullForm

which returns:

0.9999999999999994`

I'm not really sure why, but I suspect that this is related to the order in which Mathematica evaluates each operation. Since 9.2/1.2 and 8/1.2 have no finite decimal expansion, apparently Mathematica truncates this expressions and rounds them when showing the final answer. So I decided to round them myself before plugging them in the Floor function as follows:

Floor[Round[(9.2 - 8)/1.2, 0.1]]

resulting in:

1

I still have no clue why Mathematica would distribute the 1/1.2 into the subtraction apparently ignoring the parenthesis, but I suppose it's usually better when working with large numbers...

$\endgroup$
5
  • 3
    $\begingroup$ It's not that it's distributing the 1.2, it's that 9.2 - 8 is slightly less than 1.2 when using machine precision numbers. Try just 9.2 - 8//FullForm and you'll get 1.1999999999999993. If you use Round (as you already did) or Rationalize to get exact numbers, or enter exact numbers yourself (Floor[(92 - 80)/12]), Mathematica will be able to return the correct value. $\endgroup$
    – MassDefect
    Commented Mar 12, 2021 at 4:51
  • $\begingroup$ The Floor[Round[...]] trick could give wrong results for values which actually are something .999999 ish $\endgroup$ Commented Apr 4, 2023 at 7:27
  • 6
    $\begingroup$ It's been said many times, but maybe saying it a different way will help. This doesn't really have anything to do with Mathematica. Floating point arithmetic is done with a representation of numbers and associated functions that are not intuitive (details don't matter to make my point). As soon as you enter a finite precision number like 9.2 you have entered this world, and you must understand and accept the limits of precision in your results. Nothing about this is wrong or even particularly mysterious. $\endgroup$
    – lericr
    Commented Apr 4, 2023 at 17:17
  • $\begingroup$ If you want infinite precision, you need to introduce it early (what "early" means depends on context). For example: Floor[(Rationalize[9.2] - 8)/Rationalize[1.2]]. You can't recover infinite precision later. Well, you can impose it, but it may be too late to get the results that match your intuition. $\endgroup$
    – lericr
    Commented Apr 4, 2023 at 17:17
  • $\begingroup$ Ah, I see @Roman has provided a nice explanation of the details. $\endgroup$
    – lericr
    Commented Apr 4, 2023 at 17:19
8
+500
$\begingroup$

Machine-precision numbers are stored internally by a 64-bit pattern encoding the IEEE 754 format. Each bit pattern describes a rational number of the form $\pm m\times 2^{e}$ where $m$ is the mantissa or significand and $e$ is the binary exponent. To see which rational number is encoded in a given machine-precision number, we can set the precision to infinity:

SetPrecision[9.2 - 8, Infinity]
(*    337769972052787/281474976710656    *)

SetPrecision[(9.2 - 8)/1.2, Infinity]
(*    9007199254740987/9007199254740992    *)

The latter number is clearly less than 1.

Digging a bit deeper, things clarify when we look at the involved numbers in their hexadecimal representations:

SetPrecision[9.2, Infinity] ==
  2^-48*FromDigits["9333333333333", 16]
(*    True    *)

SetPrecision[9.2 - 8, Infinity] ==
  2^-48*FromDigits["1333333333333", 16]
(*    True    *)

SetPrecision[1.2, Infinity] ==
  2^-52*FromDigits["13333333333333", 16]
(*    True    *)

Note that the representation of 9.2 - 8 has one fewer digits in the periodic expansion than the representation of 1.2. This comes from the cancellation and error amplification when subtracting two large numbers (here, 9.2 and 8) to form a smaller number (here, 1.2).

Another way of seeing IEEE 754 floating-point numbers is as intervals: for example, the machine-precision number 9.2 is actually the interval of size $2^{-48}$ centered at $2589569785738035\times2^{-48}$: it is $9.2=(2589569785738035\times2^{-48})\pm2^{-49}$. The interval's size is given by the number of binary digits that we can store in the mantissa. Subtracting 8 (an exact number) from this number gives $9.2-8=(337769972052787\times2^{-48})\pm2^{-49}$; notice that the interval's size has not changed. This contrasts with the number $1.2$, which can be expressed as the interval $1.2=(5404319552844595\times2^{-52})\pm2^{-53}$ and has a 16-fold smaller interval size.

$\endgroup$
15
  • 1
    $\begingroup$ Hmmm I should probably open a separate question for that, but there are more involved cases. The reason I actually revived this question is that I needed ArcSinh[Sqrt[5] 3/2]/Log[GoldenRatio]. This is actually 4, but Floor[N[ArcSinh[Sqrt[5] 3/2]/Log[GoldenRatio]]] returns 3, FullForm[N[ArcSinh[Sqrt[5] 3/2]/Log[GoldenRatio]]] being 3.9999999999999996` $\endgroup$ Commented Apr 4, 2023 at 17:23
  • 1
    $\begingroup$ You may have seen the result displayed as 3, but the result "returned" was the FullForm result (which is incredibly close to 4). FWIW, the result displays as 4. on my system. If you didn't want approximate results, then you shouldn't have applied N. If you needed something even closer, you can pass a second argument to N to indicate the desired precision. $\endgroup$
    – lericr
    Commented Apr 4, 2023 at 17:29
  • 2
    $\begingroup$ Maybe all this is an XY problem. What are you trying to achieve with Floor[N[...]]? If you are trying to prove that the expression is equal to 4, then PossibleZeroQ[ArcSinh[Sqrt[5] 3/2]/Log[GoldenRatio] - 4] returns True. Invoking machine precision is a terrible idea for such checks. $\endgroup$
    – Roman
    Commented Apr 4, 2023 at 18:04
  • 1
    $\begingroup$ @მამუკაჯიბლაძე You can also calculate how far a given expression is from the nearest integer, instead of using Floor and N. For example, with a = ArcSinh[Sqrt[5] 3/2]/Log[GoldenRatio] you can compute Mod[a, 1, -1/2] // N giving $-4.44089\times10^{-16}$: the number $a$ is just a tiny bit smaller than the nearest integer. Naturally, for some expressions you'll get "slightly larger" and for others you'll get "slightly smaller", depending on the exact calculation details. But Floor treats these two cases very differently, which makes it an unstable operation. $\endgroup$
    – Roman
    Commented Apr 4, 2023 at 20:20
  • 1
    $\begingroup$ Here's a solution that avoids machine precision and avoids Floor: define a = ArcSinh[Sqrt[5] 3/2]/Log[GoldenRatio] and b = Round[a] (noting that $b=4$ is evaluated exactly and accurately), then check that FullSimplify[a == b] returns True. No approximations involved. $\endgroup$
    – Roman
    Commented Apr 5, 2023 at 9:06
3
$\begingroup$

One can finesse this for machine numbers by allowing some relative error to handle the fact that (i) decimals do not always have exact finite binary expansions and (ii) finite precision arithmetic can have roundoff and/or cancellation error. I use an order of magnitude above machine precision in the code below, but easy to reconfigure e.g. as a Tolerance option.

floorWithFuzz[n_] := Floor[n*(1 + 10*10^(-MachinePrecision))]

Here are two examples that have appeared in this thread and comments.

floorWithFuzz[(9.2 - 8)/1.2]

(* Out[15]= 1 *)

floorWithFuzz[N[ArcSinh[Sqrt[5] 3/2]/Log[GoldenRatio]]]

(* Out[16]= 4 *)
$\endgroup$
9
  • $\begingroup$ This works until it doesn’t, upon which the user is even more confused. $\endgroup$
    – Roman
    Commented Apr 5, 2023 at 13:04
  • $\begingroup$ @Roman I doubt anyone would use this without recognizing there have to be discontinuities. Same as with Chop. Stated slightly differently, you can’t have a smooth transition for a discontinuous function. Which is one reason some functions have a Tolerance option. $\endgroup$ Commented Apr 5, 2023 at 13:25
  • 1
    $\begingroup$ Is there no option simply to not assign the bounty? $\endgroup$ Commented Apr 10, 2023 at 15:01
  • 1
    $\begingroup$ @DanielLichtblau Yes, a bounty can remain unassigned: "If, after the end of the bounty period, a question has no answers, the bounty will expire and the reputation will disappear. Part of what you're 'paying for' with a bounty is for higher question visibility and increased answerer motivation. A bounty does not guarantee a response and is not refunded if none are received." $\endgroup$
    – Roman
    Commented Apr 10, 2023 at 15:05
  • 1
    $\begingroup$ Thanks @მამუკაჯიბლაძე. I'll pass these points onto another bounty. Cheers! $\endgroup$
    – Roman
    Commented Apr 10, 2023 at 17:50

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