1/0 = 0
hillelwayne.com
hillelwayne.com
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.
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)".
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.
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.
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.
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.
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.
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....
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 anywayIsn'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"
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?
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?
I'm on the Pony core team. I will be writing in more detail about this decision. A few short notes until then:
1) no one on the team has ever been happy with ending up here, understanding why the decision was made involved understand how partial functions (one that can produce errors like division by zero) are handled in Pony and interesting ergonomic issues that can result that is a large part of what my post will be about.
2) this applies only to integer division wherein division by zero is undefined. 1.0/0.0 uses the available Infinity value.
3) Partial function math is coming to Pony that includes 1/0 as partial (ie error producing) as well as handling integer overflow and underflow for operations: `+`, `-`, and `*`.
4) It's very straightforward even without that those operations to define your own division that will be partial (ie return an error) for division by zero.
Edit to include link to the comment below because it contains a good bit of the decisions that have to make when dealing with integer math:
https://news.ycombinator.com/item?id=17736637
Which is part of what I will touch on in my post about the decision process that Pony went through to reach to state we are currently at (which I want to highlight, is one we intend to improve- see #3 above).
Oh jeeze... to me that almost nullifies all of the points in the article. It does an alright job explaining how 1/0 = 0 is at least consistent with other notions in the language, but to hear that the same logic can’t be applied to floats is just... well, objectively it’s a mess.
The floating point standard says that division by 0.0 should be Infinity and provides a value for it. The integer math, all possible values are used for numbers, so division by zero is undefined behavior.
And from there, every language is potentially going to have a mess of inconsistency to deal with.
You can make 1/0 = Infinity so long as you box all your integers. That is, its not using the machine type, but I type that is a wrapper that can be the machine type or Infinity. And every user takes a large performance hit even if they will never divide by zero.
For some languages boxing integers is not a bad thing because they already do it. Why do they do it? Usually, because of another not math problem. Overflow and underflow:
What is 0 - 1? Well, its -1 right? Except of course if you have an unsigned integer, then its the max value of the integer. And what if you had 1 to the max value that the computer can represent? You wrap around. Some languages make all integers protect you from this and take a performance hit to be able to box and be able to represent numbers better as we expect them to work. In return, you can not do integer math as fast as you could.
Computers and numbers... its an inconsistent mess and a series of tradeoffs for any language of performance and various possible errors.
I'm going to talk about this and more in my post.
What does your favorite programming language return for 3/2 as compared to 3.0/2.0? Many will return different values.
EDIT:
for clarity of my point: integer math that uses machine types is pretty surprising compared to what we would expect from "math" in general for division.
Floating point doesn't even form a Ring, because the operation is not associative[0]. Coq, for instance, defines the operation for QArith, which are the rational numbers. The representation is a pair (Z, positive), where Z is an integer and positive is a positive number (i.e., 1,2,...). QArith does form a field in the usual sense.
Integers are not a Field (unless you pick Z/p for some prime p), and machine words, signed or unsigned are even less of a mathematical object with the usual operations. So you can do almost anything you want to them from a programming perspective, including 1/0 = 0.
As Hillel writes, what to do will make sense in some settings, but not in others. You may want MAX_INT in some situations, and 0 in others. And this requires a check, partiality or not. Rejection of the case x/0 has more to do with the notion that it is usually a corner case where you want the programmer to think about what the result should be.
[0] The reason is that any addition can incur information loss, and the further away from 0 you are, the more information is lost. Thus, the order in which you add values matter.
Division with floats doesn't usually have a definition like that, so you don't end up in a similar situation. Plus, floating point math can have denominators get really close to 0 and have the result of the division tend toward the infinity value. Ints don't have values close to 0 or an infinity to represent what that division would tend toward.
And I was thinking that perhaps 0x80000000 (in two's complement arithmetic) would be a good value for integer NaN. This value is already fixed point with respect to negation, so why not make it into an exception on its own right?
But it would mean to rethink all the hardware which I don't think is an option.
In any case, I think to set 1/0 to 0 is a bad idea, but I could imagine setting x/0 to 0x80000000 (interpreted as NaN) to make more sense.
Ah, that seems rather reasonable. For whatever reason, the OP had me thinking that Pony was replacing the ordinary semantics of IEEE 754 floating point division (probably because he talked about fields and multiplicative inverses).
Having 1/0 = 0 seems like a very reasonable choice, especially if you have 1%0 = 1 as well. Of course, if anyone thinks they will actually encounter division by 0 in their code, and needs to treat it as an error, they can always test for it.
So you may be saving some developers time in operator overloading, etc. More important than the actual binary decision itself is having well-documented behavior. Have not tried Pony yet, but will take a look. Good discussion, keep it up!
https://tio.run/##K8jPq/z/PzG5JL9IwTcxM49LAQjyUssVkotSE0tSNV...
`I64.max_value() + 1` gives the expected result.
I'm going to have to look into that.
Thanks.
If 1/0 = 0 sounds unusable to you, Pony's garbage collection scheme (which is brilliant, by the way) is going to make you think the language is insane.
I don't know, does it even matter? What happens when you try to divide a physical object into 0 parts? It doesn't create infinite pieces. It doesn't make the object disappear. Seems like nothing happened. Maybe when we divide by 0 in a program it should halt and catch fire?
I hate logical arguments, I get sucked in and start arguing all the positions.
The limit of x/y as x and y go to zero depends on which path across the xy plane you take towards the singularity. Along one approach, the limit is 0, along another approach the limit diverges to infinity, along yet another the limit is 17.
I'm not kidding! Go to https://www.geogebra.org/3d and enter "x/y" and spin the graph around. The "value" at x=0 y=0 is the entirety of the z-axis.
For another perspective, try http://www.wolframalpha.com/input/?i=z%3Dx%2Fy and turn on contour mode for the 3D plot. Notice how the contour lines radiate out from the z-axis; each of those is an "approach" to the singularity at a different z-value, and taking the limit along each line leads to a different value.
How would a path lead to 17?
x=170, y=10
x=17, y=1
x=1.7, y=0.1
x=0.17, y=0.01
The answer keeps being 17, even as x and y both get vanishingly close to zero.I encourage you to play around with a 3D graphing calculator and see all the different paths you can take along that surface to reach the singularity. They all "reach" it at different heights.
17 / 1
1.7 / 0.1
0.17 / 0.01
0.017 / 0.001
...
0 / 0
I think 0/0 is a different concept than 1/0. It's a different expression. 0/0 doesn't explicitly express a particular "path" on the graph.
We can call it (0/0) different names if we want. They can say 0/0 = "undefined". I am currently satisfied with 0/0 simply equals to 0/0, or simply "undefined".
If we all agree it is "undefined" we ironically defined it.
Except it isn't. It still depends on which side you take the path from - from the positive denominator or the negative denominator side. (This is why IEEE 754 has a single signless infinity).
Not for integers.
You bring up 0/0. But 0/<anything> is already defined, it's 0. So 0/infinity = 0
Programmatically, I think I've always wanted X/0 to be 0. For example: progress bars, currency, or damage in a video game. It wouldn't be very helpful to have infinity be an answer there.
Why infinity and why not negative infinity.
Normal langs throw. That what the checked int div will do. But the unchecked int div has no return type which allows throwing an error. And pony is strongly typed, unlike most other langs. That's why it can guarantee much more safeties and performance than all other languages.
It actually does break something, the symmetry between division and multiplication and the many pieces of code that assume that (x / y) * y equals x. Here is a naive and non practical example, but it is not impossible to find a real world example where this simplified code manifests itself accidentally or by design
function do_something_with_x(x, y) {
let ratio = x / y;
if (do_something_with_ratio(ratio)) {
return x * y;
}
else return null;
}
Again, this is a naive example but one that could manifest itself with a very imprecise result when y = 0The same issue if division by zero throws an exception. This is simply a more practical approach that dispenses with the exception handling (ie becomes a less irregular test case). Edit: Not buggy behavior, when expected.
Even without Pony's assumption, that's only true when y != 0.
What? If you’re using integers or floating-point numbers, that has never been true in general. Consider x=1, y=49 in the realm of IEEE doubles. Addition isn’t even associative, consider 1e-50 + 1 - 1.
Do any languages do that? Seems more consistent (if way more hassle) than giving a mathematically false result out of pragmatism. At least in a strictly typed language.
The author does not once say that Pony's choice is a good one, only that whether it is a good or bad one should be settled by engineering consequences, and that there is no purely mathematical argument that precludes it.
I can't help but think it _is_ a bad idea, because it's easier to overlook a 0 appearing than a NaN. That argument isn't math, though.
Most integer representations have no space for such a value, so the only choices available to language developers are:
1. Throw an exception or otherwise consider it an error
2. Define the result to be 0 or 1 or some other integer value (0 being the only good choice)
3. Use a non-standard integer representation
Most languages opt for option 1, Pony chose 2, and I've never seen 3, perhaps due to it requiring so much software interference and precluding inter-operation with other languages.
Devil's advocate: why not a maximum integer like 18446744073709551615 or 9223372036854775807?
0 looks like a valid number, while 18446744073709551615 is an obvious NaN for any programmer looking at the output.
That seems sub-optimal.
No, you haven't. You've merely failed to locate any. You've said "I'm not going to prove that this works. I'm going to assume that it does and act as if it did, and place the burden on you to prove otherwise."
> Lawrence Paulson, professor of computational logic and inventor of Isabelle: > > [...] This identity holds in quite a few different proof assistants now.
Excellent point. It's not even known if we can get any inconsistencies without making this definition; and it's known that, if it's true that we can't get any inconsistencies without it, then we can't prove that it's true. Building on such possibly shaky foundations can't make them stronger.
Erm, when we say it's "undefined" we mean it literally -- standard mathematical systems of arithmetic do not define a value for that division.
If you make another system of arithmetic you can define it how you want and be consistent with "regular maths" for the operations in which things are defined. It's an extension.
> We have "NaN" for a reason
Funnily enough, IEEE754 defines 1/0 as positive infinity, not Nan. But none of these things are "the truth" in any reasonable sense of the word, just "useful systems".
Defining it as 0 at least means floating point numbers are (presumably) closed under arithmetic operations, which could be handy.
They already are closed. Floating point includes Inf and Nan for that reason. :-)
Mathematicians define equality of fractions by stating that a/b = c/d if and only if a·d = b·c. This means that if we define 1/0 = 1 then 0 = 1.
To be fair, this is perfectly consistent, except everything in our system is equal to zero.
I mean, what the mathematical definition of division says is not that 1/0 is indeed something and that that something is "undefined" or "NaN" or anything else really. What it says is "I cannot do 1/0, the division operation a/b does not apply when b is 0".
So 1/0 is not a thing in itself in mathematics; it's something which cannot be operated.
Now, back some time ago, "division by zero" simply threw an error. It signaled "this is not something that can be done". undefined, NaN, anything else, including 0, is not really something that has a mathematical justification. It's merely a practical approach to encapsulate that error into some form of pseudo-value to control it to some extent.
Personally, I don't really see how 1/0 = 0 is better than 1/0 = NaN or "undefined" or "Infinity".
For example, say if a zero divide happens with normalized values. Meaning the immediate result is only off by one at most. Odds of catching that are probably low. Meanwhile, a NaN will infect all numbers that come into contact with it, bubbling up faster...
Under this system, programs are easier to debug if they use bigger numbers... That property does not seem like a win to me.
If I attempt to open a file, but an access fault occurs, I'd rather be told the fault and given a chance to recover than receive an empty file.
I find particularly interesting Leslie Lamport's comment that "Since 0 is not in the domain of recip, we know nothing about the value of 1 / 0", which I think is the most correct mathematical stance.
Then again, I think it is all a red herring. This (Pony's decision) is not about the mathematical definition of division. This is about the trade-offs computational systems do to manage the situation.
The issue with special definitions is you can use them to say anything you want, turning regular, common operations into weirdness.
What does it mean to take a factorial on the real numbers, or to add only on the even integers? In both cases, we're twisting what are generally accepted mechanics and domains into something else to get a funny result, like 1/0 = 0.
He gets to that point here:
" We can say that division’s domain is all the real numbers except zero. This is what we do in our day-to-day lives, and the way that Agda and Idris handle division. We can choose some value that isn’t a real number, such as “undefined” or infinity, and say x/0 = <whatever>. This is what some mathematicians do with the Riemann sphere. We could choose some real number, like 19, and say that x/0 = 19. This is what Isabelle, Lean and Coq do.
"
So, tiny change in definition --> big change in outcome.
*I'm an undergrad
The answer is that the gamma function is the unique logarithmically convex extension of the factorial function.
I don't think that's exactly the same problem.
It is perfectly valid to define an operation on a particular domain. Defining an add operation on even numbers is perfectly fine (If you had said odd, well...). Factorial on real numbers is more or less the Gamma function. The problem here is that the operation is defined on one domain...
> We can say that division’s domain is all the real numbers except zero.
...but then, for some reason, it is decided that will will be applied on a different domain. That is, that we're going to apply on all real numbers including zero. So now we need to modify the definition of the operation. And the thing is that, the new definition, created to accommodate the new domain, usually tends to produce friction precisely on the new elements added to the domain. We excluded zero initially because that way the operation was defined in a simpler way. Now, having to consider zero implies that our definition becomes more "complex".
Now, we can change the definition in different ways. Is any one of those objectively "better" or "worse" than the rest? Well, that's what the discussion is all about.
I've always thought that programming languages should have a nonzero number class in the vein of unsigned and float and that division should only be defined with a nonzero number class as the denominator. To divide a 64-bit float by another float, you'd have to either specify it as nonzero in the type or convert it somehow. Make division by zero impossible with the type-checker.
Setting your programming language to evaluate x/0 = 0 seems evil. You're taking a bug in the programmers code and then hiding the fact that something logically unsound happened and in a way that would be very difficult to debug or detect.
In fact, this is what happens when C# think it's being clever by returning infinity as the result of a division operation (which isn't even a number). It's a bug that winds up infecting every function that relies on that code without ever throwing an error.
A programming language shouldn't return NaN or Infinity or anything like that when it encounters division by zero. It should demand you use error-handling or ensure it can't happen. If it does happen, it should tell you exactly where the problem occurred and not assume that some arbitrary value will work just as well.
I'm on the Pony core team and I generally agree with you. The problem is, if you want to allow people to write high performance code, you are penalizing them becaues, for floating point:
1.0/0.0 is infinity. That's not C# being clever. That's C# following the floating point standard and using the math that the computer provides. To not use that standard means that you make every use of division slower, even if the programmer has in some way assert and otherwise knows that that they won't be dividing by zero.
Note that 1/0 is integer math and for that there is no standard and its undefined behavior. You can't return NaN or Infinity for that without boxing numbers which is a performance hit and would be penalizing programmers who want/need to go as fast as possible.
Explicit might be better than implicit most of the times, but it is possible to be too explicit.
That said, we can achieve this, although it's probably a bit trickier than you'd think at first. The best option I know is called "refinement typing", which basically allows you to specify types as subsets (ie "the set of integers which are not 0") and automatically verifies whether your functions actually return a value in the subset.
Liquid Haskell[1] is an example of a system you can use today to play with the idea. It uses an automatic logic solver (Z3) under the hood to automatically prove that your refinements hold. (For example, I believe it should be able to automatically verify that the function f x = abs x + 1 will never be 0.)
Worse, division by zero is just the tip of the iceberg. There’s overflow and underflow (which both can happen with addition, subtraction, multiplication, and division) and invalid operations (0/0, √-1, arcsin(2), etc), and you might even want to extend this to detect inexact computations (e.g. to properly report whether you are sure a later division by zero actually is a division by zero)
IEE754 ‘won’ because it is relatively simple to use and many times good enough for real use.
let a = 17
let b = 10
let c = 7
let d = a - (b + c) // throws AddsToZeroException
> Make division by zero impossible with the type-checker.Forcing programmers to consider division by zero when it occurs can be done simply by having the language compiler force handling of a divide by zero exception when division occurs.
He does not prove that this is a useful representation, only that given his own axioms, this can be considered mathematically correct.
Very practical issues with 1/0 == 0:
- This result is counter-intuitive, took building a custom fields and responding to the incredulity of all. - The main reason this is counter-intuitive is not mentionned in the post: division is no longer monotonous.
1/0.5 = 2 1/0.25 = 4 1/0.0001 = 10000 1/0 = 0
This is calling for an Ariane 5-type crash because an underflow error caused a value to suddenly fall from 10e6 to 0.
That’s the opposite of what happened. A value overflowed, which triggered an exception, which caused a module to emit diagnostic information, which was misinterpreted by other systems as flight data. Interestingly, the value which overflowed was part of a system which was only used until LT+7, which had aleady passed…
In short, if the overflow had simply saturated or wrapped around to 0 or given some other garbage result, the Ariane would have not crashed.
But that isn't my only qualm.
In the construction of the fields, there is a simple definition of the division function. It is intrinsically the solution of a = r * b, where r is the unknown. If a is nonzero, and b is zero, then r, the ratio, is said not to exist. Put another way, there is no real value of r that can be chosen that satisfies r * 0 = a. See [1] for example, as 0 is not an invertible element of the field over the reals.
So ... I can't say I agree with their choice of 1/0 == 0. They may be free to choose any value they wish, but then from an aesthetic viewpoint, do they really want to surprise the user?
Exactly. That's the definition of division. Here, he is introducing a totally different definition and claiming it isn't wrong because it is consistent. I mean, by his logic I could define division as a/b := a-b and claim it is consistent and therefore not wrong, but that is obviously not division...
I'd like to point out that, as I stated in another comment in this thread, the author's supposed refutation of the inconsistency inherent in division by zero within fields is incorrect. Their refutation is as follows:
The problem is in step (3): our division theorem is only valid for c ≠ 0, so you can’t go from 1/0 0 to 1 * 0/0. The “denominator is nonzero” clause prevents us from taking our definition and reaching this contradiction.*
This is a strawman, and it's not the way a formal proof that division by zero in fields is undefined would proceed. First and foremost, the conceptual purpose of the proof is to demonstrate that you cannot define division by zero while still retaining the algebraic structure of a field. Trying to refute this point by stating that the proof cannot make use of division by 0 is begging the question. The whole point is that the proof shows you any way you define division by 0 is going to compromise your definition of a field, or it's going to make fields with nonzero elements impossible.
What the author provided is a very contrived strawman for refutation that belies the actual point. You can define division by zero if you'd like, by using wheels, or extended number systems based on fields (i.e. positive and negativie infinities in the extended Real number system), but you cannot do it using fields. In fact, the modern, axiomatic definition of a field explicitly excludes the unit 0 from the otherwise sane rules of multiplicative inverses.
More generally, I'd like to make a couple of observations from both a philosophical and a practical standpoint. First, mathematics does not give us true statements about the world, it gives us consequences that must follow if we accept various axioms or definitions. You can define division by 0 if you'd like, and you can even do so in a sane and useful way. But you will not have a field. But much more importantly, it's conceptually unsound to base an argument about the practical, programmatic behavior of an undefined operation based on imperfect arguments about abstract mathematics. Technically speaking, computers don't even deal with real numbers. If you find yourself mounting a defense of your programming language's behavior by running through the first lecture of a real analysis or linear algebra course, something has gone very wrong with your enterprise.
But it does neither. All field theorems pertaining to inverses/division take the form `x ≠ 0 ⇒ ... x⁻¹ ...` None of them is compromised.
> In fact, the modern, axiomatic definition of a field explicitly excludes the unit 0 from the otherwise sane rules of multiplicative inverses.
Exactly, which is why defining 1/0 = 0 (or 42) poses no problem to fields (BTW, the Lean proof assistant defines 0⁻¹ = 0 for fields, even though it's not the inverse and 0*0 = 0).
The whole problem is one of formalization. "Undefined", as it's used in mathematics, is very much informal. It is used to say that a certain expression, 1/0, while "grammatically" correct, is meaningless. Formal systems simply cannot do that: the expression 1/0 must either be ill-formed (can be done with dependent types but is often inconvenient) or it must mean something in the semantic domain of the language. Different formal system define what that something is, but whatever it is, it is no longer "undefined" in the informal sense.
But that isn't the same as defining a division operation.
Isn't a consequence in itself a true statement?
https://en.wikipedia.org/wiki/Principle_of_least_astonishmen...
However:
> One of developers of Pony reached out to me. They’re planning on writing a more in-depth explanation of why Pony chose 1/0 == 0, which I will linked when available. 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.
I am looking forward to this write up.
For those who still object to the argument, would you object to me defining the piecewise function f:R->R defined to be 1/x for x =/= 0 and 0 for when x=0?
Are they? I see nothing in the article about the negative consequences of surprising users with silent failure, for example. Or the fact that it makes little sense in real-world scenarios.
would you object to me defining the piecewise function f:R->R defined to be 1/x for x =/= 0 and 0 for when x=0?
What do you mean by objecting to you defining a function?
That's because the article is purely about the mathematical consistency of using 1 / 0 = 0. It explicitly and repeatedly mentions that it's not about real-world logistics.
> "What do you mean by objecting to you defining a function?"
The author is defining the division function with this special mapping, f, which includes 0 in the domain. Damn near everyone in this thread is confusing that definition with defining the multiplicative inverse, 0⁻.
It's an understandable confusion, but also honestly getting pretty frustrating. That's what he or she is getting at.
What proportion of the time will a divide-by-zero operation be a symptom of a bug, where evaluating 1/0 as any valid number will make it harder to identify that bug?
There might be a mathematical justification for 1/0 = 0, but the computer science justification seems tenuous at best. The overwhelming majority of the time, I want divide-by-zero to throw an exception or evaluate to NaN. I'd far rather deal with the edge case of intentionally dividing by zero, rather than the not-at-all-uncommon case of unintentionally dividing by zero and getting unexpected behaviour as a result.
If you’re dividing floats, you will get NaN or Infinity as expected. Just like how, if you overfloat a float, you’ll get Infinity. Floating-point has well-defined semantics, which CPUs are required to adhere to, and languages just expose.
If you divide “CPU integers” by (CPU integer) 0, the result is undefined. Just like how, if you overflow a “CPU integer”, the result is undefined. There is no standard semantics being adhered to. There is no equivalent of IEEE754 for CPU integers. There is only convention, and convention has no universal answer at these edge-cases. Thus, a language is free to do whatever it wants in response to either. There are no formal semantics to expose.
Usually, a language that calls itself “safe” will choose to make CPU integers behave a lot like real integers, by generating checks and throwing exceptions for the edge cases.
And usually, a language that calls itself “low-level” will just expose whatever semantics the CPU integers of its host platform already possess. In the case where there’s still an impedance mismatch (i.e. in the case of division by CPU-integer-0 where you don’t actually get any output into a result register), the language has to make something up. To “go with the flow” of how CPU integers work, probably it should be something fast.
Thus, 1/0=0 kinda sorta makes sense. It is an operation on the field of “CPU integers” that vaguely “fits in” with the existing ones. No mathematical relevance, but just fine from the perspective of someone e.g. seeking to create a higher-level abstract machine targeting the Pony compiler, where you’d still just insert a divisor check before doing the division op if you wanted the result to make any sense.
A basic theorem is that for any field we must have that a/b = c/d if, and only if, ad = bc. See theorem 2 in this classic text here for a proof[1]
So if 1/0 = 0, then we also have 1/0 = 0/1, which implies 1 x 1 = 0 x 0 which is a contradiction. But then we also have 1/0 = 0/x for any x, hence we also have shown x=0 for any x, which is an infinite number of contradictions. Is this a problem which is addressed in the post that I missed?
By the way, I have spent more time than it's polite to discuss in public studying the topic of dividing by zero, and also what 0/0 means. If you are also interested in this topic, the best youtube video is by Mathologer and I highly recommend it to everyone. See here [2]
[1] https://books.google.com/books?id=3ApEDwAAQBAJ&lpg=PP1&pg=PA...
x = a/b + c/d + e/(f*g/h)
I need to check: if(b == 0 || d == 0 || f == 0 || g == 0)
rather then: if(isFinite(x))
Or whatever function/equality check is appropriate, or a try/catch if it throws an exception.I have to say that using a single check on the result seems significantly less prone to bugs then having to check all the values that could produce an invalid result.
Still, I agree that a NaN value(s) is(are) preferable for this reason.
I would REALLY hate having to write a geometry library without NaN values. Both because of error checking, and the fact that NAN/INF is often the 'correct' answer when it's the result of a calculation.
One of those proposals which can only be made when you don't take time to understand and appreciate use cases the current solution was created for.
It makes sense if we have signed zeros; then +0 represents an infinitesimal positive value. IEEE 754 has signed zeros, though the signs may sometimes not match programmers' expectations, e.g. 1 - 1e-50 - 1 == +0.
Maybe 1/+0 == +inf is reasonable, but would you agree that there's no reasonable answer for 0/0? So IMO, reasonable languages must define division as a partial function, or a function that can throw an error (or return NaN, Nothing, etc).
And if division is a partial function anyway, it seems best to not allow division by zero at all, since even 1/+0 == +inf can have unexpected consequences.
> > These things are conventions, exactly the same as announcing that x^-n = 1/x^n and that x^0 = 0.
Say what? x^0 is 1, and not by convention, other than in the 0^0 = 1 case.
It's around since 2013 and was found in a unit test coverage improvement effort in Feb. 2018.
Given that pypy is mostly used for hard-core math and scientific computations, how the hell this bug wasn't found earlier by a user ?
My bet is that ZeroDivisionErrors are very rare. Do you even remember a ZeroDivisionError in production code ? I don't.
1/0 is my dirty little trick to add a breakpoint when I'm too lazy to launch a debuger. Why does this work? Here again, because nobody never catch ZeroDivisionErrors, because they don't happen.
So, what a fuss for such a tiny convention. Granted, it breaks the math correctness, as do ±Infinity. CPU integer math is broken anyway. In what world does 2^31 - 1 + 1 == -2^31 ?
I took a fairly large, used and old repository, Django and I search for ZeroDivisionError:
https://github.com/django/django/search?q=ZeroDivisionError&...
guess what:
- 4 occurrences in the tests
- 3 occurences in the issues
- last, but not least 1 occurrence in the code:
except ZeroDivisionError:
result = '0'But you try to slide one little real number without an inverse by and everyone freaks out.
There is no proof in this post that 1/0 = 0 maintains consistency. Rather, it contains refutations of one or two arguments that claim inconsistency, along with appeals to authority.
However, while I'm a bit rusty, I think it basically has to be true. For there to be an inconsistency, 1/0 = 0 must either
a) imply the negation of some previous theorem of arithmetic or
b) imply that 1/0 = x, for x != 0.
I think a) can only be true by way of b), since no existing theorem of arithmetic involves the expression y/0 for any y. I confess, I don't know the right way to prove that b cannot be a consequence, but it doesn't look like one.
Edit: I think it's just trivial to take a model of the existing axioms, then add n/0 = 0 to the division relation.
(a+b)/c = a/c + b/c
(in particular, for C != 1, as the author claimed it would work not just for 0 for for any real number)
Now to say this is true you have to say "unless c = 0", whereas before that was automatic from the definition of division.
let a/b mean, in my programming language's syntax, = { a divided by b when b is not zero, or 0 otherwise.
Yeah, it's not "mathematically invalid," but it punches a hole in the fidelity between numerical methods and the math that they approximate.
As the denominator gets smaller, the result is bigger and bigger and bigger. The closer the denominator gets to zero the larger the result. Why would we want to pick an answer that is small when instead it should be larger than any number?
One might argue that 1/0 should be MAXINT or IEEE 754 plus infinity due to the limitations of our hardware, but I’ve never wanted 1/0 to result in zero in any program I’ve ever written.
I would prefer that it be treated as an error or possibly some reserved value that behaves like INF while still being an integer in the programming language.
a=x/N
b=y/N
If a==b, then x==y
No longer true, with this change. Essentially a variety of mathematical properties of numbers in the Real space don't hold when you allow division by 0.
In practical terms, that _does_ have an impact, because people might use those shortcuts without realising the system they’re operating under doesn’t allow it.
But you picked the wrong quote to make that point under.
If I divide one thing into zero groups, how many items do I have in each group? Zero. Edited per comment below.
If I were really pedantic, I'd say that in the above, it's almost like the input "1" is not being divided, but grouped.
The funny thing then, is that if I am grouping instead of "dividing", then if I enter 1 modulo 0, I'd expect to get 1. i.e. the modulo zero should be an identity function: 1%0 = 1, c%0 = c.
However, nobody stops people from defining their own Algebras or Fields on languages that support it. On C++ this should be a no-brainer, if the sentiments of the article is true that consistency is no problems. Being a "man of industry" myself, 1/0 throwing or at least becoming infinity doesn't seem a problem to me. Similar to not using gotos or so.
FWIW, JavaScript an underrated language when it comes to weird corner-cases does the right thing. It doesn't throw an error, instead the result becomes Infinity. When I multiply it with a finite number it stays Infinity. When I multiply it with 0 it becomes NaN which is totally sound because in a real-world application one could come to these numbers because of limitations of the storage. 0.00000......1 becomes 0 thus the real result of 1/0 x 0 in that case can indeed be literally everything. The beautiful thing about JavaScript is how it continues to handle this, when I say 1 < NaN or 1 > NaN it stays false. So the algorithms are likely to fail much more graceful than in other languages.
The issue here is hardware integers, which have no signalling bits beyond possibly sign.
The convention that x^0 = 1 (including 0^0 = 1) is genuinely useful because it removes a case distinction from lots of combinatorial formulas. Hence this: http://tinyurl.com/zeropowerzero
x^0 = 0 seems to me to be - I won't say "wrong" because you're free to define things the way you like - in general less useful than x^0 = 1.
For similar reasons, (0 * log 0) = 0 makes sense when computing entropies and stuff in certain machine learning applications. This one can also be justified as the limit of x * log(x) as x -> 0 is 0, but I've seen at least one person just trying to define "log 0 = 0" which I consider less elegant; in the kind of application where this convention is useful, you'll rarely if ever see a log 0 that's not multiplied by a plain 0.
Where it gets really weird is when you do KL divergence and can end up with 0 * log (0/0), but again you can save a case distinction by just declaring this to be 0. The elegant way to do this is to define d(p, q) = p * log(p/q) when p, q != 0 and 0 otherwise. In this particular application, just going with 1/0 = 0 seems to work fine in practice as in a term q * log(q/0) you can rewrite the whole thing by flipping the sign to get 0 * log(0/q) in that term, which we have already declared to be 0 (whether or not q is 0 itself).
In this case it's not a bug in the code, it's being able to handle probability distributions with mass 0 on some points at the level of arithmetic rather than as a special case in the formula.
If you set 1/0 = 1 (as the author claimed causes no inconsistency), then (2 + 1)/0 = 2/0 + 1/0 is false
If you come to rely on division by zero behaving a certain way (as happens to all features/bugs of any language), then suddenly silly decisions about how to implement a formula can cause wildly different behavior. Good luck refactoring!
Therefore (2 + 1)/0 = 2/0 + 1/0 is not even false, it just means nothing.
BTW, you didn't mention Infinity explicitly, but it would lead to the same inconsistency, if you think about it:
(2 - 1)/0 = 2/0 - 1/0
:)
While reading the article and the comments I realized that it will work perfectly in my NISC processor [1]. I just need to update my specification.
To extend the simple first specification to include multiply is very simple. I just need to have a MUL register which, when written, multiplies the written value by the value in the accumulator with the low part of the result being left in the ACC and the high part in the MUL register.
Extending to include integer divide is a little more complicated. I will have to add a DIV register and a DIVEX register. Writing to DIV will divide the value in the ACC by the value written leaving the result in the ACC and the remainder in the DIV register. DIVEX will contain the address of the routine to call when division by zero is attempted which means that DIVEX must be loaded before division is used. If I specify that DIVEX is initialized to zero by the hardware and that DIVEX==0 means that divide by zero leaves zero in the ACC and the remainder (former contents of ACC) is left in the DIV register then 1/0 = 0 will be the default behavior with the ability to change that behavior simply by writing the address of the divide exception handler to the DIVEX register.
I leave it as an exercise for the reader to determine how to deal with this in high level languages. I can fully support it in machine code and assembly language.
For instance, I made a game, and I might accidentally end up with a value that won’t modulus correctly due to zero denominator. In that app, mathematical accuracy for this single outlier does me no good at all because it manifests as “might crash randomly” when I want “doesn’t crash, period”. I therefore wrote a “safe modulus” routine that essentially guards against zero and makes a command decision to return a value. The added robustness against crashes is preferable, since I may not know if I have found every stupid case of the math accidentally working out to exactly zero.
In fact, more generally, exact zeroes mask lots of bugs. They are often default initial values, meaning you might not notice something working accidentally. Sometimes I use a really tiny float as my “meant to be zero” to distinguish, e.g. 0.0001 means “item was supposed to appear at coordinate 0”, that way anything that accidentally ended up at 0 is easier to detect.
Edit: it seems that Pony is only doing this for integer "division", which isn't regular division anyway, and the standard rules don't apply.
On the other hand, the author of the article seems to push for a general, mathematical definition of 1/0 as 0. First of all, good luck with that, as there is no standards-issuing body in mathematics. And whether the author likes it or manages to overturn a very long tradition, division is defined when we grow up as multiplying by the multiplicative inverse, with some occasional notes like "Division by zero is undefined for the real numbers and most other contexts [1] or "In general, division by zero is not defined [2], pointing to settings where clearly you won't find consensus.
I refer to when we grow up because we should not forget the intuitive definition Wikipedia gives first and, I guess understandably, MathWorld gives last: "separating an object into two or more parts". As we know, this restriction of two parts can (more and then less) intuitively be loosened to one, negative numbers, rational numbers, real numbers, etc., but only arbitrarily for zero. But then again, even if we unanimously agreed on one value, what does it give you?
For real numbers though, division by zero will always diverge or be undefined, no matter how you represent them. All computations on real numbers are continuous and there is no continuous extension of division which includes 0 in the domain of the denominator.
One can get around this by switching to projective reals (essentially reals extended with a new element that's both +infinity and -infinity). This is no longer totally ordered and has some other problems, but it's actually a great choice for a lot of numerical computations. Yet, I've never seen something like this implemented in hardware, aside from John Gustafson's unum concept.
But having said that, I do not remember when I last saw a Divide-by-Zero-error in the wild. In practical terms, this is the last kind of bug I worry about.
https://twitter.com/westurner/status/960508624849244160
> How many times does zero go into any number? Infinity. [...]
> How many times does zero go into zero? infinity^2?
What value does 1/x approach?
What about 2/x?
And then, what about ∞/x? What value would we expect that to approach? ∞(±∞)
The idea behind this is you're never going to get the computer to program itself with exceptions, so better not to let it produce code it will not be able to execute fully without human intervention.
infix operator /? : MultiplicationPrecedence
extension FloatingPoint {
static func /? (lhs: Self, rhs: Self) -> Self? {
return rhs == 0 ? nil : lhs / rhs
}
}
extension BinaryInteger {
static func /? (lhs: Self, rhs: Self) -> Self? {
return rhs == 0 ? nil : lhs / rhs
}
}
10 /? 2 ?? 0 // -> 5
10 /? 0 ?? 0 // -> 0
10 /? 0 ?? 10 // -> 10/?, *?, +? and -? are coming to Pony. We call them "safe math operators" and will error on integer overflow, underflow, and division by zero.
I see this with investments that have infinity ROI. If you get into an investment using only other people's money (OPM) and you make a profit then you have made x / 0. Saying I made an infinity return makes more logical sense than I made 0 return.
There's a saying about the difference between a mathematician and an engineer. If there is a $100 bill on a table and each step you take must be half the remaining distance or less the mathematician will never get there, but the engineer will get there no problem.
It's a non-sensical debate IMO but from the investing scenario that I described above, for all practical purposes, it makes more sense to consider it infinity.
Note that this only works if the denominator approaches 0 from a positive side. Otherwise it's 0. This is the idea behind the extended real numbers.
The function sqrt(x) is undefined for negative x in the real number system. Is it therefore zero? No. Just because a value is not in a domain doesn't mean just pick one.
Most math textbooks I know and Wikipedia define division as the inverse of multiplication. You are saying that there is a distinction, the inverse of multiplication is not the same as division: you can write 1/0, but that is not the same as 1 * 0⁻.
This is unconventional to say the least, and the only argument you give to make this destinction is your desire to define 1/0 as 0.
However, to extend an operation usefully to a larger set of numbers, we can isolate some properties that we want to keep, for example:
1. Division is linear in the first argument: (ab + c)/x = a(b/x) + c/x, for all a,b,c,x.
2. If x has a multiplicative inverse, then x * (1/x) = 1.
3. Taking reciprocals twice gets back to the original number: 1/(1/x) = x for all x.
The choice 1/x = 0 is consistent with all the above, and so probably still “useful”. What properties do you want out of a division function?
Perhaps it's not a good design to have division as an operator a programming language? Just like
Maybe<int> i = ParseInt("foo")
one should also do this for division:
Maybe<int> q = Div(n, d)
Is there any language that does this (returns an option for default division?)
Obviously, this is also the case for ALL other operations on finite number types because of overflow. But overflow is MUCH rarer than division by zero issues, at least in my experience.
So anyway, IANAM. What I learned was that 1/0 is infinity. And in software, that generally means a divide-by-zero error. But I also learned the utility of making approximations and exploring behavior at limits. And 1/x obviously increases exponentially as x approaches zero.
So how, then, can 1/x all of a sudden be zero when x is zero? It makes no sense to me. Call it undefined if you like. But it's obviously not zero.
<rant> I see a lot of software engineers trying to get rid of the cost of writing robust programs. It's not possible. Stop trying. You are either safe, or you are not. You either handle all cases, or you don't. </rant>
But, falling back to 0 is not the correct error handling result for all situations. In other situations, an error is the correct result. The language designers should include proper division, and this style of division should be labeled a "safe" division; or, a division that throws an error should be labeled an "unsafe" division.
I have serious doubts about a language if it chooses one-size-fits-all error handling like this.
It may be true that 1/0=0 can be part of a reasonably consistent system, but that doesn't mean it's a good one. Can we define 1/0 as ∞?
Given a/b = c, your intuition of finding c is to get a number such that c×b = a.
c×0 = 0 has an infinite number of solutions, 0 being a particularly practical one.
c×0 = 1 (and more generally a≠0) has no solution, especially in the reduced explanation of multiplication as taking integer multiples of a value.
(Also note that having 0/0 = 0 maintains symmetry and continuity of the function y = 0/x.)
Basically the reason why 1/0 = 0 is wrong is the fact that you cant reproduce 1 by taking the answer 0 and multiplying it by 0 to get the number being divided. Which goes on to break a bunch of other fundamental rules of mathematics.
That is to say, if you took 6 / 2 = 3 you can reverse the division by doing 3 * 2 = 6.
However if you have 6 / 0 = 0 and you were to try and reverse it then you would have 0 * 0 != 6. Then you have bizarre circumstance where everything decided by zero logically equals the same thing. Where (2 + 2) / 0 all of a sudden equals (Einsteins laws of gravity) / 0. And there is no logical way to really explain what that is supposed to mean.
Also if you try to use limits to find a solution of f(x) = 1/x where x is defined as the limit of 1 and -1 as it approaches 0 from both sides. You end up with the answer that is even more confusing when you try to graph it.
That is x = +inf and -inf. Which on a graph is represented as two curves where the limit as 1 -> 0 from the positive side of the x axis curves up along the y axis to infinity without intersecting where x=0. And from the negative side where we approach 0 from -1 we end up with a curve that moves down the -y axis without intersecting at 0.
So frankly, n / 0 = 0 just doesn't make sense because just trying to approach it from both sides suggests that the closer you try to move to zero from both sides the further apart the answer moves away from converging together at the origin. Which would be expected if n / 0 = 0 was actually true.
So at least that's something
It's also nothing. :-)
When a CPU is asked to divide by zero it generates an exception. This exception would cause the OS to jump to the interrupt handler in the IDT for divide by zero exceptions. This handler generally results in the OS terminating the process. Does the Pony runtime register its own signal handler with the OS for a divide by zero exception?
1.0 / 0
=> Float::INFINITY
and 0 * Float::INFINITY
=> Float::NAN
I think infinity is a more intuitive result, but most of the time i get this as a user i would rather see 0 (bought books per month: Infinity). As a programmer i like to get an error, because i forgot to handle an edge case...It's like WWE Raw Smackdown, Hulk Hogan vs. The Rock and everyone has to take a side.
And it's literally Friday night.
Sorry we're not supposed to comment like this buy y'all (I suppose 'we') are a really funny bunch sometimes.
Another interesting example is array access. What should a[len(a)+1] be if your language doesn't have runtime errors?
Elm returns a Maybe type. However, this would be very awkward if you're actually doing a lot of calculations using arrays.
[Edit: Pony is different.]
Because of this rule, defining / as a partial function forces the user to fence every single division in a try clause.
Using boxed integer as Elm does is not an option as Pony targets high-speed computing, not browser rendering. Different fields, different trade-offs.
Defensive users can well define `div(I32, I32): I32?` that throws an error and use it instead of `/`.
As repeated in this thread, 1/0 is undefined because nonsensical. So, it's OK to have any sane, consistent and well documented behaviour. At least with Pony, the user can choose speed and convenience when they're sure there is no possible 0 at this place. Well, TBH, I think it's always the case. Because there is no such thing as a division by zero, you're algorithm is simply wrong and unsound if it ends up dividing by 0, and that's where this case differs from integer overflow
Note: in Pony literature, "partial" means "can throw an error". "partial" as in "doesn't apply to the whole domain of the arguments types"
I prefer this because if you have a function converging on 0 then you will see growth towards Inf - then Inf - rather than growth followed by a cliff; which could then propagate into all your other calculations and move you from the very small to the very large in an instant.
From this definition, I'd guess that pony isn't optimized for numerical code and floating point operations, because there it tends to make less sense.
1.0 / 0.0 = infinity per the standard.
1/0 = undefined behavior and is left to language implementers to decide how to handle.
>>> 1 / 0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
>>> 0 / 0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
>>> 1 / 0.0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: float division by zero
>>> 0.0 / 0.0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: float division by zero
Meanwhile, in Elixir v1.7.1... iex(1)> 1 / 0
** (ArithmeticError) bad argument in arithmetic expression: 1 / 0
:erlang./(1, 0)
iex(1)> 0 / 0
** (ArithmeticError) bad argument in arithmetic expression: 0 / 0
:erlang./(0, 0)
iex(1)> 1.0 / 0.0
** (ArithmeticError) bad argument in arithmetic expression: 1.0 / 0.0
:erlang./(1.0, 0.0)
iex(1)> 0.0 / 0.0
** (ArithmeticError) bad argument in arithmetic expression: 0.0 / 0.0
:erlang./(0.0, 0.0)
Meanwhile, in Ruby v2.5.1... irb(main):001:0> 1 / 0
Traceback (most recent call last):
3: from /Users/mpope/.rbenv/versions/2.5.1/bin/irb:11:in `<main>'
2: from (irb):1
1: from (irb):1:in `/'
ZeroDivisionError (divided by 0)
irb(main):002:0> 0 / 0
Traceback (most recent call last):
3: from /Users/mpope/.rbenv/versions/2.5.1/bin/irb:11:in `<main>'
2: from (irb):2
1: from (irb):2:in `/'
ZeroDivisionError (divided by 0)
irb(main):003:0> 1 / 0.0
=> Infinity
irb(main):004:0> 0.0 / 0.0
=> NaN
Meanwhile, in PHP v7.0.8... > echo 1 / 0;
>
INF
Division by zero :1
> echo 0 / 0;
>
NAN
Division by zero :1
> echo 1 / 0.0;
>
INF
Division by zero :1
> echo 0.0 / 0.0;
>
NAN
Division by zero :1
Meanwhile, in Node v10.7.0... Infinity
> 1 / 0
Infinity
> 0 / 0
NaN
> 1 / 0.0
Infinity
> 0 / 0.0
NaN In [1]: 255 + 1 is 256
Out[1]: True
In [2]: 256 + 1 is 257
Out[2]: False
Every language has its inconsistencies, when it comes to math, because they run on real-world computers. All these are "slow" interpreted languages that allow runtime failures and don't use plain machine integers. Just a different trade-off.Anyway, the Inf/NaN trick is not better than the 0 trick in my opinion, because all subsequent operations result happily in NaN. I've never seen in any NaN language a program that checks for NaN the result of each and every division.
with a NaN-kind of language, it would be:
a = b/c
if a == NaN:
print "oops"
with a 0-trick language: a = b/c
if a == 0 and c == 0:
print "oops"
not THAT different, IMO :)So, there's one data point in favor. :-)
Out of curiosity, could you describe what you were doing and why 1/0 = 0 would have returned the correct result?
y = 1/x
will result in 2 nice asymptotes with a dot in the middle (0,0)
1/0 = 0 doesn't make sense to me, even because lim x->0+ 1/x is infinite and lim x->0- 1/x is -infinite
so the value that makes more sense for 1/0 is infinite
The Post JavaScript Apocalypse https://youtu.be/99Zacm7SsWQ?t=2434
So that's it. I read your proof. You try cloaking it in pseudo-mathematical formulation, but what you are really doing is defining division as a derived operator, rather than the axiomatic operator it is in mathematics. I can say 1= infinity in my universe and as long as I am consistent, it is sound.
You're saying this is a useful property to have, and certainly that holds merit in certain conditions, but it is not a correct property.
Your article title should be clarified to state "1/0=0 (in my universe)." But then it wouldn't be controversial would it? After all, this is just an experiment to gain some visibility for Pony, isn't it?
[1] https://en.wikipedia.org/wiki/GF(2)
> "It is totally fine to define 1/0 = 0."
No, it's not, at least not useful. If you define it that way, you will not have a field. Then you don't have +, -, *, and / operations with the commonly assumed behavior. However, it is definitely possible to define some operation such that 1 `op` 0 := 0, just not the inverse operation of multiplication.
If you read the post this is actually exactly what the OP is saying.
I say neigh (sorry if you didn't catch that joke, I'm a little horse).
If x/0 =0 then, since division is inverse of multiplication x= 0*0 this fails to hold for any x not 0
But the story says there is no defined multiplicative inverse of zero (undefined) so that they are free to choose any value they want. And they choose 0.
This is unsatisfactory to me because it seems to break the intuitive pattern of x/n getting larger as n approaches zero from the positives. 1/4 .25< 1/3 .33 < 1/2 .5 < 1/1 1 >1/0 0 >1/-1 -1 < 1/-2 -.5 < 1/-3 -.333
You see that sign flip thingy happening around 1/0?
Maybe I am misunderstanding.
This is playing semantic games. A whole lot does break: / no longer has its usual properties, and if you use those usual properties you can certainly prove false things. It's no different from saying that it's totally fine to define 2 + 2 = 5 and nothing breaks, you then just have to say that 5 isn't the arithmetic successor of 4 any more.
Thought of another reason besides below comment why I was wrong, but thanks goes to below comment too!
If you allow 0 to have a multiplicative inverse 1/0, then the product of 0 and 1/0 must be equal to the multiplicative identity, which is 1. Then it follows that 0 = 0 * 0 = 1. From here it's straightforward to prove that x = y for all x, y in the field F.
A multiplicative inverse is a division. You cannot define division by x in fields without defining multiplication by 1/x and vice versa. The article is plainly incorrect in its attempt to refute this point (and in any case, shouldn't be using field theory to justify its purpose in the first place).
And dividing by 0.5 is breaking into how many pieces exactly?
This explains exactly why we shouldn't divide by 0. The pieces after breakage should, if one is so inclined, be able to be re-assembled into the whole; but, once you break something into 0 pieces, it is gone. Thus at best `0/0` could be defined … but the problem there is not that there's no answer but that every answer is equally good.
You know.. dividing is like breaking something up [..]
No. What it is 'like' is totally irrelevant and actively harmful to consider if you want to be able to get anything serious done with your programming language. This is not a place to start philosophizing. For consistent arithmetic it is important to know which axioms are fulfilled. Division needs to be a mathematically clearly defined operation.I dunno, if I'm dividing a quantized space, which is a fairly common thing to do, its exactly what I want.
> (unless you cast all you numbers to float which is a pain to do).
Using floats just gets you different wrong answers than using integers, unless you happened to somehow have ints to start and want a floating point approximation, which I find is less likely than wanting integer division.
What you really want for exact division where there are integers involved is to have at least one operand specified as a rational; this also takes less keystrokes, if its a literal, than changing it to a float to get an inexact answer.
Related to this, prior to Ruby 2.5, the standard library include "mathn", a library which monkey-patched all the numeric types so that operations on them acted pretty much like Ruby had a Scheme-style numeric tower, but since that has process-wide effects not restricted to where the library is required, that was deprecated in 2.2 and removed in 2.5.
It's not always what you want, but it's pretty reasonable. The alternative would be if it always rounds up, which seems weirder to me. I don't think rounding to the nearest integer makes sense with integer quantities at all, that's strictly a real number operation.
When we first teach children about division we teach them that, "17/5 = 3 with a remainder of 2". It is only later that they learn about decimal points and learn to say that 3.4 is the answer.
Who is mocking anyone though?
The set of integers represented by the integer types of pony can be treated as a field, since they have discrete ranges.
As the article explains, the whole issue about dividing by 0 is the lack of multiplicative inverse. If you define division for 0 as something other than multiplication by the multiplicative inverse, there is no longer an issue doing the division. Hope that helps.
The entire point of the article was suggesting you can define a consistent system without multiplication and division being entirely symmetrical (as they already are not)