Hacker News new | comments | show | ask | jobs | submit login
1/0 = 0 (hillelwayne.com)
650 points by ingve 72 days ago | hide | past | web | favorite | 570 comments



My problem with "1/0 = 0" is that it's essentially masking what's almost always a bug in your program. If you have a program that's performing divide-by-zeroes, that's almost surely something you did not intend for. It's a corner case that you failed to anticipate and plan for. And because you didn't plan for it, whatever result you get for 1/0 is almost surely a result that you wouldn't want to have returned to the user.

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.


An issue with D that has repeatedly engendered heated debate is what should happen when a programming bug is detected at runtime. The two camps are:

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.


> If a program has entered a state unanticipated by the programmer, then there is no way to know how it entered that state [...] and hence no way to know what the program might do next (such as load malware).

... 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)".


> (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)).

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).


The mis-categorization of some exceptions[0] as checked or not is part of why checked exceptions seem like a mistake. The other is when exceptions cross boundaries of human organizations like nested libraries.

[0] NumberFormatException is a RuntimeError as are all IllegalArgumentException, WTF?


Another is that well-known attempts at checked exceptions in practice provide an incredibly limited vocabulary for talking about raised exceptions - usually just unconditional "might raise X" or "does not raise X". If you could say things like "raises anything raised by my argument f, except for X because I catch that" it seems like it might be a much better idea (especially if you can infer those...)


> (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)).

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.


D draws a clear distinction between programming bugs (not recoverable) and environmental errors (which can be recoverable).

Failures of the former throw an Error, the latter Exception.


Great analysis! I think that summarizes a lot of dev arguments. That said, Erlang appears to agree with both camps. If an error occurs, the _process_ immediately crashes (not OS process, it's Erlang Kingo for an actor or a green thread), but the _program_ soldiers on as if nothing happened. The idea behind the design, I believe, is that programmer errors are unavoidable, but that eg a bug in some edge case triggered by uncommon data shouldn't crash the entire server.

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.


Java has essentially the same behavior with threads. If an error occurs in a thread, it will throw an exception that will terminate the thread if uncaught. Other threads are not directly affected. This is typically what you want as threads are usually independent of each other, often processing an isolated request, which may encounter a bug due to bad data or other conditions. Contrast this to languages like C++, where the usual behavior is to segfault and crash the process.

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.


One big difference between Erlang and Java is that all processes (threads) in Erlang are safely contained in private memory, whereas in Java threads occur in public memory. Another big difference is that Erlang has supervisor processes to restart child processes that have crashed. Java just farts itself.


Indeed. Java is in fact a sort of worst-of-all-worlds in that it's even possible to catch things that are really unrecoverable errors like StackOverflowException or OutOfMemoryError. To to uninitiated it might not like appear that catching these is that bad, right? In addition to the problems with finally clauses not necessarily running to completion under such circumstances catching these exceptions can cause extremely hard-to-debug deadlocks because object locks won't be released properly.

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.


In C++ the default behaviour is for the runtime to call std::terminate, not segfault. It's easy to catch exceptions by wrapping the callable in a packaged_task and then the exception is available in the future. The key point is that the exception is rethrown when accessing the result through the future.

Swallowing exceptions is a really poor idea. Does Java really do that?


Erlang originates from telecom, where the behavior you describe makes perfect sense. Equipment should work all the time; you don't want to (or even can't) send out people to debug/restart them.


It's not just programmer error, it can soldier on through some hardware errors, rogue cosmic rays, and arguably the hardest - other people's software errors, like os errors, driver errors, library errors, network stack errors...


Regarding "some hardware errors": This might include operationg conditions you didn't consider; say:

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


Adopting this policy was what destroyed the first Ariane 5.[0] IIRC, a conversion from floating-point to a 16-bit integer overflowed, raising an exception which reset the flight control system. The flight control system was redundant, but both copies experienced the same error. The result that was being computed would not have been used.

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/


To say that either (1) or (2) is always the right choice is unreasonable. It depends on context.

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.


Aircraft and spacecraft all have backup systems for critical software. When software self detects a bug (such as an assert failure) the offending computer is shut down and electrically isolated, and the backup is engaged.

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


And what if the program is the lowest level and getting into an impossible state due to a hardware issue (bad bit). At some level a program has to soldier-on.


Then it sets an I/O pin that pulls the reset switch.


I cant tell ic you're being serious. The appropriateness of that would depend on whether the hardware condition was persistent in which case we have a reboot loop. It doesn't seem resonable for software meant to run in a harsh envirinment to assume prefect rumtime conditions. Do probabilitites and tradeoffs ever get considered when faced with inconsistent state?


If the program is designed to "soldier on", then why not? The question is how is the program designed to react in face of failure: should it fail safe? And is it safe to crash on any error?

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.


I can't say that I'm firmly in either camp.

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.


My comment from when this came up on Reddit, slightly edited for context:

`/`-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.


An example well-defined use case is if you want to compute a harmonic mean, e.g.

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.


Quite the opposite I would say: the harmonic mean can be seen as a very good justification for `1/0=inf` as the pragmatic choice.

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.


I think for floating point 1/0=Inf makes a lot of sense, because divisions are frequently done with continuously-varying properties. Division by integers is different, since you do a different kind of work with it; there is no "very small" integer.


"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"

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.


So, I did forget the correction such that you don't get 2b. The general case is described e.g. in the link below, and this is known as a "zero-corrected harmonic mean". It is actually "obvious" in some cases, but I'll concede that in other cases it's not.

https://www.rdocumentation.org/packages/lmomco/versions/2.3....


Eh? That page says nothing about 1/0 being 0, or anything like that. That function takes the harmonic mean of the non-zero samples, and then adjusts the result by multiplying it by the fraction of the samples that are non-zero. It purposefully avoids dividing by 0. And the result of that function for a==0 and b!=0 is b/2, not b or 2b. So not only is what you claim to be obvious not obvious, it's flat-out wrong.


> `average = 0` with no elements

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.


You have a point, but I think this is a difference in the target markets. NASA can afford to have a thoroughly-tested, redundant, quickly-accessible failure path that restarts and reinitializes the process, and their processes are so thoroughly tested that they can expect their production instances not to have any software failures.

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.


It's not only NASA. Zero is just not a reasonable default value for an average. Imagine average credit card balance for FICO score, average blood pressure, average temperature in a freezer, etc.

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.


It's a value type, so you can't really have NULL. I don't see the issue with those examples; if you have no samples, there's nothing to mislabel. An average blood pressure of 0 across 0 patients is only going to harm 0 patients.


> I don't see the issue with those examples

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.


It seems to me taking averages would be inappropriate in all three of those scenarios.

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.


Everything is relative. $1,000 balance is a lot of money for someone making $35,000, not so much for someone making $350,000. Many independent variables that go into risk scores tend to be ratios or averages, hence zero (or near zero) denominator is possible.

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.


I feel for the typical target market for Pony is going to be doing operations like that in floating point, or perhaps some explicit fixed-point non-integer type in financial cases. Whilst, yes, I can see this causing issues in some small subset of cases, the question really does come down to cost-benefit. Integer division by zero is not the only way for arithmetic to go wrong; would you expect every overflow to be checked too?


https://en.m.wikipedia.org/wiki/Credit_score_in_the_United_S...

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.


There are many different credit scores. The range of valid FICO scores does not include zero, but internal acquisition and behavior scores that banks use might.

But in this case it's irrelevant because average balance is independent variable (input of the scoring model).


You can check for 0 and throw in those cases, and then propagate the error path appropriately. And as they've already said in this thread, they will be providing a checked arithmetic in the standard library to do that for you.


Well, sometimes the average is 0, e.g. (-2 + 2)/2.

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.


If I'm not mistaken, jeremyjh was referring to checking for 0 in the denominator, not the numerator.


Pony requires programmer to handle everything case of every function...except this one case where it will just pick an arbitrary value to handle a case the progrmammer forgot, hiding the very error Pony was designed to guarantee gets solved. Why not allow partial functions everywhere and have them default to 0 where undefined?


That's a terrible default for an average function lol. Once again in nearly all cases, taking the average of a list without elements is an error.


What happens for 0 / 0? Is this also 0?


It's extra zero.


Is this the same as 0 * 1 / 0?


It returns a larger value of 0.


All zeroes are equal, but some are more equal than others.


ε


inf 0


I almost want two different division operations: One where 1/0 = 0, exclusively for use in progress bars and stuff like that, and another one for everything else.

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 / foo


I'm reading the "Pony" tweet quoted in TFA and your comment and I'm left very puzzled: is that really that common to want x / 0 == 0 ? In practice where does that crop up?

You 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.


Practically, it's quite common to not immediately know the divisor. In cases where the divisor is initially unknown but takes an imperceptible amount of time to compute it's better to render 0%. Otherwise you might get a flash of a full progress bar (for example) while the divisor is determined.

Of course, it's context dependent. As others mention, your code might be full of stuff like X / (divisor || 1).


