1. The program should "soldier on" if it can.
2. The program should go immediately to jail, it must not pass Go, and must not collect $200.
I'm solidly in the latter camp. If a program has entered a state unanticipated by the programmer, then there is no way to know how it entered that state (until it is debugged), and hence no way to know what the program might do next (such as load malware).
1/0 is a bug, and the program should immediately halt.
... or kill the patient ( https://en.wikipedia.org/wiki/Therac-25 ) or set the company bankrupt overnight ( https://en.wikipedia.org/wiki/Knight_Capital_Group ), or simply delete all your data (happened to me with some buggy media player that though my $HOME directory belonged to its download cache).
Programming languages don't generally allow us to "handle", from inside the faulty process, stuff like NULL-pointer dereference, double-free, or segmentation faults. And that's a good thing, because as you said, at this point, there's a very high probability the program is in some rogue state.
(Making special cases for NullPointerException or OutOfBoundsException, like some languages do, is, IMHO, a bad idea, that spreads the confusion between programming mistakes (i.e coming from the source code) and invalid runtime conditions (coming from environment). (I'm avoiding the ambiguous terms "errors" or "bugs" here)).
Making "divide by zero" non-fatal belongs to the same family than making "((int)0)" return zero, or making "((int)0) = x" a no-op. At first, it seems like this will only hide programming mistakes ; but this impression comes from my current programming habits, which are tailored to avoid these situations.
But maybe there are advantages in being able to write these things on purpose (at the cost of losing the runtime detection of some programming mistakes). After all, I'm perfectly happy with "free(NULL)".
Actually if you check out the interviews with Java designers the checked exceptions mechanism was designed for that purpose: unchecked exceptions are for bugs that should never happen in a well written code while checked exceptions are for conditions that can happen regardless to how good is your code (e.g. i/o errors).
See: https://www.artima.com/intv/solid.html
There are of course also Errors for fatal conditions that should been be caught (actually in CLR even if you catch the exceptions will be re-raised automatically at the end of the handler).
Another interesting thing in CLR is Constrained Execution Regions that allow you to run the cleanup code reliably even when such a fatal condition is encountered (but the code to be run is limited e.g. it cannot allocate).
[0] NumberFormatException is a RuntimeError as are all IllegalArgumentException, WTF?
And that's why I like dual error mechanisms, like the panic vs manual error handling mechanism in Go (despite all its verbosity). It's very important to make the difference between an external error (that should be considered something "normal" to deal with) and an invalid state (that should lead to a halt of the program, maybe after logging something, or before trying to restart the program from zero).
Traditional expection mechanisms (à la try/catch) make it too hard to deal with an external error, and too easy to think you can recover from an invalid state.
Failures of the former throw an Error, the latter Exception.
I don't know much about Pony, but given that it is also actor based, I'd imagine this would be a great opportunity to crash early and eagerly. It gives you all the advantages of catching a bug early while not crippling a production app under what might be a pretty low-impact, low-frequency bug.
Special care must be taken if threads are manipulating shared data structures, especially inside a "critical section" where the data structure invariants must be maintained. This is why automatically terminating the JVM when OutOfMemoryError occurs is a best practice, as code is often not paranoid enough to handle it.
This is an area where the design of Java/JVM is badly screwed up.
[1] JVM, really. It's pretty unavoidable on most (all?) JVM languages.
Swallowing exceptions is a really poor idea. Does Java really do that?
The divisor in your code went through a non-ECC DRAM module (maybe in a disk drive) that runs hotter than its specified operating temperature would allow and a random bit-flip changes your `1` into a `0`.
On this topic, the talk on Bitsquatting from Artem Dinaburg during Defcon 19 is worth watching. The most interesting part on bit flips starts around 15:05 minutes: https://youtu.be/9WcHsT97suU?t=15m5s
The lesson I took from this is that neither of these two options is acceptable in software that has to work all the time, like much real-time software. Instead, we should detect all the bugs in such software before runtime, which is only feasible for small systems such as seL4[1]. So we should diligently minimize this software.
For other programs, the best way to handle an error is context-dependent. If a rendering bug will result in a glitch showing in a texture onscreen, that's preferable to exiting the game in the middle of a raid. If a PID motor control system for a robot arm has a division by zero, halting the program without first slowing the motors to a stop could be catastrophic, even causing fatalities. And of course there are numerous cases where continuing execution in the face of such an error is far worse.
[0]: https://web.archive.org/web/20000815230639/http://www.esrin.... [1]: http://sel4.systems/
If a program is running under a supervisor, it makes sense to bail, as a new clean instance will become available.
If it was the code of the life support machine that displays data on the LEDs that I was connected to, I prefer that it solder on rather than stop functioning altogether because data for one of the digits was out of 0-9 range.
I'm sure there's been many instances where spacecraft had partially malfunctioning software that was remotely corrected because it didn't entirely give up but rather continued to accept input.
What is absolutely NOT done is soldiering on with a computer in an unknown state.
Any software system that is life critical is either designed this way or is very badly engineered. I've written a couple articles about this:
https://www.digitalmars.com/articles/b39.html https://www.digitalmars.com/articles/b40.html
My point of view is that a program should abort if it encounters an irrecoverable error, such as imminent memory corruption. However, if it's designed to fail functional, then it could continue to work, either by reinitialising itself, or by aborting the current operation. However, it must be said that it's hard to design such programs, so the option of bailing out is very attractive.
Example: an out of bounds access is a typical programmer error. This should probably result in an abort during development, so that it can be fixed. However, it is possible to not want to abort just because something is not accessible when in production. The program could instead raise an exception and clean up the call stack up to its initialisation point, then notify the surrounding system that a problem was encountered and resume service. The surrounding system could then keep track of the encountered issues and try to perform a recovery after a threshold is reached: this is how Android recovery works for e.g. crashing system services, if I recall correctly, it first cleans some application settings, then system settings, then does an OS reinstall. Now in this example the trigger condition is a crash, but it doesn't necessarily have to be like that.
What seems much more important to me is that the behavior is well documented. If I am working with a system where 1 / 0 is defined to be zero, I will deal with that just as I deal with other peculiarities like 1 / 3 being 0 and 1.0 / 3.0 only approximately being a third in some systems. It's a practical concern like many others in programming.
`/`-by-0 is just an operation and it tautologically has the semantics assigned to it. The question is whether the specific behaviour will cause bugs, and on glance that doesn't sound like it would be the case.
Principally, division is normally (best guess) used in cases where the divisor obviously cannot be zero; cases like division by constants are very common, for example. The second most common usage (also best guess) is to extract a property from an aggregate, like `average = sum / count` or `elem_size = total_size / count`. In these cases the result is either a seemingly sane default (`average = 0` with no elements, `matrix_width = 0` when zero-height) or a value that is never used (eg. `elem_size` only ever used inside a loop iterated zero times).
It seems unlikely to me that `/` being a natural extension of division that includes division by zero would be nontrivially error-prone. Even when it does go wrong, Pony is strongly actor-based, has no shared mutability and permits no unhandled cascading failures, so such an event would very likely be gracefully handled anyway, since even mere indexing requires visible error paths. This nothing-ever-fails aspect to Pony is fairly unique (Haskell and Rust are well known for harsh compilers, but by no means avoid throwing entirely), but it gives a lot more passive safety here. This honestly doesn't seem like a bad choice to me.
x = 2/(1/a +1/b)
This is a form of average where you are giving increased importance to the smaller number. It frequently pops up in science/engineering, e.g. in hydrology when you are computing flow of water underground.
In this case, it's "obvious" that when e.g. a is zero, you want 1/a to be zero so you simply return b. There's typically a clear, big distinction between "smallest realistic number", for instance 1e-3, and a zero, whether actual zero or 1e-34.
For instance Numpy offers a nice way of doing this:
def safeInv(a):
tol = 1e-16
return np.division(1.0,a,out=np.zeros_like(a),where=np.where(a>tol))
This tells numpy to put the result of division into an array of zeros shaped like a, but only do the divide where a>tol, so it returns 0 where a<tol.In this case, though, the programmer understands this division is somehow special and should allow div-by-zero, so it is handled specially.
If, say, `a` is mathematically very small (and `x` is accordingly expected very small`) but `a` become zero due to rounding behavior, then having `1/a=inf` results in `x=0` which is arguably closer to the expected result than e.g. `x=2b`.
As someone involved with numerical methods, I have very often relied on `1/0=inf` because it would ultimately warrant the proper behavior in case of rounding errors in the denominator.
b obviously isn't what you want from this harmonic mean when a is 0. And in any case x will be 2b if 1/a is 0, not b.
https://www.rdocumentation.org/packages/lmomco/versions/2.3....
Imagine a Mars lander program that looks at the average of altitude measurements to decide when it's ok to jettison the parachute, sees no measurements, and decides that altitude is zero.
Pony is made for a more typical scenario where every failure case is a new, untested error path, and history shows that general developers handle errors badly. Pony makes a heroic effort to remove the error path from the language semantics entirely: every failure is explicitly annotated in the code, and has to be handled there. This is much better for the typical developer, allowing them to produce very robust code without NASA's degree of investment towards it. Giving Pony a special unwinding mode just for division by zero would be very dangerous, because this is a new path that only exists for a rarely-encountered sort of error; you cannot assume a sudden shutdown is going to do less damage.
Having Pony present the error cases inline on division by zero would certainly be safer, but there is a degree of disproportionality here: most divisions are impossible to go wrong in this way, and I suspect most of the rest are benign or would be quickly caught by the check after. Since Pony aims to compete against incumbents that are generally less safe in this regard (C++ has UB, Python has exceptions but makes it hard to handle them correctly, etc.), a compromise seems sensible.
UB is not the only way. You can have NULLs (they come with their own sets of problems, but still).
In many cases no value is better than incorrect value.
Let me elaborate then.
Average credit card balance predicts probability of default. Joe who maxed out his credit card is higher risk than Jane who pays back her entire credit card balance every month. Now we have Jack with no credit card. We predict that Jack is low risk because his average credit card balance is zero.
Second example, defibrillator that monitors blood pressure and shocks the patient when his heart stops (blood pressure drops to zero). We attach the defibrillator to a patient, turn it on, and it shocks the patient immediately because there are no blood pressure measurements yet and therefore it thinks the average is zero.
Third example, a thermostat that turns on the freezer if average temperature is above a set threshold. It never turns on because average (of zero observations) is already zero.
Of course you can hard-code handling of those special cases, but if you did not, failing would be preferable to continuing to work incorrectly.
Credit risk is based on total debt, and since credit cards are a revolving line of credit, the entire credit limit is considered debt, even if the balance is zero. That's why you can improve your credit score by closing out a credit card that you never use and that carries a zero balance (which we did in order to qualify for a mortgage several years ago). In other words, it's a sum, not an average; there's no division involved.
Setting aside the fact that there are much more reliable ways to detect a stopped heart than blood pressure: If you're taking a running average of BP over time, a low reading after a heart attack would just be a single data point, and you'd probably have to accumulate a bunch of them in order for it to register (particularly if you got a high reading just before the event). Even if you take a reading every five minutes, which is ridiculously frequent for BP, the patient will be long dead by the time you notice. You should be acting based on the last reading; again, no division involved.
I don't see why a freezer would be based on running averages rather than the most recent reading either. Thermostats generally work off of two thresholds: a higher one above which the compressor turns on, and a lower one below which it turns off. That smooths out any measurement noise without taking averages. Even if you do use averages for some reason, though, presumably it will start measuring at _some_ point and the compressor will turn on; I don't see why the average would stay at zero.
I'm not convinced that `1/0 = 0` is correct in any meaningful way, but I feel like any situation where it would cause bugs more critical than a UI issue probably points to a deeper design flaw. After all, if the alternative is to crash on a failed assertion, that's not necessarily preferable in a life-or-death situation.
It is normally addressed with floors and ceilings or something like Laplace rule of succession, so that no data at all results in a reasonable number. Very rarely that reasonable number is zero.
I'm not saying taking the average is necessarily good in the examples given, just that if you are computing the average, then that computation should not silently return zero if there's no data to average.
That's the way AVG() in SQL or mean() in R work, they return NULL or NA rather than 0. If you know that NA should be 0 you can explicitly COALESCE, but that should be your decision rather than the default behavior.
Credit scores don't work this way. Zero is not a valid credit score, and the things you mentioned describe different components of the score.
I work a lot with survey data, to be interpreted by humans. In this problem domain, zero is a sane value for an empty average, though "N/A" is usually better. I wish our language would let us define it as zero so long reports don't die in the middle. But it's about your problem domain, as usual.
But in this case it's irrelevant because average balance is independent variable (input of the scoring model).
And the whole point is that this is meant to catch unforeseen interactions as soon as possible. If you add a check it's no longer unforeseen, and it may easily slip the programmer's mind.
Because frequently division by zero indicates a bug. But similarly frequently, I end up crapping out annoying little bits of code like
if (foo == 0):
return 0
else:
return bar / fooYou say that you frequently have to write your little shim but honestly I don't remember writing code like that in recent memory.
You talk about progress bars, I suppose it makes sense if you somehow try to copy 0 elements for instance, and you end up dividing by zero when computing the progress, like:
pos_percent = (copied_elems * 100) / total_elems
And both copied_elems and total_elems are zero. But in this case wouldn't you want the computation to return 100% instead of zero?It's also a bit odd because it introduces a discontinuity: as the divisor goes towards zero the result of the operation gets greater until it reaches exactly zero and it yields 0. Wouldn't it make more sense to return INT_MAX (for integers) or +inf for floats? If you're writing a game for instance it might work better.
I guess it just goes to show that it probably makes a lot of sense to leave it undefined and let people write their own wrapper if necessary to do what works best for them in their given scenario.
Of course, it's context dependent. As others mention, your code might be full of stuff like X / (divisor || 1).
Also, if loading total elements takes a significant amount of time, shouldnt this loading also be reflected in the progess bar?
X / (divisor || 1).
It's invalid code in C, C++ and Java. % cat >division.c <<EOF
#include <stdio.h>
int main(void)
{
printf("1/0 = %d\n", 1 / (0 || 1));
printf("1/2 = %d\n", 1 / (2 || 1));
printf("1.0/0 = %f\n", 1.0 / (0 || 1));
printf("1.0/2 = %f\n", 1.0 / (2 || 1));
}
% gcc -Wall -o divison divison.c
% ./divison
1/0 = 1
1/2 = 1
1.0/0 = 1.000000
1.0/2 = 1.000000
In Python this is not the case: % python3
>>> 1 / (0 or 1)
1
>>> 1. / (0 or 1)
1.0
>>> 1 / (0 or 1)
1
>>> 1. / (2 or 1)
0.5
% python3
>>> 1 / (0 or 1)
1.0
>>> 1 / (2 or 1)
0.5 x / (divisor ?: 1)C doesn't have booleans and treat them as integers 0 or 1, it can do the math and will always return 1.
https://en.cppreference.com/w/cpp/language/implicit_conversi... (under "Integral promotion")
And C has a boolean type as of C99.
Defining x / 0 = 0 gets this behavior for free, while leaving zero as an exception means it has to be caught for every single signal calculation, which is a pain when there are potentially dozens of different signals, all of which are counting different things (and have different divisors). I've actually defined a helper function to do this automatically, which also lets me change the default "null object" easily if I choose a different representation or want to apply some baseline value.
Unless you're using unsigned ints, the discontuity will exist no matter what you do, because of negative divisors.
pub fn checked_div(self, rhs: u8) -> Option<u8>
Checked integer division. Computes self / rhs, returning None if rhs == 0.
You would use this as lhs.checked_div(rhs).unwrap_or(your sane default)
This is dramatically better than always returning zero silently, as doing so is bound to be wrong in certain cases. If you run into a situation where you are afraid your RHS may be 0 but still want to do the right thing, this is what you'd use.
Unfortunately, GCC only lets you enable this globally (within a program): see http://www.gnu.org/software/libc/manual/html_node/FP-Excepti....
A "strict" mode is usually always an afterthought once we have errors & people run it in strict mode and facepalm.
One of the first "utilities" I wrote for PHP was something called "pecl/scream", which turned off all the "unchecked" operations across the whole VM.
And in general, this works out poorly for code quality.
You can always (as in OS with lazy allocation) allocate enough memory to get OOM no matter how safe your language is.
return (foo == 0) ? 0 : bar / foo
or even return bar / max(1, foo)
in the case where foo is integer or tiny foo would overflow your range anywayjust for fun, in kotlin (making use of expressions removes some verbosity):
return where(den) {
0 -> 0
else -> num / den
}
return if (den == 0) { 0 }
else { num / den } return foo && bar / fooIsn't this reason the the point of having a utils library with commonly used pieces of code?
if (foo == 0):
return MAX_FLOAT
else:
return bar / foo
is more sound for the given algorithm than "return 0" (still crappy, but more sound).Then use MAX_FLOAT as an error flag instead of 0, which could have been the result of a legal operation (with bar==0).
Just try INT_MIN/-1, can blow most "foolproof" divide logic away.
How could you possibly make an assertion like this without knowing why they made said choice[1]? Per the article, they didn't do it for fun, or to avoid exceptions (as you point out, more like defer exceptions). FTA:
> As I understand it, it’s because Pony forces you to handle all partial functions. Defining 1/0 is a “lesser evil” consequence of that.
Do you know enough about Pony's language design and decision process to support your claim that this decision (and all its second-order consequences) is hurting users far more than helping them?
[1] On the off chance that you are familiar with the tradeoffs Pony made and do know what you're talking about: care to clarify?
I will be writing up a post about why we made this decision.
Edit: Although Pony claims that programs should never crash, the language designers clearly understand the pragmatism of an unrecoverable error because it happens on OOM and stack overflow
[1] The author has a dislike of the notion that 1/0 = 0 in a programming language. The author identifies this as a personal preference; I'm sure that preference is based on falsiable notions, but the author doesn't elaborate. The author _DOES_ elaborate their motivation for writing the post: The post debunks the notion that 1/0 = 0 is 'bad math'.
[2] Pony chose 1/0 = 0 because the language of Pony is set up to enforce the coder to complete all functions; if you go with the strict definition of division, which is an operation which has no definition if the second operand is 0, it's not complete anymore. I agree with your sentiment; this feels like the wrong answer, but it's definitely not as simple as just saying: Why don't the pony folks redefine things such that dividing by 0 raises some sort of error condition?
It's such a common case to consider, I don't think that's compelling. Knowing that a division by zero throws an exception or casts to zero, is similarly considered.
> they are hurting their users far more than they are helping.
There's no evidence of this.
Even Javascript is better with "NaN"
Oh well :)
It's 0.
>> 1 / 0
Infinity
>> 1 / -0
-InfinitySo 1/0 is infinity, and 1/-0 is -infinity.
For example, if we examine a sample of zero elephants, then we end up estimating the average elephantine mass as being zero.
This shows to be wildly off as soon as we upgrade our statistical wherewithal to work with a sample size of one.
A center of mass is a kind of average. If we have an empty object made of no particles of matter at all, can we arbitrarily pin its center of mass to the (0, 0, 0) origin of the coordinate system we are using?
Let’s say some program calculates the mean of an array and then adds that mean to some accumulator.
For purposes of updating the accumulator, the mean of an empty array is perfectly well-defined: it should add nothing to the accumulator (add 0).
This might be a major operating requirement for the mean function, such that rather than guarding or pattern-matching on an empty array and handling a failure is far worse than having a better 0-length convention.
Consider the difference between the “head” function of some List type, which has to either raise an exception or wrap the return value in some Maybe structure, because it’s literally not definable, vs the “length” function which has an obvious natural definition for empty arrays that is often highly preferred to some design where length(empty_list) throws an exception and everyone has to handle it in little bits of custom code to specify 0.
To me this topic is all about usability and not about some parochial claim that some operation is nonsensical.
Why would you want to do this rather than validating understanding of the data before computing on this?
> For purposes of updating the accumulator, the mean of an empty array is perfectly well-defined: it should add nothing to the acculator (add 0).
That's not a mean, though, that's a quirk of how you decide to (incorrectly) compute a mean. What's the point of such a program? It should refuse to compute when it's given no values--that's clearly a category error.
Otherwise, you're not using a mean, you're using a mean-or-0-when-lacking-a-mean. Might as well not call it a mean at all so other people can read your code.
Yes, this is pedantic, but this kind of subtle changing meaning of terms is exactly what leads to bugs. Name your functions accurately.
Consider needs to vectorize a large column-wise mean calculation across columns of a large data matrix (where NaN values are sparse but appreciable).
The NaNs might be perfectly reasonable, expected pieces of data, but you still want to understand the distribution of the non-NaN data, and adding extra work to filter it out first might be hugely costly, or even actually wrong depending on what other operations the NaN data is planned to be passed to and how those operations natively handle NaN.
And simple columnar summary stats are just the tip of the iceberg. It gets much more complicated.
By no means is the solution of “diagnose why there are NaNs ahead of time and preprocess them accordingly” even remotely realistic in most use cases. This is why libraries like pandas, numpy and scipy for instance provide specific nan-ignoring functions or function parameters.
You bring up the history of CS, but even there you have debates about what convention to use for defining 1/0 for function totality and theorem provers.
There’s no aspect of pure math derivation of number systems on up through vector spaces that definitively makes a zero mean for an empty array ill-defined. Whatever choice you make, positive infinity, undefined, 0, or any finite values, etc., any such choice is purely down to convention that depends precisely on your use case.
It’s not conceptually wrong, it just means the “mean” you’re referring to calculates a different value than the “mean” we’re taught in school. So, underlying assumptions about the differences in “mean” should be communicated where it’s used.
Wouldn’t that be a (potentially very different) case of 0/0? If there are 0 elements, you wouldn’t have a nonzero numerator, right?
When encountering such unanticipated corner cases, it's almost always better to throw an error. That prevents any further data/state corruption from happening. It prevents your user from getting back a bad result which she thinks she can trust and rely on. It highlights the problem very clearly, so that you know you have a problem, and that you have to fix it. Simply returning a 0 does the exact opposite.
If you're one of the 0.1% who did anticipate all this, and correctly intended for your program to treat 1/0 as 0, then just check for this case explicitly and make it behave the way you wanted. The authors of pony are welcome to design their language any way they want. But in this case, they are hurting their users far more than they are helping.