3 ms·
My favorite thing about floating point numbers: you can divide by zero. The result of x/0.0 is +/- inf (or NaN if x is zero). There's a helpful table in "weird
by exDM69 1y ago
My favorite thing about floating point numbers: you can divide by zero. The result of x/0.0 is +/- inf (or NaN if x is zero). There's a helpful table in "weird floats" [0] that covers all the cases for division and a bunch of other arithmetic instructions.
This is especially useful when writing branchless or SIMD code. Adding a branch for checking against zero can have bad performance implications and it isn't even necessary in many cases.
Especially in graphics code I often see a zero check before a division and a fallback for the zero case. This often practically means "wait until numerical precision artifacts arise and then do something else". Often you could just choose the better of the two options you have instead of checking for zero.
Case in point: choosing the axes of your shadow map projection matrix. You have two options (world x axis or z axis), choose the better one (larger angle with viewing direction). Don't wait until the division goes to inf and then fall back to the other.
[0] https://www.cs.uaf.edu/2011/fall/cs301/lecture/11_09_weird_floats.html https://www.cs.uaf.edu/2011/fall/cs301/lecture/11_09_weird_f...
- thwarted 1y agoYou still can't divide by zero, it just doesn't result in an error state that stops execution. The inf and NaN values are sentinel values that you still have to check for after the calculation to know if it went awry.
- sixo 1y agoIn the space of floats, you are dividing by zero. To map back to the space of numbers you have to check. It's nice, though; inf and NaN sentinels give you the behavior of a monadic `Result | Error` pipeline without having to wrap your numbers in another abstraction.
- TehShrike 1y agoYou can divide by zero, but you mayn't.
- epcoa 1y agoIf dividing by zero has a well defined result that doesn’t abort execution what exactly does “can’t” even mean? Operations on those sentinel values are also defined. This can affect when checking needs to be done in optimized code.
- bee_rider 1y agoI believe divide-by-zero produces an exception. The machine can either be configured to mask that exception, or not. Personally, I am lazy, so I don’t check the mxcsr register before I start running my programs. Maybe gcc does something by default, I don’t know. IMO legitimate division by zero is rare but not impossible, so if you do it, the onus is on you to make sure the flags are set up right.
- epcoa 1y agoCorrect, divide by zero is one of the original five defined IEEE754-1985 exception. But the default behavior then and now is to produce that defined result mentioned and continue execution with a flag set ("default non-stop"). Further conforming implementations also allow "raiseNoFlag". It's well-defined is all that really matters AFAIC.
- mike_ivanov 1y agoThis is the Result monad in practice. It allows you to postpone error handling until the computation is done.
- wpollock 1y agoIt has always amused me that Integer division by 0 results in "floating point exception", but floating point division by 0.0 doesn't!
- exDM69 1y agoIt's not always necessary to check for inf/NaN explicitly using isinf/isnan. Both inf and NaN are floating point values with well defined semantics. I'll give two examples from a recent project where I very intentionally divided by zero. First one was about solving a zero in a derivative and check if it falls on 0..1 range. This exploits the fact that (x < NaN) is always false and comparisons with +/- inf behave as expected. float t = a / b; // might divide by zero. NaN if a and b == 0.0, +/- inf if b == 0.0 if (t > 0.0 && t < 1.0) { // we don't get here if t is +/- inf or nan split_at(t); // do the thing } The second one was similar, but clamping to 0..1 range using branchless simd min/max. f32x8 x = a / b; // might divide by zero return simd_min(simd_max(0.0, x), 1.0); // returns 0 for -inf or nan, 1 for +inf In both of these cases, explicitly checking for division by zero or isinf/isnan would've been (worse than) useless because just using the inf/NaN values gave the correct answer for what comes next.
- kccqzy 1y agoThis is also my favorite thing about floating point numbers. Unfortunately languages like Python try to be smart and prevent me from doing it. Compare: >>> 1.0/0.0 ZeroDivisionError >>> np.float64(1)/np.float64(0) inf I'm so used to writing such zero division in other languages like C/C++ that this Python quirk still trips me up.
- pklausler 1y agoDivision by zero is an error and it should be treated as such. "Infinity" is an error indication from overflow and division by zero, nothing more.
- ForceBru 1y agoThe article's point 3 says that this is a myth. Indeed, the _limit_ of `1/x` as `x` approaches zero from the right is positive infinity. What's more, division by _negative zero_ (which, perhaps surprisingly, is a thing) yields negative infinity, which is also the value of the corresponding limit. If you divide a finite float by infinity, you get zero, because `lim_{x\to\infty} c/x=0`. In many cases you can treat division by zero or infinity as the appropriate limit.
- pklausler 1y agoI am allowed to disagree with the article.
- ForceBru 1y agoSure, but it makes sense, doesn't it? Even `inf-inf == NaN` and `inf/inf == NaN`, which is true in calculus: limits like these are undefined, unless you use l'Hôpital's rule or something. (I know NaN isn't equal to itself, it's just for illustration purposes) But then again, you usually don't want these popping up in your code.
- pklausler 1y ago
- layer8 1y agoFloating-point arithmetics adopted this from the extended reals (usually denoted as ℝ̅): https://en.wikipedia.org/wiki/Extended_real_number_line https://en.wikipedia.org/wiki/Extended_real_number_line (see Arithmetic operations there)
- wolvesechoes 1y ago> you can divide by zero It is implementation-dependent. It is not obligatory for implementation to respect IEEE 754.