The nicest UX would be to say something like "waiting..." while the denominator is zero. Presenting a zero to the user instead is probably an acceptable short-cut, but at the end of the day, it's a UX decision. Which is why it should be handled in explicit if-else code, that lives as close to the UI as possible, rather than being buried in the semantics of basic numerical operators.


If you dont know the number of elements, shouldnt that be tracked in another variable, instead of reusing the total elements variable and assuming 0 elements means unknown?

Also, if loading total elements takes a significant amount of time, shouldnt this loading also be reflected in the progess bar?


I’ve run into this a bit with progress related stuff. I bet if you looked at progress bar libraries they’d have similar logic built in.


    X / (divisor || 1).
It's invalid code in C, C++ and Java.


Boolean operators returning values is common, especially in dynamically typed languages. The code is valid in JavaScript, for example, and in Python (except it uses "or" instead of "||").


In python, it should probably be corrected with // or 1.0 to make it either an integer division or a float division.


Ah, that's true. Good point.


This is valid C and C++.


It is, but it will always give you a divisor of 1 (because "true" is 1 -- I also thought it would work until I tested it):

    % 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


Some trivia: GNU extensions to C/C++ include a "?:" operator that does what you'd want in this case, e.g.

    x / (divisor ?: 1)


It shouldn't be valid in C++. booleans cannot participate in arithmetic operations. You will get a warning from the compiler if you are lucky.

C doesn't have booleans and treat them as integers 0 or 1, it can do the math and will always return 1.


That's not true. C++ does define an implicit conversion from bool to int:

https://en.cppreference.com/w/cpp/language/implicit_conversi... (under "Integral promotion")

And C has a boolean type as of C99.


What do you expect for that C code?


In the context of this discussion, the idea was that it would act the same way as Python. But it obviously doesn't.


Real example: I'm collecting some quality signals from a corpus, most of which are some form of ratio, average, weighted average, or scaled average of counting various quantities within the documents. If the elements being counted are missing, I want the term involving that quality signal to disappear from the final ranking calculation.

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.


Defining a function would seem like exactly the right thing to do! There's no problem.


Module-scoped operator overloading would be really, really nice in this situation, though.


No, it probably wouldn't, unless you were using integers. Pony still uses floats and returns NaN or Infinity or something.


>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?

Unless you're using unsigned ints, the discontuity will exist no matter what you do, because of negative divisors.


Rust has your back:

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.


This is actually included in the IEEE754 floating point standard - there's a concept of "trapping" vs. "non-trapping" exceptions, where implementations are allowed to decide which exceptions should trap.

Unfortunately, GCC only lets you enable this globally (within a program): see http://www.gnu.org/software/libc/manual/html_node/FP-Excepti....


We will be introducing two sets of integer math operators in Pony. The current which are non-partial and can over/underflow + division by zero == 0 AND new ones that will be partial functions that cause an error on under/overflow and division by zero.


> new ones that will be partial functions that cause an error on under/overflow and division by zero.

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.


There is no golden path here. Checked exceptions for division would make a lot of Pony code objectively worse. Unchecked exceptions are great for PHP, Haskell, Rust or Go, but Pony is trying to do something different - to literally make it impossible to panic in an operation without describing that in the type system. The ergonomics of divide by zero in this context are absolutely debatable, not an issue to dismiss out-of-hand.


Well, except for kernel panics and hardware errors...


You can write user code in Pony that will cause a kernel panic or hardware error?


Yes. Hardware errors, can't really protect from that or a kernel panic. A kernel panic is a panic in non-pony code.


> You can write user code in Pony that will cause a kernel panic or hardware error?

You can always (as in OS with lazy allocation) allocate enough memory to get OOM no matter how safe your language is.


It may be forced to error as a result of one.


Pony is not 1.0 yet. The language is still malleable.


Couldn’t you alternatively introduce total operators that just grow the memory size as necessary instead of overflowing? For me if you’re handling wrapped types, I’d always prefer the numbers to grow into extra memory instead of overflowing or raising exceptions. Erlang, for instance, does this well. Obviously, division may still not be total, though you could define an operator like `//` that is.


A wrapped types library with the corresponding performance tradeoff is something I expect will be added to Pony at some point. There's definitely value in them for a variety of use cases.


Isn't a ternary here nicer?

  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 anyway


I would say it doesn't really matter much as both are readable. I would prefer either the original if/else or the ternary solution over the last solution as I personally think the intent is not as immediately clear in your "max" form. I would optimize for readability over density, unless there is some reason to write it differently (e.g. performance).

just 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 }


Ternary doesn't really look any better. I'd do this in a language that supports it though:

  return foo && bar / foo


I've run into this a lot, but I'm also not sure what I really want to happen. Even for progress bars, the behavior can be different depending on how exactly you're obtaining the denominator -- does it increase dynamically or is it known from the beginning? Is it one of those fake progress bars that just ignores the fact that it'll stay at 100% for the next hour, or would it filling up genuinely mean that the work is actually done? After all, if you have 0 operations total and you've finished 0 of them, are you really 0% done? Wouldn't it make more sense to say you're 100% done? At the end of the day the user is trying to figure out how long they should keep waiting, to which the answer would be "you don't need to wait, there's no more work left to do".


> But similarly frequently, I end up crapping out annoying little bits of code

Isn't this reason the the point of having a utils library with commonly used pieces of code?


Some utilities libraries can be reflections of deficiencies in the language itself and not simply about common code reuse.


Agreed. Both really.


Most of the time when I have to think about what to do with a 0 divisor, I come to the conclusion that

  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).


Wait, when creating a progress Duke you divide in other direction: (things already done) / (all things). Can you give a specific example when it causes problems? My experience of writing code like that is that there a clearer way or I made a mistake somewhere.


Honest question. What is so annoying about that code? It's handling (in an application-specific manner) the special case of dividing by zero (which the `/` operator doesn't handle). I'm not sure of any other way to handle this.


Actually, bar/foo STILL has a gotcha which can throw exception, many developers doesn't think of this.

Just try INT_MIN/-1, can blow most "foolproof" divide logic away.


> But in this case, they are hurting their users far more than they are helping.

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?


Hi I'm on the Pony core team and spent some time explaining the reasoning to Hillel. He hasn't written any Pony but I did walk him through how we ended up where we currently are.

I will be writing up a post about why we made this decision.


The real issue is that Pony doesn't have the concept of an unrecoverable error. All exceptions in Pony are checked, but sometimes if an error happens there's nothing your program can do and it should just crash.

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


It happens in scientific computing with zero bounded signals fairly regularly. Sometimes values hover close enough to zero that the computation results in a/0. It's not desired but the only alternative is a guard that invariably adds a performance penalty. There are other ways around it, such as normalization, but they're not trivial depending on the problem and can actually add other sources of bugs/errors. I understand the author's intent even though I disagree with the result.


1/a when a is close to zero is very large. 1/0 == 0 violates the limit and is just bad math.


Totally agree, in a mathematical proof you wouldn't ignore the edge case just because you think it might work the same way it has for some of your other proofs. It would invalidate everytihng, and in sequential processing, you could really mess up some of the data with one iteration with a 1/0 assumption that completely changes every iteration after it. Bad news.


What the world really needs: "1/0 = 0" in production build, "1/0 throws error" in development build...


That makes your bugs invisible in production. You could be failing every single transaction in the real world, and never know if your tests don't tickle the unhappy case.


Fairly straightforward in some languages (Ruby for instance).


The authors covers this at the end:

[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?


Does Pony have some analogous dodge for logarithms and square roots of reals, or for the gamma function?


Yep. Having had a couple divide-by-zero errors in a complicated simulation, I'm immensely grateful that it fails hard.


> If you have a program that's performing divide-by-zeroes, that's almost surely something you did not intend for

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.


Is this different than integer division or integer over/underflow? I mean, besides that the integer behavior is status quo and 1/0 is not. Genuine question.


Integer over/underflow is arguably less arbitrary. Sometimes Z mod 2^k is really where you want to be working, and the rest of the time at least it's the fastest answer. Whether that's enough of a justification to be a difference? shrug


OK but that doesn't have a lot to do with what TFA is actually talking about.


You're absolutely right. Not only that, but it's impossible to distinguish a bad result like 1/0 vs a good result like 0/1.

Even Javascript is better with "NaN"


To clarify, in Javascript 1/0 is Infinity, not NaN.


You are correct. But it really should be NaN, since 1/ε is positive infinity, whereas 1/-ε is negative infinity.

Oh well :)


JS just assumes the limit direction for you, so 1/0 is Infinity, but -1/0 is -Infinity. 0/0 at least is correctly NaN. (Edit: And I just verified against my memory, Matlab (or at least Octave) does the same thing. While Matlab might get characterized as being for the ivory tower, at least it's had a long history of being used for practical math applications within the tower. Edit2: And anyway this is the defined behavior for IEEE floats. Men of industry use industrial standards. :))


Moreover, 1/-0 is -Infinity. IEEE float has two zeros, positive and negative.


Nope, I'd disagree. Zero isn't an approximation of some epsilon, it's really just zero. It makes sense for the output sign to match the input sign.


What is the sign of 0?

It's 0.


Parent was talking about the sign of the numerator. But... zero in IEEE 754 representation has a meaningful sign bit. 1/-0 = -inf.


Or as IEEE 754 sees it (you can try this in your JavaScript REPL).

  >> 1 / 0
  Infinity
  >> 1 / -0
  -Infinity


e is nearly 0, -e is nearly -0.

So 1/0 is infinity, and 1/-0 is -infinity.


It’s common for 1/0 = 0 to be exactly what you want. Array average for example. That said raising exception would be the only consistent behavior if a language supports that.


It is not valid to extrapolate an average of 0 from no data at all.

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?


This is probably beside the point, but shouldn't the center-of-mass of a massless object be everywhere simultaneously? With no reason to prefer any location, over any other?


The center of mass of a massless object makes as much as "everywhere" being a location of anything.


And in other cases you really do want the infinities. For example fast ray-AABB tests like https://tavianator.com/fast-branchless-raybounding-box-inter...


Common compared to what? Taking an average of an empty array is nonsensical.


I disagree. It’s similar to computing NaN-mean or NaN-sum for an array of all NaN values (which returns 0).

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.


> It’s similar to computing NaN-mean or NaN-sum for an array of all NaN values (which returns 0).

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.


From your response I can tell you don’t do much numerical linear algebra work.

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.


That's all fine and dandy, but conceptually it's wrong to say the average of an array is 0, and can and will lead to wrong results in a variety of cases. I'm sure you can think of a lot of these cases yourself. I think in the history of computer science we programmers have found that there are a lot of convenience shortcuts that make sense in a lot of cases but bite our asses in other. Implicit is fast and fun, but it's nice to have your seatbelt on when the car crashes. Going back to the average case, if you want an average function that returns 0 on empty arrays, fine. But that's not the average function, and you shouldn't call it that way, and names matter, you should call it averageOrZero or something like that.


Why is it conceptually wrong to say the average of an empty array is zero? My undergrad degree is in pure math and my grad degree is in mathematical statistics and I’ve never heard an idea like saying the mean of an empty array is zero is “conceptually wrong.”

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.


> Why is it conceptually wrong to say the average of an empty array is zero?

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.


Sure, I agree they should be communicated. Like, in the docs for “standard” mean functions, and not pushed into “specialized” mean functions, since needing this particular convention is not remotely special, and is rudimentary and expected in 99% of linear algebra and data analytics work, which are the largest drivers of these types of statistical functions.


> It’s common for 1/0 = 0 to be exactly what you want. Array average for example.

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?


The mean of an empty array isn't zero though.


Hi,

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).


> 2) this applies only to integer division wherein division by zero is undefined. 1.0/0.0 uses the available Infinity value.

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.


Hillel is talking about math. Unfortunately computers don't really do "math". For example, math doesn't have overflow and underflow to deal with.

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.


Integers are perfectly fine in math. But they are not a field. Integers modulo something are also well understood in math - but again not a field.


Integers modulo any prime are a field.


Powers of 2 are not a prime though, unless the power is 1 (i.e. we are dealing with single-bit integers).


Underflow and overflow and 2's complement are all just modular arithmetic, which is definitely math.


Computers is the math you get, not the math you want.


Here's another good one for you...

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.


`3/2 = 1` is a consistent answer as long as the programmer understands modulo. The way you're phrasing it makes it sound like there are languages out there that say "`3/2 = 2` because 1.5 rounds up to 2".

just so long as you don't expect a consistent answer between python2.7 and 3.x... >.>


The phrasing is relatively clear to me as saying that some languages say 3/2 = 1.5 (e.g. JavaScript) and some languages say 3/2 = 1 (e.g. C).


Did you mean 2/3?


No. But most programming languages I know of return different results for 2/3 vs 2.0/3.0.


In the mathematical sense, a Field is a Commutative Ring with the added constraint that the multiplicative operation has inverses for every non-zero element.

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.


As pointed out by others, floats work differently from ints in programming languages. Division in ints is usually defined such that if x = d * y + m, x / y = d and x % y = m. You can make this work by choosing the right m for every d, but when y is not 0, there are a few conventions you can choose to pick a sensible d and m. When y = 0, the m = a and d is completely free, so there really isn't a right answer from how division works.

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.


As an aside, I have thought recently that integers should also have NaN value, just like floating point values have. It's useful in statistics to differentiate between not having a value and value of 0.

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.


I like the idea of having integer NaN but I think unsigned numbers are problematic as there isn't any convenient way to encode it.


>2) this applies only to integer division wherein division by zero is undefined. 1.0/0.0 uses the available Infinity value.

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.


Having 1/0 = 0 and 1.0f / 0.0f = +inf makes sense in two applications I can think of. The first would be currency systems in which prices down to the pre-determined fractional component are represented as unsigned integers. In calculating margin requirements division by zero equating to zero would be the correct answer. And two, for double precision continuous variables, any application involving 3D geometrical transforms. An aspect ratio approaching zero in one axis, would approach positive infinity in the other.

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!


We are over in #ponylang on freenode if you ever want to stop by and chat.


This should be the top comment. INTEGERS are not a field. But that kind of makes the 0 choice worse? Even cpus always throw on integer divide by 0, so i don't really see the low level optimization. Does it enable some higher level optimization? Why not x/0 = x?


What about I64.min_value() / (-1)? Why does that give 0? If you're doing wrap-around arithmetic it should give I64.min_value().

https://tio.run/##K8jPq/z/PzG5JL9IwTcxM49LAQjyUssVkotSE0tSNV...


That might be an LLVM bug.

`I64.max_value() + 1` gives the expected result.

I'm going to have to look into that.

Thanks.


Apologies, I realise my comment was a bit snarky. Pony looks like a really neat language: I'll be interested to see what you do with it.

Unfortunately LLVM considers division overflow undefined: http://llvm.org/docs/LangRef.html#id109

If it's any use, you can see what Julia ended up doing here: https://github.com/JuliaLang/julia/issues/8188


I didn't take it as snarky. I was genuinely surprised. Thank you. If you intended it to be snarky, I didn't notice.

And thank you for the additional info, I've updated the issue I opened accordingly:

https://github.com/ponylang/ponyc/issues/2858


I'd like to jump in here and point out that Pony ergonomics are unlike that if any other language; it is significantly different from any other language.

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.


1/0 is not infinity either...


If we assume that we got to 0 because of a rounding error we do know that it should be some positive integer. I think that 1/0 = infinity is a reasonable substitute for an actual value. 1/0 = 0 seems absurd. 1/(a number approaching 0) produces ever increasing integers.

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.


>If we assume that we got to 0 because of a rounding error we do know that it should be some positive integer.

That's assuming we had rounding error from a positive number, not a negative number. Of course, if you are concerned about rounding, you shouldn't be using integer arithmetic.

>1/(a number approaching 0) produces ever increasing integers.

No. It produces a bunch of 0s, except when the denominator is 1 (where it produces 1) or the denominator is -1 (where it produces -1). Unless you're using a language like Python, in which case it produces -1 for all negative denominators.


Actually if your embedded system will stop keeping a patient alive if you crash and the zero value doesn't cause any harm then you don't crash.

There are lots of situations where it makes sense to keep computing even if you suspect the input data is wrong. It's not really good practice to always be crashing. You need to decide how to handle the bad input on a case-by-case basis.


I think "crash" in these discussions generally refers to "abort the current action and return to a safe state".


If you divide something 12 feet long into 3 parts, you don't get 4 parts, you get 3 parts that are each 4 feet long. So, if dividing a number by 0 yields infinity, you would expect that dividing something physical into 0 parts would yield 0 parts, each of infinite size, whatever that means.

Sorry to be pedantic.


It's fine to be pedantic in math. You can't divide something 0 times, there's no solution for that. You can divide something by infinite times, but infinity is not 0. This might help explain things:

https://www.math.utah.edu/~pa/math/0by0.html


but with floating point units, 1.0/0.0 is infinity.


That's not correct mathematically though. You can't divide something 0 times and get any number. Adding zero + zero + zero infinite times does not equal 1.


> That’s not correct mathematically though.

Floating point infinity is not equal to math infinity. Floating point infinity means there was overflow of the representation we are using, not that the number is larger than any known number.

> Adding zero + zero + zero infinite times does not equal 1.

Sometimes it does. That’s what an integral is.


Nope. Integral is the sum of infinitesimal values, not zeroes. If you dig into definition of Riemann integral, then you'll see that the are no zeroes in the sum at any step. Except for zeroes of function being integrated, of course.

For example Riemann integral is just limit of sums built on tagged partitions of a closed interval with largest sub-interval approaches zero. But no single sub-interval is equal to zero, their lengths just approach zero while being strictly positive values.

Calculus does not divide by zero, calculus explores what happens when we move to zero as close as we can and even closer.


"That's not correct mathematically though."

Yes, certainly it is.

"You can't divide something 0 times and get any number. Adding zero + zero + zero infinite times does not equal 1."

The discussion is about mathematics, not elementary school arithmetic.


Feel free to give me a proof that adds zeros and gets a number greater than 0.


It's plenty correct mathematically; the linked article even explains this.

Generally, your viewpoint comes from an incorrect understanding of mathematics, so let's explore what math actually is:

When we say "10 / 2 = 5", the result "5" doesn't come out of nowhere. It comes out of the definition of division.

There are several ways you could define division. You could interpret "10 / 2 = 5" as meaning any of:

• "If you had a 10-inch stick and you divided it into 2 equal parts, each part would be 5 inches."

• "If you had the equation 2 * X = 10, X = 5 would be a solution to it."

• "If you had 10 Pa of pressure in an enclosed box, and you increased the volume of the box to 2 times its previous volume, its new pressure would be 5 Pa."

• "A wave of wavelength 2cm would have a frequency of 5 per 10cm."

These definitions all lead to the same result – in other words, they're equivalent. When this happens, mathematicians usually don't particularly care which one is the "real" definition.

This applies to many different mathematical axioms. For instance compare the definition of the Parallel Postulate on Wikipedia:

"If a line segment intersects two lines, the two lines will intersect on the side that their angles with the line segment sum up to less than 180°"

https://en.wikipedia.org/wiki/Parallel_postulate

with the definition on Wolfram MathWorld:

"If you have a straight line and a point not on it, there exists only one straight line which passes through that point and never intersects the first line."

http://mathworld.wolfram.com/ParallelPostulate.html

These are different definitions. But since they lead to the same result, mathematicians don't really care to argue over which is the "real" definition.

Some definitions only apply over a subset of numbers. For instance, originally, "5 - 10" was undefined. Then negative numbers were invented, to expand the definition. The same thing happened with square roots and complex numbers.

That's the same thing with division. 1 / 0 was originally undefined. But you can come up with new number systems that define it, while preserving all the other properties that make division what it is. A common definition is 1 / 0 = Infinity, which is done in the Riemann sphere number system:

https://en.wikipedia.org/wiki/Riemann_sphere

There's nothing mathematically incorrect about doing any of this.


thanks for such a ginuwine response


Some people say "oh, that's easy, 1/0 is +Infinity". So the real fun is at 0/0.

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.


I always thought division by zero was, at best, + and - infinity, depending on the path, which is why we leave it undefined.

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.


you are explaining why 0 / 0 can approach 17, this is much less controversial, and is often considered the bottom element...


17 is a bottom element?


That's not what he said.


In calculus you're supposed to learn that y/0 with y != 0 is "undefined" (±Inf with limit expressions), while 0/0 is "indeterminate" (can construct any value using a limit expression that contains x/x with x -> 0).


Take the limit as x goes to 0 of (17*x)/(x). Both numerator and denominator go to 0, so this is a representation of 0/0. The limit is then equal to 17.


This just means that 0/0 = 1. It doesn’t mean that any number / 0 tends to 17.


But think about what a limit is. Think of X and Y as their own functions. so something more like X(g) / Y(g). As g increases, X and Y can take any path across the plane that they want. We get to arbitrarily choose what X and Y are. All we are doing is describe how our limit behaves as we slide along the scale towards infinity. Just because g increased by went from 10 to 11 doesn't mean that X and Y behaved the same way. We can arbitrarily choose X and Y such the limit as g approaches infinity is 0 for both, but the way that X and Y are changing is different. Like the example above, if we choose X and Y such that X(g)/Y(g) is 17 for all g, but the X and Y both approach 0 as g approaches infinuty, then as g approaches infinity X and Y go to 0, but X / Y (0 / 0), goes to 17. It's not a true 0/0, because the functions we chose got to 0 at different rates.


Right, it just means the limit of that path tends to 17. It's not a well-defined limit.

In a function of one-variable, there's a distinction between one-sided and two-sided limits. I don't know what the terminology would be for multivariable functions, but this is closer in nature to a one-sided limit.


24 / 2

17 / 1

1.7 / 0.1

0.17 / 0.01

0.017 / 0.001

...

0 / 0


Take the path (x,y)=(17a,a) as a goes to zero one dimensionally.


Limits aren't the only thing broken by division by zero, though. For integers it's mainly algebraic stuff.


I like your point here. I think 1/0 = Infinity+ is a satisfying expression. Its a clear concept which can be visualized in a simple graph.

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.


>I like your point here. I think 1/0 = Infinity+ is a satisfying expression. Its a clear concept which can be visualized in a simple graph.

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).


> IEEE 754 has a single signless infinity

No it doesn't. I don't know where you got that idea, but it can't be from ever writing any floating point code, or looking at the IEEE-754 standard, or the floating point format. Please see https://www.h-schmidt.net/FloatConverter/IEEE754.html and stop spreading radically wrong misinformation.

> It still depends on which side you take the path from - from the positive denominator or the negative denominator side.

In IEEE-754, 1/0 == Infinity and 1/-0 == -Infinity.


Don't know why you're getting downvoted, but thanks for the clarification. Looks like I misremembered. Apparently it also has +0 and -0...


Opps, I forgot the negative side. Thanks.


> I think 1/0 = Infinity+ is a satisfying expression. Its a clear concept which can be visualized in a simple graph.

Not for integers.


Since the "1/0 = 0" is only defined for integer division then I'd argue that the chosen constant should be MAX_INT. (or MIN_INT in case of signe values and negative numerator)


That's a wonderful graphing website from the look of it!


I would argue 1/0 is infinity. Not with mathematics, just logically. By using the wording "how many times does 0 go into 1?"

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.


Your argument is flawed because your zero is positively biased.

Why infinity and why not negative infinity.


I encourage you to look at the graphs. Yes, the value along the y-axis is 0, and the value along the x-axis approaches positive and negative infinity. It's path-dependent. Take a look.


In float yes. In int there's no Inf. There's only MAX_INT or 0 or throw an exception.

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.


> But is Pony doing something unsound? Absolutely not. It is totally fine to define 1/0 = 0. Nothing breaks

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 = 0


I think OP means that nothing breaks mathematically. It is not inconsistent and not false, so you can work with it. The only issue is to deal specially with the case of division by zero, which you have to do anyways. Code that assumes that (x/y) * y = x is wrong if you don't check for y = 0, independently of what you define x/0 to be.


You do realize that division is the inverse operation of multiplication, right? Like subtraction is the inverse of addition. By defining addition we define subtraction. By defining multiplication we define division. This is where the author fails. Division is multiplication of a fractional value. This is VERY important.

And just because it is mathematically a field does not mean it is particularly the right choice. A field is not strictly definitely by addition and multiplication, it is definitely by two operators where one is an abelian group (addition in our case) and the other forms and abelian group over the non identity term of the first (eg multiplication is an abelian group over non zero terms). The complex numbers create a field, which is really how we do addition and multiplication in 2D space. 3D space you can't form one, so you have a ring. Which is why quaternions are so important, because they form a field.

But the author is wrong because they think division is a different operation than multiplication. It's just a short hand.


In a field, division by zero is not the inverse operation of anything.


The usual definition of division is the multiplicative inverse, yes. But that does not mean that, as an intellectual curiosity (which is how I take this post) you can't define "division" as having other value. Sometimes that's useful, sometimes it's not. Most of the time it is fun to see how things break and don't break. For example, the infamous 1+2+3+... = -1/12 sum[1]: for most purposes it's a divergent sum but you can find ways to assign a finite value and make that useful.

1: https://en.wikipedia.org/wiki/1_%2B_2_%2B_3_%2B_4_%2B_%E2%8B...


The OP says

> The field definition does not include division, nor do our definitions of addition or multiplication.

This is clearly a misunderstanding of not realizing that the definition of division is a symbolic shorthand for inverse multiplication.

Sure, you're right that we can define it another way. But I don't know what we would call an object with three operators. (Someone more knowledgeable in field theory please let me know, it's hard to Google) in the normal field we work with (standard addition and multiplication) we only have two unique operators. Everything else is a shorthand. In fact, even multiplication is a shorthand.

>For example, the infamous 1+2+3... = -1/12

Ramanujan Summation isn't really standard addition though. It is a trick. Because if you just did the addition like normal you would never end up at -1/12, you end up at infinite (aleph 0, a countable infinity). But the Ramanujan Summation is still useful. It just isn't "breaking math" in the way I think you think it is.

But I encourage you to try to break math. It's a lot of fun. But there's a lot to learn and unfortunately it's confusing. But that's the challenge :)


The fact that it's a shorthand means that it's not in the definition, just a convention. In fact, nowhere in my studies I saw anyone using 'division' when working explicitly with fields in algebra, it's always multiplicative inverse.

By the way, you cannot do the addition like normal on that series, you don't end up at anything. You can say it diverges and its limit is +infinity (not aleph0, aleph0 is used for cardinality of sets so I think no one would use it as the result of a divergent series). When I said it 'broke math' what I meant was that, in the same way that OP did, it is a way to assign values to things that usually don't have one. I know it does not actually break math.


That's not necessarily the definition of division, there are many commonly used definitions in different contracts that are different.


Bravo, that's a great explanation. I think you've done a better job here than my comments elsewhere in this thread. Nice tie in to dimensionality, the complexes and quaternions too.

There are legitimate reasons to advocate for defining division by 0 in the context of a programming language. But the attempt at mathematical rigor really distracts from those reasons. It feels like the author made a decision and reached for some odd mathematics that seems to support it but doesn't. Floating numbers don't even form a field. If we define division by 0 in a field it stops being a field and becomes a ring or some sort of weird finite field with a single element...


> Division is multiplication of a fractional value

Thus, division by 0 is multiplying by (1/0). Does such a fraction exist? It can go along two potentially different paths depending on the limit we take.

Alternatively, there is information lost when you multiply something by 0: a x 0 = 0, b x 0 = 0. When you perform an inverse by dividing, will you get back a or b (or any number)? Thus, it is not invertible at least at 0.

Disclaimer: Not a mathematician


>Alternatively, there is information lost when you multiply something by 0: a x 0 = 0, b x 0 = 0. When you perform an inverse by dividing, will you get back a or b (or any number)? Thus, it is not invertible at least at 0.

>>A field is not strictly definitely by addition and multiplication, it is definitely by two operators where one is an abelian group (addition in our case) and the other forms and abelian group over the non identity term of the first (eg multiplication is an abelian group over non zero terms).


It is inconsistent. If 1/0 = 0, then 1 = 0*0.


This should not be getting downvoted. The author of the article made an incorrect refutation of this point under the "Objections" heading. Division by 0 in fields is undefined precisely because it leads to contradictions like this. The full statement of a proof that 1/0 = 0 includes the temporary assumption that you have defined division by 0 such that it is no longer undefined.

You can't logically refute that proof by saying, "well no, you have yet to define division by 0 according to the field axioms, so you can't use that division as part of your proof." That's the point! The proof does not demonstrate that division by 0 results in 1, it demonstrates that you cannot define division by 0 while maintaining the algebraic structure of a field.

If the author wants to talk about defining division by 0 in wheels or something esoteric they're more than welcome to. But among fields, and among the real numbers, it's not possible. This whole exercise of trying to refute what has been commonly accepted for over a century is frankly ridiculous for trying to justify undefined behavior in a programming language.


But the point is you keep the structure of a field for everything that already has it, right? No structure is taken away. All statements valid on any subset of the reals are still valid without modification under this new definition. The algebraic structure is still a field obeying the same axioms. There are just some new valid statements too.


No that's not correct, and this is why I think the author's entire point is pretty inane. If you're going to start off your argument with the full formalism of field axioms and consequent theorems, you need to be prepared to split hairs about whether or not your definitions constitute a field.

Mathematics is thoroughly pedantic about definitions for a reason. If those formalisms don't matter because what you've done is "close enough", then skip the song and dance about field definitions and stop trying to use it to justify the behavior of an undefined operation in a programming language. Just say you're defining 1/0 to be equal to whatever you want because the world doesn't break down. It actually detracts the author's point to so confidently (and incorrectly) refute something that is robustly proved in the first few weeks of an undergraduate analysis course. Why is this even in a blog post about a programming language?!

This is essentially the same point as the extended real (or complex) number systems. The sets of all real and complex numbers (respectively) form fields under the axioms of addition and multiplication. But you can define division by 0 and division by infinity in a way that works with familiar arithmetic (I explained how to do this in another comment barely two weeks ago [1]). But the key point here is that in doing this you sacrifice the uniqueness of real numbers.

The author tries to refute this observation by claiming the proof uses an undefined division operation, but that's a red herring. The real assertion is that you cannot define division as an inverse operation from multiplication to be inclusive of division by the unique unit (i.e. 0, in the real field) unless you are willing to state that every number is equal to every other number in the entire field. And you can do that, but it trivially follows that you no longer have a field without a nonzero element.

So really the actual proof is that division by 0 is undefined for any field with at least one nonzero element.

__________________________________

1. https://news.ycombinator.com/item?id=17599087#17601806


Is there a proof or something elsewhere you can link to? To be honest I can't really tell the point you're trying to make.


I can give you a simple proof by contradiction.

1. Let F be a field containing an element x =/= 0.

2. Suppose we have defined division by zero in F such that, for all x in F, there exists an element y = x/0 (i.e. F adheres to the field axiom of multiplicative closure). Note that at this point it does not matter how we have defined division by 0, we will just generously continue and assume you've done it in a way that maintains the other field axioms.

3. Since y = x/0, it follows that the product of y and 0 is equal to x, because division is the inverse of multiplication. By the field axioms, division does not exist if there is no multiplicative inverse with which to multiply.

4. But by the field axioms this implies that x = 0, which contradicts our initial assumption. Likewise, since we can repeat this procedure with any element x in F, this demonstrates that there exists no nonzero element x in F, and in fact F = {0}.

The failure in the article's refutation is that this proof is designed to permit you to assume you have suitably defined division by zero, then proceed to demonstrate without any loss of generality that you could not possibly have unless 1) F is not a field, or 2) F contains only 0. The fundamental algebraic property you sacrifice by defining division by zero is uniqueness, and uniqueness is a hard requirement in fields with nonzero elements.


There is a mistake in your step 3, as it relies on an informal and imprecise "division is the inverse of multiplication". If you were to write that formally, you'd get `∀ x ≠ 0 . x(1/x) = 1`. This holds unchanged even if you define division by zero.

Even if you could come up with another formalization that does cause a problem, e.g. `∀ x ∈ dom(1/t) . x(1/x) = 1` (and I would say that this is the only formalization that causes an issue, and it requires the use of a language with a dom operator, something that is absolutely not required for theories of fields), it won't matter because the question is not whether one could come up with a formalization that leads to contradiction, but whether there are reasonable formalizations of fields where this does not happen, and there are (in fact, most of them satisfy this, as they do not rely on a dom operator).

In addition, it is not true that "by the field axioms, division does not exist if there is no multiplicative inverse with which to multiply." It's just that the field axioms do not define what the meaning of division is in that case. Defining it, however, does not lead to contradiction with the axioms, at least not a contradiction you've point out. In fact, most common languages of mathematics cannot even explicitly express the statement "x is not in the domain of f." All they can do is define f(x) for values of x in the domain, and not define f(x) for values outside it. The "exist" in your statement does not refer to ordinary mathematical existence (usually formally expressed with the existential quantifier) but to an informal notion of definedness (discussed by Feferman) that has no formal counterpart in most formal systems, because it is very rarely needed; it is certainly not needed to state the field axioms.


This is nonsense. If F is a field with elements x, y, then the quotient x/y is equivalent to the product x(1/y). If 0 has no multiplicative inverse, there is no division by 0. The two concepts are one and the same, just as is the case for subtraction and additive inverses. The problem is not that field theory doesn't tell you "what happens" when you divide by 0 - you haven't defined that, there is no what happens because nothing happens at all.

You can't engage with the problem because it only exists as a syntactical annoyance. You seem to acknowledge this, but then continue to argue when I explicitly tell you I am in agreement on that point. Then you proceed to argue the theoretical basis all over again.

I'm not going to continue arguing this with you. You're presently the only one in this thread who isn't following and I've tried to direct you to other resources. You've alternated between saying those proofs are either incorrect outright or not applicable because they don't have relevance for programming. If you actually believe division by 0 is possible in fields you have an immediately publishable math paper waiting for you to submit it.

Otherwise we're just talking past each other because my whole point here has been that the author's discussion of fields is irrelevant for programming language theory in the first place.


> If 0 has no multiplicative inverse, there is no division by 0. The two concepts are one and the same.

But formalizing the statement "there is no division" poses challenges. If you agree that a formalization of fields that is acceptable to you exists, please write a formal axiom/theorem in that theory which would break if we were to extend the definition of division.

> just as is the case for subtraction and additive inverses.

No, because subtraction is not partial, and therefore poses no challenge for formalization.

> there is no what happens because nothing happens at all.

This is fine when working informally, but doesn't work for formalizations, and formalizations of fields do exist.

> You can't engage with the problem because it only exists as a syntactical annoyance.

But that is the heart of the problem. Either you say one cannot formalize fields at all, or you must admit that some acceptable formalizations do not break. No one is claiming that one should be able to divide by zero in informal mathematics.

> I'm not going to continue arguing this with you.

That's fine, but settling this argument is very easy: write a theorem of fields in a formal language that would break if we extend division, one that cannot be equivalently written in an acceptable formalization in a form that does not break. This can literally be done in one line. If you cannot write such a theorem, then there really is no point arguing, because you cannot provide a counter-argument. Repeating the same informal theorems over and over is indeed pointless.

> You've alternated between saying those proofs are either incorrect outright or not applicable because they don't have relevance for programming.

I've not alternated on anything. Your proofs are incorrect in formal systems that you yourself would find acceptable.

> and I've tried to direct you to other resources

Resources about informal mathematics, on which everyone agrees. That the informal concept of division cannot be extended to 0 is obvious. The only question is whether a formal definition of division would necessarily break formal field theorems if extended to division by zero. You seem to claim that's the case, yet have not stated a single formal theorem of the kind.

> If you actually believe division by 0 is possible in fields you have an immediately publishable math paper waiting for you to submit it.

I would if only that result (doesn't merit a paper) had not already been published by at least Paulson and Avigad, two of the world's best known logicians. The formal field theory in the Lean proof assistant explicitly allows division by zero. That division coincides with the informal division everywhere but at zero, and the extension introduces no inconsistencies to the theory.

> the author's discussion of fields is irrelevant for programming language theory in the first place.

It's not about programming, but about any formalization (which includes, but is not limited to programming).

Anyway, see my comment https://news.ycombinator.com/item?id=17738558, which distills the debate into a concise, precise form.


>Since y = x/0, it follows that the product of y and 0 is equal to x, because division is the inverse of multiplication.

Can you explain how this follows? I thought division was only the inverse of multiplication for all nonzero denominators, which would mean we can't use that definition for deduction in x/0.

It might hinge on your next sentence:

>By the field axioms, division does not exist if there is no multiplicative inverse with which to multiply.

but I don't understand why that's necessarily true. I don't understand how the field axioms require division by x to require the existence of a multiplicative inverse of x when x is zero. Sorry to take a bunch of your Friday, but I'm very curious now. Explanation much appreciated.

-------

edit:

Come to think of it, couldn't I define x/y as cotton candy for all x,y in field F and still satisfy the field axioms? They just don't refer to division.

Any connection between x/y and y's multiplicative inverse is just a nice convention. That convention states that x/y = x * mult_inv(y) when y != 0, but nothing else. That definition has nothing to do with the field axioms and changing it doesn't require that I change anything about multiplicative inverses. That means I don't touch the field axioms and my field is still a field.


Your edit is starting to get it. If by the argument of the article x/y = cotton candy for all x,y, then probably the argument of the article isn't good. And the reason is precisely that division in a field is taken to be nothing but a notational shorthand for multiplication by the multiplicative inverse.


> Come to think of it, couldn't I define x/y as cotton candy

Yes. That's the thesis of the article. Make an arbitrary choice for 1 / 0 = ?, and if it helps you, use it. It's mathematically, rigorously fine.


This is explicitly talked about in OP about 2/3 of the way down the page. "If 1/0 = 0, then 1 = 0 * 0" is untrue. Your argument is "1/0 = 0, so multiply both sides by zero: 1/0 * 0 = 0 * 0, and then take 1/0 * 0 = 1 * 0/0." That last step isn't the case for reasons covered in the article we're ostensibly discussing.


Right. So the multiplicative inverse property _breaks_! He just points out that it breaks, and thus you need to use a more complicated property instead. That doesn't mean that the property doesn't break.


He's not saying that it breaks; quite the opposite, he repeatedly states that 0⁻ doesn't exist. He's just defining division in a specific way.

The MI property states that every element except 0 has a multiplicative inverse. He's defining division via two cases: If b≠0, then a/b = a*b⁻ (multiplicative inverse). If b=0, then a/b=0. This definition does not imply that 0⁻ exists, so there's no violation of MI.


The problem this and the other replies miss is that the standard definition of division is multiplication by the inverse. The entire argument rests on a notational slight of hand. The property that held before -- that _when defined_ division has the inverse property -- no longer holds. Thus many equational identities that otherwise would hold do not hold.


> The problem this and the other replies miss is that the standard definition of division is multiplication by the inverse.

Try to state this definition formally. The statement: ∀ x,y . x/y = xy⁻¹ is not a theorem of fields or a definition of division. However, ∀ x, y . y ≠ 0 ⇒ x/y = xy⁻¹ is, but is completely unaffected by defining division at 0. Those who think they see a problem rely on informal and imprecise definitions. Could you formally state a theorem that is affected? That would help you get around issues that are merely artifacts of imprecision.

But let's entertain you, and state that what we really mean by the informal and vague statement, "division is the inverse of multiplication," could be stated formally as:

    ∀ x ∈ dom(1/t). x(1/x) = 1
You are absolutely correct that this equational theorem is broken by extending the domain of division. However, there is absolutely no way to say that the formalization of this theorem isn't actually

    ∀ x ≠ 0 . x(1/x) = 1
because the two are equivalent. You cannot then claim that something is necessarily broken if you choose to pick a formalization that is indeed broken, while an equivalent formalization exists, that is not broken (not to mention that the formalization that is broken requires a strictly richer language). All that means is that your formalization in this case is brittle, not that laws are broken.


I have to disagree -- this isn't sleight of hand. The standard definition isn't being violated here, because standard division isn't a total function. The denominator's domain in Hillel's function is a proper superset of the standard domain: when restricted to the standard domain, the two functions are precisely equivalent. Therefore, every standard identity still holds under Hillel.

The hole that he is filling here isn't one that he bored into the standard definition, but a hole that the standard definition already admitted. If something is explicitly undefined, there's nothing mathematically wrong with defining it, as long as the definition doesn't lead to inconsistency.


> If something is explicitly undefined, there's nothing mathematically wrong with defining it, as long as the definition doesn't lead to inconsistency.

The definition does lead to inconsistency...you can't look at the field axioms, observe that 0 has no multiplicative inverse, then proceed to define a special, one-off division rule that doesn't involve multiplicative inverses for that one element. Either your division rule is pathological and breaks a fundamental field property or you've introduced a division rule which is just a syntactical sugar, not a real operation (in the latter case you've introduced confusing notation, not a new division function). Why do you think mathematicians explicitly state that the real field with the augmentation of positive and negative infinity (which allow division by 0) is not a field?

I don't understand why there is so much resistance to this idea in this thread, but the simple fact remains that if you define division by an additive identity (0) in any way, the field containing that unit ceases to be a field. This is because all elements cease to be unique. You can quickly prove that every element is equal to every other element, including (critically) the additive and multiplicative identity elements. Fields are defined by closure under the operations of addition and multiplication, and that closure requires uniqueness of their respective identities. Upend that and your entire field structure breaks down, because all you're left with is a field with a single element 0.

Stating that you've defined division by 0 using a one-off case that permits all other field identities to remain consistent is like saying you've turned the complex field into an ordered field using lexicographic ordering. You haven't, because i admits no ordering, much like 0 admits no multiplicative inverse.

Onlookers reading these comments probably think those of us harping on this point are anal pedants with a mathematical stick up our ass. But this thread is increasingly illustrating my central point, which is that the author shouldn't have tried to justify numerical operation definitions in a programming language using field axioms of all things.


> Onlookers reading these comments probably think those of us harping on this point are anal pedants with a mathematical stick up our ass. But this thread is increasingly illustrating my central point, which is that the author shouldn't have tried to justify numerical operation definitions in a programming language using field axioms of all things.

You can't use your own stubbornness to justify itself.

I'm waiting for you to justify your claim that this extension to division breaks any of the field axioms. pron even made you a nice list of them.

Just name one equation/theorem that the new axiom would break.

I'm completely open to being convinced! But so far you've only given arguments about giving a multiplicative inverse to zero. Everyone agrees on that. It's the wrong argument.


Because a divisor cannot exist unless it is a multiplicative inverse. Therefore 0 is not a divisor.

This is getting to be Kafkaesque...it breaks the field axioms themselves. How many different explanations and external resources do I need to provide in this thread for you to be convinced that this is not a controversial point in modern mathematics? I just explained it in the comment you responded to.

You have exactly two options here.

If you define x/0, that definition must interact with the rest of the definitions and elements of the field. To maintain multiplicative closure (a field axiom!) there must be a unique y element equal to x/0. So tell me how you will define x/0 such that x is not equal to the product of 0 and y. Regardless of what you think the author has shown, the burden of proof is not on me at this point to show that you can't do it, because it follows directly from the field axioms. Trying to impose a one-off bizarro divisor function defined only on {0} is not only mathematically inelegant, it immediately eliminates the uniqueness of all field elements. Therefore your "field" just becomes {0}, and since it lacks a multiplicative identity it ceases to be a field. There is your contradiction. Why don't you tell me how you're going to prove any equation defined over a field that relies on the uniqueness or cancellation properties of fields?

On the other hand, let's say you tell me you want define x/0 so that the definition doesn't interact with any of the field definitions or elements. Then you haven't actually introduced any new operation or definition, you've just designed a notation that looks a lot like division but has no mathematical bearing on the field itself (i.e. absolutely nothing changes, including for 0). That's not a divisor function, it's just a confusing shorthand. You can't just add another axiom to a field and call it a field.

If you believe I'm stubborn, that's fine. I might be a poor teacher! There are ample resources online which will patiently explain this concept in mind numbing detail. It boggles my mind that there are people in this thread still fighting an idea in earnest which has been settled for over a century.


> Because a divisor cannot exist unless it is a multiplicative inverse. Therefore 0 is not a divisor.

So there are two separate issues here. One is whether we can extend the definition of the "/" operator, and the other is whether we call it "division".

I'm not interested in what we call it. I'm interested in the claim that extending "/" will break the field.

The dichotomy you're talking about is wrong. The two options are not "multiplicative inverse" and "does not interact with anything". "1/0 = 0" interacts with plenty! If I make a system where it's an axiom, I can calculate things like "1/0 + 5" or "sqrt(1/0)" or "7/0 + x = 7". I can't use it to cancel out a 0, but I can do a lot with it.

> It boggles my mind that there are people in this thread still fighting an idea in earnest which has been settled for over a century.

Remember, the question is not "should this be an axiom in 'normal' math?", the question is "does this actually conflict with the axioms of a field?"

> You can't just add another axiom to a field and call it a field.

Yes you can. There is an entire hierarchy of algebraic structures. Adding non-conflicting axioms to an X does not make it stop being an X.


> you can't look at the field axioms, observe that 0 has no multiplicative inverse, then proceed to define a special, one-off division rule that doesn't involve multiplicative inverses for that one element

Why not? What mathematical axiom does this break?

> Either your division rule is pathological and breaks a fundamental field property

It doesn't, or you could show one here: https://news.ycombinator.com/item?id=17738558

> or you've introduced a division rule which is just a syntactical sugar, not a real operation

Is this math? We define symbols in formal math using axioms. I am not aware of a distinction between "syntactical sugar" and a "real operation."

> Why do you think mathematicians explicitly state that the real field with the augmentation of positive and negative infinity (which allow division by 0) is not a field?

That's simple: because unlike the addition of the axiom ∀x . x/0 = 0, adding positive and/or negative infinity does violate axioms 2, 7 and possibly 11 (depending on the precise axioms introducing the infinities).

> I don't understand why there is so much resistance to this idea in this thread, but the simple fact remains that if you define division by an additive identity (0) in any way, the field containing that unit ceases to be a field.

Because a field is defined by the axioms I've given here (https://news.ycombinator.com/item?id=17738558) and adding division by zero does not violate any of them. If you could show what's violated, as I have for the case of adding infinities and as done in actual math rather than handwaving about it, I assume there would be less resistance. I don't understand your resistance to showing which of the field axioms is violated.

> You can quickly prove that every element is equal to every other element, including (critically) the additive and multiplicative identity elements.

So it should be easy to prove using the theory I provided, which is the common formalization of fields. Don't handwave: write proofs, and to make sure that the proofs aren't based on some vagueness of definitions, write them (at least the results of each step) formally. The formalization is so simple and straightforward that this shouldn't be a problem.

> Stating that you've defined division by 0 using a one-off case that permits all other field identities to remain consistent is like saying you've turned the complex field into an ordered field using lexicographic ordering. You haven't, because i admits no ordering, much like 0 admits no multiplicative inverse.

Stop handwaving. Show which axioms are violated.


Consider the following two functions, that we can define for any field F:

• Our first function f(x, y) is defined for all x in F, and all nonzero y in F. That is, the domain of this function f is (F × F\{0}). The definition of this function f is:

f(x, y) = x times the multiplicative inverse of y

(Note that in the definition of f(x,y) we could say "if y≠0", but whether we say it or not the meaning is the same, because the domain of f already requires that y≠0.)

• Our second function g(x, y) is defined for all x in F, and all y in F. That is, the domain of this function g is (F × F). To define this function g, we pick a constant C in F, say C=0 or C=1 (or any C∈F, e.g. C=17 if 17 is an element of our field). And having picked C, the definition of this function g is:

• g(x, y) = x times the multiplicative inverse of y, if y ≠ 0, and C otherwise [that is, g(x, 0) = C].

Note that this function is defined for all (x, y) in F×F, and when y≠0 it agrees with f, i.e. g(x,y) = f(x,y) when y≠0.

Would you agree that both of these are functions, on different domains?

Next, we have the notation x/y. To assign meaning to this notation (and to the word “division”), there are two conventions we could adopt:

• Convention 1: When we say "x/y", we will mean f(x,y) (as defined above) — that is, x * y^{-1}.

• Convention 2: When we say "x/y", we will mean g(x,y) (as defined above).

The point of the post is that we can well adopt Convention 2: with such a definition of "division", all the properties that were true of f (on the domain F×F\{0}) continue to be true of g, except that g is defined on a wider domain.

----------------------------------------------

Now, maybe Convention 2 offends you. Maybe you think there is something very sacred about Convention 1. In all your posts in this thread, you seem to be insisting that "division" or "/" necessarily have to mean Convention 1, and the provided justification for preferring Convention 1 seems circular to me — here are some of your relevant comments, with my comments in [square brackets]:

> Since there is no multiplicative inverse of 0, division by 0 is undefined behavior [This seems to be saying: Because of Convention 1, we cannot adopt Convention 2.]

> Since y = x/0, it follows that the product of y and 0 is equal to x, because division is the inverse of multiplication [Here, your reasoning is "because we adopt Convention 1"]

> in algebraic fields division by x is equivalent to multiplication by 1/x. This is precisely why you cannot have a field that admits division by 0: because 0 has no multiplicative inverse. [Again, you're stating that Convention 1 has to hold, by fiat.]

> If F is a field with elements x, y, then the quotient x/y is equivalent to the product x(1/y). If 0 has no multiplicative inverse, there is no division by 0. The two concepts are one and the same [This is just stating repeatedly that we have to adopt Convention 1.]

> A multiplicative inverse is a division. [This is merely insisting that Convention 1 has to be adopted, not Convention 2]

> divisor cannot exist unless it is a multiplicative inverse [Again, stating Convention 1.]

> you can't look at the field axioms, observe that 0 has no multiplicative inverse, then proceed to define a special, one-off division rule that doesn't involve multiplicative inverses for that one element. [Why not?] ...you've introduced a division rule which is just a syntactical sugar [yes the same is true of Convention 1; what's the problem?]

All these comments, which emphatically insist on Convention 1, seem to ignore the point of the article, which is that Convention 2 has no more mathematical problems than Convention 1, because the function g is no less a “valid” function than the function f.

In mathematics when we use words like “obvious” or insist that something is true because it just has to be true, that's usually a hint that we may need to step back and consider whether what we're saying is really mathematically justified. What we have here is a case of multiple valid definitions that we can adopt, and there's no mathematical reason for not adopting one over the other. (There's a non-mathematical reason, namely “it breaks convention”, but the entire point is that we can break this convention.)


I follow everything you're saying. As I have said many times already, I have no problem with convention 2. But don't use field theory to justify convention 2, because it's mathematically incoherent and unnecessary. I broadly agree - there is no reason to be involving field theory in programming language design.

I don't take issue with division by 0 - you can do that in mathematics just fine! I take issue with defining that division and calling the consequent system a field when it's not a field, and acting as though everyone else is wrong. The author invited this criticism when they loaded in the full formalism of field theory without needing to.

If the author had just stated they wanted to define division by zero that wouldn't be a problem. I have no idea why they felt the need to pull in abstract mathematics. I'm not disagreeing with their point, I'm taking issue with the strange and incorrect way they defended it.

Note that in my top level comment I specifically said, "Mathematics does not give us truths, it gives us consequences." I will happily agree with you that there is usefulness and coherence in a definition of 0. There is no canonical truth about the world regarding that matter. But a direct consequence of defining any division by 0 is that you cease to have an algebraic field.

Therefore, using field theory to defend a system which defines division by 0 doesn't make sense. It's not that the system is "wrong" for some meaning of wrongness. It's that you shouldn't be trying to pigeonhole field theory to make it work, because you don't need to.


Glad you follow! But I'm not sure we agree yet, as there's still something puzzling when you say:

> But don't use field theory to justify convention 2, because it's mathematically incoherent

> defining that division and calling the consequent system a field when it's not a field

> a direct consequence of defining any division by 0 is that you cease to have an algebraic field

If you go back to my comment (the one you're replying to), both the functions f and g assume a field F, and they are well-defined functions on F×F\{0} and on F×F respectively. (Do you agree?) For example, F may be the field of real numbers. Forget about the word “division” for a moment: do you think there is something about the function g, that makes F not a field?

To put it differently: I agree with you that it is a direct consequence of defining a multiplicative inverse of 0 that you cease to have an algebraic field. But the only way this statement carries over when we use the word “division”, is if we already adopt Convention 1 (that “division” means the same as “multiplicative inverse”).

Again, I think you are implicitly adopting Convention 1: you're saying something like “if we adopt Convention 2, then x/y means the function g and includes the case when y=0, but [something about multiplicative inverses, implicitly invoking Convention 1], therefore there's a problem". But there's no problem!

It is not a direct consequence of defining the function g(x,y) that something ceases to be a field: it is a consequence only if you also insist on Convention 1, namely if you try to assign a multiplicative inverse to 0 (which everyone here agrees is impossible).

Let me emphasize: whether we adopt Convention 1 or Convention 2, there is no problem; we still have the same field F.


Look at it this way...

Standard definition of division function, d:

d(x, y) = x * y⁻, for all x and y EXCEPT 0

Author's modified, piecewise (https://en.wikipedia.org/wiki/Piecewise) definition:

d(x, y) = x * y⁻, for all x and y EXCEPT 0

d(x, y) = 0, for y = 0

He's just adding 0 to the domain of d(x, y) to extend the definition, and deliberately not using xy⁻ for that particular element of the domain. No inverse needed.


I know what he's doing. The problem is when you make it a different function (even by just extending it) then you change its equational properties. So equational properties that held over the whole domain of the function no longer hold over the extended domain. This is repaired by modifying the equational properties. But the modified equational properties mean that you now have a different system than before. So the whole thing is just playing around with words.


> So the whole thing is just playing around with words.

Er... that's what mathematics is. It's a word game - we build systems from arbitrary rules and then explore the results.

Look through https://www.mathgoodies.com/articles/numbers for a bunch of uncommonly-defined numbers.


You missed the part where he defines division as a/c = a * inverse(c) for all c != 0


Multiplicative inverse is already broken. x / 0 * 0 is either 0 or ⊥ (aka exception). It wasn't true to begin with. I happen to like the exception/partial function version, so I'm playing devil's advocate, but it really was already broken when we're talking about zero.


Wait, if 1 / 0 = infinity, then infinity * 0 = 1

This seems just as bizarre, since zero times anything shouldn't become 1, no matter how big or how many times you do it.


According to the standard definition of division, 1/0 does not equal infinity. It doesn't equal anything. It is undefined.


Yes, that's correct. The fact that 1/0 shouldn't be defined as infinity isn't an argument that it should be defined as 0.


There are many contexts where defining either 1/0 = -1/0 = ∞ or 1/0 = +∞ and –1/0 = –∞ is better than the alternatives, especially when working in an approximate number system like floating point.

In geometric modeling kinds of applications, I would say that these definitions are typically desirable, with 1/0 = undefined only better in unusual cases. As a simple example, it is typically much more useful for the “tangent” of a right angle to be defined as ∞ than left undefined.

But anyhow, there are no “facts” involved here. Only different choices of mathematical models, which can be more or less convenient depending on context / application.


Yes, exactly, I have no idea why you're being downvoted.


Look up the dirac delta function. It's a spike thats infinitely tall and infinitely narrow, with an area of 1. It's established now as a very useful tool in EE. But many people fought it tooth and nail because of this logic.


> This seems just as bizarre, since zero times anything shouldn't become 1, no matter how big or how many times you do it.

It isn't bizarre, because there's an equal and opposite argument that anything times infinity is infinity, no matter how small the thing you multiply by.

If you actually do infinity * 0 you get NaN since there's no way to determine (without more information) whether the result should be 0, infinity, or anything in between.


1/0 = undef (cast to 0)

1 != 0*0

I don't see the inconsistency. Abstract math vs practical application.


You’re doing something different than real number arithmetic. Saying 1/0 = x, and then treating x like a real number is inconsistent. But just saying “we are going to augment the real numbers with an element x that is not a real number, and then define some properties of x and prove things about it” is not.


> You’re doing something different than real number arithmetic

Computer languages execute on rules that are not utilizing real number arithmetic. I didn't want to mention it, but there's these things called floats...

Edit: Pony took out the "normal" version of division by zero and suggest to write a wrapper to check beforehand.


Where did I say anything about computers? I’m referring to real numbers.


The topic is a computer language. I'm referring to utility.


Floating point is only useful to the extent that it approximates real arithmetic.


In pony, floating point behaves 1.0/0.0 the way you might expect.


It's not inconsistent. See this article [1] for an explanation. It's the first thing covered in the "Objections" section.

[1] https://www.hillelwayne.com/post/divide-by-zero/


So according to you, 0*0 = 0, thus 0/0 = 0?


If division by zero is not undefined, yes. But how does this disagree with the assertion that it's mathematically inconsistent (as opposed to programmatically inconsistent)? If any case exists that leads to a logically false conclusion, then it's not logically consistent.


Nope. If 0/0 = 0, then x * 0/0 = x * 0, so 1/0 = 1, implying 1 = 0.


You didn't read the article, did you?


> the many pieces of code that assume that (x / y) * y equals x

The 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.


I would say an exception is much more convenient. Buggy code that silently continues to run is very hard to debug!


I used to think the same way: let's throw an exception on divide by zero, and forget NaN like a bad dream! But then someone explained to me that it's common to feed a billion numbers into a long calculation, then look at the results in the morning and find them okay, apart from a few NaNs. If each NaN led to an exception, you'd come back in the morning and find that your program has stopped halfway through. So there's a certain pragmatism to having numeric code silently continue by default.


Typically, divide by zero throws for integers because they can’t express NaN, which can instead be returned for floating point. In any case, integers can’t express special values, so you get exceptions instead. And this is actually defined at the processor level (for x86 among others), the trap is free (well, a sunk cost), why not take it?

Zero is not a very good NaN.


Yeah, agreed. The thing with 1/0=0 is bizarre, I guess my comment was more about why NaNs are okay.


What of a language without exceptions? A correct program can not assume that (x / y) * y = x because such an equality does not hold in general. Therefore the program must do something right in the case that y is zero before it tries dividing x by y to be correct. It shouldn’t really matter what x / 0 is because if a program relies on its value then it is probably wrong.


It's not convenient at all in a language like Coq.


Can you elaborate?


It's common to think of an exception as the program stopping and never being started again. In that sense, the invariant is still maintained, because the program never sees it violated. This has non-trivial consequences - for example, Rust lets exceptions and endless loops produce any value at all, even though they obviously can't actually do that.


Assuming an exception is equivalent to any non-exceptional value doesn't break anything. See "Fast and Loose Reasoning is Morally Correct".


Holy moly that is not at all what that paper says! It specifically argues that certain equational properties of a given total language continue to hold in the total fragment of a given partial language. It is an embedding theorem combined with lifting properties, not a license to perform _any_ reasoning at all regarding the non-total portion of the language it considers!


> (x / y) * y equals x

Even without Pony's assumption, that's only true when y != 0.


Yeah, (x/y)*y = x is true whenever it's a meaningful statement. x/y has no meaning in standard mathematics when y=0. 1/0 isn't infinity or anything else in standard math, it's literally un-grammatical nonsense.


It should be true for every y that is allowed in the denominator.

That is how fractions are handled in higher math. (Either called "localization" or "ring of fractions").


It's true up to NaNs, which we can treat as bottoms.


It's talking about integer arithmetic, so it's already not true that (x / y) * y == x. For example, (5 / 2) * 2 == 4.


> …any pieces of code that assume that (x / y) * y equals x…

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.


Also just defining division to be whatever you want in the moment is not actually pragmatic, it's stupid and trivial. There's a reason we define a symplectic manifold the way we do. There's not a reason to say `1/0 = 0`, aside from the fact that the Pony designers didn't care to find a good solution to the problem.


What if number types were Optionals after any operation that could result in any kind of unusual number (sqrt(-1), Infinity, NaN)? Or maybe after every operation, since any operation could overflow the type.

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.


You know what would happen. Math is ubiquitous in lots of code, so syntactic shortcuts would be introduced. Thus, we wouldn't use `a >>= (\x -> 2x) >>= (\x -> x^2)`, but would soon introduce syntactic sugar. For example, we could re-define `[asterisk]` so that it had type `Maybe Number -> Maybe Number -> Maybe Number`. You'd need a `return` (or `Just`) at the beginning, but soon that would be elided too, so that numeric literals would be auto-promoted to `Maybe Number`s. Then the language would add silent mechanisms to allow `Maybe Number`s to be displayed easily. You should check if your `Maybe Number`s are `Just Number`s before letting them out to play, but, if the language doesn't force you, then you know you won't. And then we're right back in the current situation.


Zig defines checked versions of mathematical operators in its std lib [0], which return an Error Union. It's like an Optional (which Zig also has), but with an error instead of null. Your code can choose to handle this error however it wants.

[0] https://ziglang.org/documentation/master/#Standard-Library-M...


Isn't that essentially what floats already are? A sum type of numbers, infinities, and NaN(s).


Yes and no. I think the OP is looking for a strongly typed language where you can’t do any operation on that sum type, forcing you to check results after every operation.


JavaScript does that…


How so without optionals or static typing?


That's usually what NaN is used for (basically "None" for numbers).

More

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact

Search: