63

I'm in the middle of developing a new programming language to solve some business requirements, and this language is targeted at novice users. So there is no support for exception handling in the language, and I wouldn't expect them to use it even if I added it.

I've reached the point where I have to implement the divide operator, and I'm wondering how to best handle a divide by zero error?

I seem to have only three possible ways to handle this case.

  1. Ignore the error and produce 0 as the result. Logging a warning if possible.
  2. Add NaN as a possible value for numbers, but that raises questions about how to handle NaN values in other areas of the language.
  3. Terminate the execution of the program and report to the user a severe error occurred.

Option #1 seems the only reasonable solution. Option #3 is not practical as this language will be used to run logic as a nightly cron.

What are my alternatives to handling a divide by zero error, and what are the risks with going with option #1.

Reactgular
  • 13,040
  • 4
  • 48
  • 81
  • 13
    if you did add exception support and the user didn't catch it then you'd have option #3 – ratchet freak Aug 18 '13 at 11:54
  • 84
    I'm curious, what kind of stupid requirement would require you to create a whole new programming language? In my experience, every language ever created sucks (in design or in execution, often in both) and it took unreasonably much effort to even get *that* much. There are a few exceptions to the first, but not to the second, and as they're easily <0.01% of the cases, they're probably measurement errors ;-) –  Aug 18 '13 at 12:04
  • 16
    @delnan most new languages are created to allow business rules to be separated from how they are implemented. The user does not need to know how `reject "Foo"` was implemented, but simply that it rejects a document if it contains the keyword `Foo`. I try to make the language as easy to read using terms the user is familiar with. Giving a user their own programming language empowers them to add business rules without depending upon technical staff. – Reactgular Aug 18 '13 at 12:15
  • 3
    I don't know why you're building a language, but answering this sort of question probably requires more details about the language itself. Can divide return multiple values? What does your type-system look like? (e.g., you might be able to support something like `Valid Int | DivByZero`). Since this looks like a custom language to empower business users, you might just ask them what you should do (return INT_MAX, maybe?). – Aidan Cully Aug 18 '13 at 12:18
  • 1
    @MathewFoscarini Ah, for very restricted domain specific languages my criticism are less applicable (though language design and implementation is always hard to do well, no matter what the purpose). –  Aug 18 '13 at 12:29
  • 1
    People, I'm using an open source parsing engine and the language is defined in rules table that is compiled by the engine. I only have to implement the statements for the new language. It's really not that hard but I do have to implement how the statements work. – Reactgular Aug 18 '13 at 12:36
  • @AidanCully you mean you are suggesting a haskell like type system – ratchet freak Aug 18 '13 at 12:47
  • 1
    If you think this is a tough question, wait til you ask yourself what you're going to do when you run out of memory. Most languages have exception handling, for very good reason. – pdr Aug 18 '13 at 12:56
  • 9
    While not of the greatest concern if this is used in a learning environment exclusively, I could see some REALLY hard to detect bugs occurring with getting a 0 when I really shouldn't in scenario #1. I would expect something like INTMAX or INTMIN to be less anonymous if I was seeking out a bug. – Rig Aug 18 '13 at 14:02
  • 2
    Perhaps you could handle this by having separate numeric types for which division by zero is handled differently. For instance, you know something's up when you are dividing by zero when the zero is an index, but if the zero is the value from a physical measurement from a sensor, have it be +Infinity would be more correct. – AJMansfield Aug 18 '13 at 21:01
  • 1
    http://en.wikipedia.org/wiki/Null_(SQL) – rwong Aug 18 '13 at 22:23
  • 6
    I am with @delnan: Do you really want to be the first, last and only shop for support for this language? Python/Basic/JavaScript, Google, a few books and a light weight training session will beat a home grown, undocumented, buggy domain specfic language every time. If your users can't get there heads around 101, they should not be allowed to program. – mattnz Aug 19 '13 at 03:38
  • 1
    @mattnz. To many users, any "programming language 101" is merely [room 101](http://en.wikipedia.org/wiki/Room_101)... –  Aug 19 '13 at 06:02
  • 20
    @Mathew Foscarini. Never, ever, ignore the error and silently return 0. When doing a division, 0 may be a perfectly legal value (for some reason, there is such a thing in Power Basic, and it's really a pain). If you divie floating point numbers, Nan or Inf would be nice (have a look at [IEEE 754](http://www.validlab.com/754R/) to understand why). If you divide integers, you may stop the program, divide by 0 should never be allowed (well, unless you want to implement a true exception system). –  Aug 19 '13 at 06:07
  • 2
    @arbautjc: Agreed - my point is, those people are better off not being told they can program. If his language has to deal with div0 errors, it IS a programming language, adding more sugar won't make it taste better to those that cannot program. – mattnz Aug 19 '13 at 08:43
  • 2
    @delnan: For restricted domain specific language the criticism _appears_ less applicable, which is why there is so many of them. But it actually applies even stronger. – Jan Hudec Aug 19 '13 at 11:04
  • 4
    @MathewFoscarini: There is absolutely no advantage in creating your own language. User of library does not have to care about the implementations any more than a user of domain specific language without all the bugs and misfeatures. And the new language won't be any easier either; people who can't program can't do it not because they struggle with the language, they struggle with elementary concept of describing things strictly formally. Anybody who should use the language must have that skill anybody who can pick that language can pick Lua or Python too. – Jan Hudec Aug 19 '13 at 11:16
  • 2
    @JanHudec They certainly *still* apply, and I can see reasons to say they apply just as strongly, but I don't see why they would apply *even more* for DSLs. The much smaller scope makes good design slightly less impossible than for general-purpose programming languages, and for many DSLs the implementation is *much* simpler than a fully-blown "real" programming language compiler or VM. –  Aug 19 '13 at 11:30
  • 1
    @delnan I think a lot of the negative comments come from people assuming my language is comparable to populate languages. I would say mine is more like a scripting language. It has a fixed number of statements it understands, and those statements are more like business terms. A real programmer would find my language very limiting, but users will like those limits. It keeps the language within their scope of understanding. – Reactgular Aug 19 '13 at 11:47
  • 2
    @Mathew. More or less like the "language" provided by worksheet formulas I suppose? I see nothing negative in providing such features. –  Aug 19 '13 at 12:43
  • 5
    What you should really be doing, instead of trying to make your own 'beginners language', just make a robust, well-documented, easy-to-use, intuitive API for an existing language that handles the sorts of operations you are trying to simplify. If you can manage that, then perhaps you will be able to do the first one, too. – AJMansfield Aug 19 '13 at 21:42
  • 1
    Just return Infinity. People will scratch their heads and move on. – Erik Reppen Nov 15 '13 at 16:18
  • Using NaN and propagating it is the best solution - it will corrupt some of the data, but the rest of the data may be useful. It will also be possible to identify, and with option #1, you cannot do that easily. – Boyko Perfanov Nov 15 '13 at 16:30
  • 1
    Does your language support integers and floating-point numbers as distinct types? `NaN` is typically defined for floating-point types, not for integers. You *could* define your own integer `NaN`, but it would be a bit unusual. – Keith Thompson Nov 15 '13 at 19:35
  • 1
    @BoykoPerfanov the problem with NaN is that it propagates to all variables that touch it. Any math operation done on NaN results in NaN. So if you calculate a dozen variables using a variable that contains NaN then all those variables now contain NaN. It's also used in code to identify variables that have not been initialized. That works when 0 has meaning. – Reactgular Nov 15 '13 at 20:12
  • In Go 1, integer division by a constant zero produced a run-time Panic. In Go 1.1, an integer division by constant zero is not a legal program, so it is a compile-time error. (Source: http://golang.org/doc/go1.1#divzero) – JensG Nov 15 '13 at 22:28
  • Addendum: When the Go 1.1 Compiler is not able to check it at compile time, it will give +Inf or -Inf at runtime, depending on the values. – JensG Nov 15 '13 at 22:44
  • @MathewFoscarini unless the software re-prompts for presumably correct data, or prompts for a programmer to provide an instantaneous bugfix, which other method is better than NaN for marking corrupted data, and not invalidating correct data? – Boyko Perfanov Nov 16 '13 at 08:09
  • My language is targetted at novices... therefore I will make them manually check for error all the time in every function call instead of having the compiler/runtime do it? Interesting logic. – DeadMG Jul 12 '14 at 18:17
  • 17
    I amused and fascinated by a business domain complex enough to justify a proprietary, Turing-complete programming language but lax enough to tolerate drastically inaccurate results. – Mark E. Haase Jul 12 '14 at 20:06
  • Failure is not an option because cron jobs run at night? But the operating system kernel underneath those cron jobs will panic if runs into a bug, day or night. – Kaz Jul 13 '14 at 03:15
  • @MathewFoscarini Are you planning to support the concept of infinity? If you want to allow any serious mathematics, you should consider this. – Richie Cotton Jul 13 '14 at 06:19
  • Is it really that hard for the programmer to test the divisor for zero before dividing? If (s)he can't do that, the program deserves to hard crash. – user7195 Jul 13 '14 at 12:47
  • my main question would be "what is the number type you are dealing with". Operators do not exist in a vacuum, they only exist in relation to numbers to be operated upon. Floating point division is an entirely different operation than rational division, or integer division, or Base 10 Decimal Number division. If you want to be extremely technical, one can argue dividing a base 10 number by 3 is just as invalid as dividing an integer by zero. But in many business calculations, for example splitting up an amount of a resource using percentages, there is actually no need for a division operator. – don bright May 06 '19 at 23:42

16 Answers16

100

I would strongly advise against #1, because just ignoring errors is a dangerous anti-pattern. It can lead to hard-to-analyze bugs. Setting the result of a division by zero to 0 makes no sense whatsoever, and continuing program execution with a nonsensical value is going to cause trouble. Especially when the program is running unattended. When the program interpreter notices that there is an error in the program (and a division-by-zero is almost always a design error), aborting it and keeping everything as-is is usually preferred over filling your database with garbage.

Also, you will unlikely be successful with thoroughly following this pattern through. Sooner or later you will run into error situations which just can't be ignored (like running out of memory or a stack overflow) and you will have to implement a way to terminate the program anyway.

Option #2 (using NaN) would be a bit of work, but not as much as you might think. How to handle NaN in different calculations is well-documented in the IEEE 754 standard, so you can likely just do what the language your interpreter is written in does.

By the way: Creating a programming language usable by non-programmers is something we've been trying to do since 1964 (Dartmouth BASIC). So far, we've been unsuccessful. But good luck anyway.

samthebrand
  • 368
  • 2
  • 12
  • 27
Philipp
  • 23,166
  • 6
  • 61
  • 67
  • 14
    +1 thanks. You convinced me to throw an error, and now that I read your answer I don't understand why I was hesitating. `PHP` has been a bad influence on me. – Reactgular Aug 18 '13 at 14:17
  • 26
    Yes, it has. When I read your question, I immediately thought that it was a very PHP-esque thing to produce incorrect output and keep trucking along in the face of errors. There are good reasons why PHP is the exception in doing this. – Joel Aug 18 '13 at 16:14
  • 4
    +1 for the BASIC comment. I don't advise using `NaN` in a beginner's language, but in general, great answer. – Ross Patterson Aug 19 '13 at 11:15
  • 8
    @Joel If he'd lived long enough, Dijkstra would probably have said "The use of [PHP] cripples the mind; its teaching should, therefore, be regarded as a criminal offense." – Ross Patterson Aug 19 '13 at 11:16
  • 13
    @Ross. "arrogance in computer science is measured in nano-Dijkstras" -- Alan Kay –  Aug 19 '13 at 12:44
  • @arbautjc Love it! – Ross Patterson Aug 19 '13 at 13:22
  • @RossPatterson: Dijkstra actually did live long enough to see PHP. I don't know if he ever made any comments about the language. – dan04 Nov 16 '13 at 17:59
  • "So far, we've been unsuccessful." Yes, there has been plenty of hype - "Finally we have a language managers can use and no longer need programmers. Meet COBOL". However, saying we've been unsuccessful depends on your definition of a programming language and the nature of the users. Domain-specific languages have been successfully introduced for non-programmers and even tools like Yahoo! Pipes and IFTTT are a form of programming (unless we stick to a rigid circular definition...they certainly accomplish what might otherwise require a "program" to do). – mahemoff Jul 13 '14 at 05:01
  • 3
    @dan04 I wonder if Dijkstra saw PHP and decided that his life was a failure, and it was a good time to go rest in peace. – user949300 Jul 13 '14 at 05:37
  • 1
    @Philipp - One thing about IEEE 754- there's few enough professional programmers that really understand it (I certainly only know the most basic things about it), and if the language is intended for non-programmers, 754 is probably too much to ask of them. In fact, I'd go so far as to advise that any language targeting non-programmers provide a decimal representation for numbers instead of the base-2 representation that most of us use for floating point - it's got issues of course, but it leads to less surprises for the unwary. – Michael Kohne Jul 13 '14 at 23:15
  • 1
    Seems to me like the whole problem with the "a programming language usable by non-programmers" idea is that it's about as sensible as "an alphabet usable by illiterates" - using the tool axiomatically requires *developing the skillset*, but we keep trying to make languages that limit themselves to the easy parts and hide the inevitable complexities intrinsic to programming. A partial solution might be making and presenting languages in ways that let you start easily then provide a natural path and harness that engenders growth of the full skill-set. – mtraceur May 24 '18 at 22:43
33

1 - Ignore the error and produce 0 as the result. Logging a warning if possible.

That's not a good idea. At all. People will start depending on it and should you ever fix it, you will break a lot of code.

2 - Add NaN as a possible value for numbers, but that raises questions about how to handle NaN values in other areas of the language.

You should handle NaN the way runtimes of other languages do it: Any further calculation also yields NaN and every comparison (even NaN == NaN) yields false.

I think this is acceptable, but not necessarily new comer friendly.

3 - Terminate the execution of the program and report to the user a severe error occurred.

This is the best solution I think. With that information in hand users should be able to handle 0. You should provide a testing environment, especially if it's intended to run once a night.

There's also a fourth option. Make division a ternary operation. Any of these two will work:

  • div(numerator, denumerator, alternative_result)
  • div(numerator, denumerator, alternative_denumerator)
back2dos
  • 29,980
  • 3
  • 73
  • 114
  • But if you make `NaN == NaN` be `false`, then you will have to add a `isNaN()` function so that users are able to detect `NaN`s. – AJMansfield Aug 18 '13 at 20:54
  • 2
    @AJMansfield: Either that, or people implement it themselves: `isNan(x) => x != x`. Still, when you have `NaN` coming up in your programming code, you should not start adding `isNaN` checks, but rather track down the cause and make the necessary checks there. Therefore it is important for `NaN` to propagate fully. – back2dos Aug 18 '13 at 21:31
  • 5
    `NaN`s are majorly counter-intuitive. In a beginner's language, they're dead on arrival. – Ross Patterson Aug 19 '13 at 11:17
  • 2
    @RossPatterson But a beginner can easily say `1/0` -- you have to do something with it. There is no possibly useful result other than `Inf` or `NaN` -- something that will propagate the error further into the program. Otherwise the only solution is to stop with an error at this point. – Mark Hurd Aug 21 '13 at 01:23
  • But beginners can also argue about `sqrt(-1)`. Basically, it is acceptable to surprise a beginner if it serves the purpose of highlighting a critical misunderstanding (an unknown unknown). Beginners can argue or misunderstand about anything, including math. Lacking non-fatal errors (`NaN`), a program-aborting error may be acceptable. – rwong Jul 13 '14 at 06:27
  • 1
    Option 4 could be improved by allowing the invocation of a function, which in turn could perform whatever action is necessary to recover from the unexpected 0 divisor. – CyberFonic Jul 14 '14 at 06:41
22

Terminate the running application with extreme prejudice. (While providing adequate debug information)

Then educate your users to identify and handle conditions where the divisor might be zero (user entered values, etc.)

Dave Nay
  • 3,809
  • 2
  • 18
  • 25
13

In Haskell (and similar in Scala), instead of throwing exceptions (or returning null references) the wrapper types Maybe and Either can be used. With Maybe the user has a chance to test if the value he got is "empty", or he might provide a default value when "unwrapping". Either is similar, but can be used returns an object (e.g. an error string) describing the problem if there is one.

Landei
  • 1,993
  • 1
  • 13
  • 19
  • 1
    True, but note that Haskell doesn't use this for division by zero. Instead, every Haskell type implicitly has "bottom" as a possible value. This isn't like null pointers in the sense that it's the "value" of an expression that fails to terminate. You can't test for nontermination as a value, of course, but in operational semantics the cases that fail to terminate are part of the meaning of an expression. In Haskell, that "bottom" value also handles additional error-case results such as the `error "some message"` function being evaluated. –  Nov 15 '13 at 16:46
  • Personally, if the effect of aborting the whole program is considered valid, I don't know why pure code can't have the effect of throwing an exception, but that's just me - `Haskell` doesn't allow pure expressions to throw exceptions. –  Nov 15 '13 at 16:49
  • I think this is a good idea because other than throwing an exception, all the proposed options do not communicate to the users that they made a mistake. The basic idea is that the user makes a mistake with the value they gave to the program, so the program should tell the user that they gave wrong input (then the user can think of a way to remedy). Without telling the users about their mistake, any solution feels so odd. – InformedA Jul 13 '14 at 05:03
  • I think this is the way to go... The Rust programming language uses it extensively in its standard library. – aochagavia Jul 13 '14 at 15:13
12

Number 1 (insert undebuggable zero) is always bad. The choice between #2 (propagate NaN) and #3 (kill the process) depends on context and ideally should be a global setting, as it is in Numpy.

If you're doing one big, integrated calculation, propagating NaN is a bad idea because it will eventually spread and infect your whole calculation--- when you look at the results in the morning and see that they're all NaN, you'd have to throw out the results and start again anyway. It would have been better if the program terminated, you got a call in the middle of the night and fixed it--- in terms of the number of wasted hours, at least.

If you're doing many little, mostly-independent calculations (like map-reduce or embarrassingly parallel calculations), and you can tolerate some percentage of them being unusable due to NaNs, the that's probably the best option. Terminating the program and not doing the 99% that would be good and useful on account of the 1% that are malformed and divide by zero might be a mistake.

Another option, related to NaNs: the same IEEE floating-point specification defines Inf and -Inf, and these are propagated differently than NaN. For instance, I'm pretty sure that Inf > any number and -Inf < any number, which would be what you wanted if your division by zero happened because the zero was just supposed to be a small number. If your inputs are rounded and suffer from measument error (like physical measurements taken by hand), the difference of two large quantities can result in zero. Without the division-by-zero, you would have gotten some large number, and maybe you don't care how large it is. In that case, In and -Inf are perrfectly valid results.

It can be formally correct, too--- just say you're working in the extended reals.

Jim Pivarski
  • 221
  • 1
  • 3
  • But we can't tell whether the denominator was intended to be positive or negative, so division might yield +inf when -inf was desired, or visa versa. – Daniel Lubarov Nov 15 '13 at 17:31
  • True, your measurement error is too small to distinguish between +inf and -inf. This most closely resembles the Riemann sphere, in which the whole complex plane is mapped to a ball with exactly one infinite point (the point diametrically opposite to the origin). Very large positive numbers, very large negative numbers, and even very large imaginary and complex numbers are all close to that one infinite point. With a little measurement error, you can't distinguish them. – Jim Pivarski Nov 15 '13 at 21:21
  • If you're working in that sort of system, you'd have to identify +inf and -inf as being equivalent, just as you have to identify +0 and -0 as being equivalent, even though they have different binary representations. – Jim Pivarski Nov 15 '13 at 21:22
12

Other answers have already considered the relative merits of your ideas. I propose another one: use basic flow analysis to determine whether a variable can be zero. Then you can simply disallow division by variables that are potentially zero.

x = ...
y = ...

if y ≠ 0:
  return x / y    // In this block, y is known to be nonzero.
else:
  return x / y    // This, however, is a compile-time error.

Alternatively, have an intelligent assert function that establishes invariants:

x = ...
require x ≠ 0, "Unexpected zero in calculation"
// For the remainder of this scope, x is known to be nonzero.

This is as good as throwing a runtime error—you skirt undefined operations entirely—but has the advantage that the code path need not even be hit for the potential failure to be exposed. It can be done much like ordinary typechecking, by evaluating all branches of a program with nested typing environments for tracking and verifying invariants:

x = ...           // env1 = { x :: int }
y = ...           // env2 = env1 + { y :: int }
if y ≠ 0:         // env3 = env2 + { y ≠ 0 }
  return x / y    // (/) :: (int, int ≠ 0) → int
else:             // env4 = env2 + { y = 0 }
  ...
...               // env5 = env2

Furthermore, it extends naturally to range and null checking, if your language has such features.

Jon Purdy
  • 20,437
  • 7
  • 63
  • 95
  • 4
    Neat idea, but this type of constraint solving is NP-complete. Imagine something like `def foo(a,b): return a / ord(sha1(b)[0])`. The static analyzer can't invert SHA-1. Clang has this type of static analysis and its great for finding shallow bugs but there are plenty of cases it can't handle. – Mark E. Haase Jul 12 '14 at 20:12
  • 9
    this is not NP-complete, this is impossible -- says halting lemma. However, the static analyzer doesn't need to solve this, it can just crap out on a statement like this and require you to add an explicit assertion or decoration. – MK01 Jul 13 '14 at 04:07
  • 1
    @MK01: In other words, the analysis is “conservative”. – Jon Purdy Jul 13 '14 at 19:04
8

3. Terminate the execution of the program and report to the user a severe error occurred.

[This option] is not practical…

Of course it is practical: It is the programmers' responsibility to write a program that actually makes sense. Dividing by 0 does not make any sense. Therefore, if the programmer is performing a division, it is also his/her responsibility to verify beforehand that the divisor is not equal to 0. If the programmer fails to perform that validation check, then s/he should realize that mistake as soon as possible, and denormalized (NaN) or incorrect (0) computation results simply won't help in that respect.

Option 3 happens to be the one I would have recommended to you, btw, for being the most straightforward, honest, and mathematically correct one.

stakx
  • 2,138
  • 18
  • 29
4

It seems like a bad idea to me to run important tasks (ie; "nightly cron") in an environment where errors are ignored. It's a terrible idea to make this a feature. This rules out options 1 and 2.

Option 3 is the only acceptable solution. Exceptions don't have to be part of the language, but they are part of reality. Your termination message ought to be as specific and informative as possible about the error.

ddyer
  • 4,060
  • 15
  • 18
4

IEEE 754 actually has a well defined solution for your problem. Exception handling without using exceptions http://en.wikipedia.org/wiki/IEEE_floating_point#Exception_handling

1/0  = Inf
-1/0 = -Inf
0/0  = NaN

this way all your operations make mathematically sense.

\lim_{x \to 0} 1/x = Inf

In my opinion following IEEE 754 makes the most sense since it ensures that your calculations are as correct as it going to be on a computer and you are also consistent with the behaviour of other programming languages.

The only problem that arises is that Inf and NaN are gonna contaminate your results and your users will not know exactly where the problem is coming from. Take a look at a language like Julia that does this quite well.

julia> 1/0
Inf

julia> -1/0
-Inf

julia> 0/0
NaN

julia> a = [1,1,1] ./ [2,1,0]
3-element Array{Float64,1}:
   0.5
   1.0
 Inf

julia> sum(a)
Inf

julia> a = [1,1,0] ./ [2,1,0]
3-element Array{Float64,1}:
   0.5
   1.0
 NaN

julia> sum(a)
NaN

The division error is correctly propagated through the mathematical operations but at the end the user does not necessarily know from which operation the error stems from.

edit: I didn't see the second part of the answer by Jim Pivarski which is basically what I am saying above. My bad.

vchuravy
  • 141
  • 2
2

SQL, easily the language most widely used by non-programmers, does #3, for whatever that's worth. In my experience observing and assisting non-programmers writing SQL, this behavior is generally well understood and easily compensated for (with a case statement or the like). It helps that the error message you get tends to be quite direct e.g. in Postgres 9 you get "ERROR: division by zero".

Noah Yetter
  • 121
  • 3
2

I think the problem is " targeted at novice users. --> So there is no support for ..."

Why are you thinking that exception handling is problematic for novice users?

What is worse? Have a "difficult" feature or have no idea why something happened? What could confuse more? A crash with a core dump or "Fatal error: Divide by Zero"?

Instead, I think is FAR better to aim for GREAT message errors. So do instead: "Bad calculation, Divide 0 / 0" (ie: Always show the DATA that cause the problem, not just the kind of problem). Look at how PostgreSql do the message errors, that are great IMHO.

However, you can look at other ways to work with exceptions like:

http://dlang.org/exception-safe.html

I also have dream on build a language, and in this case I think that mix a Maybe/Optional with normal Exceptions could do be the best:

def openFile(fileName): File | Exception
    if not(File.Exist(fileName)):
        raise FileNotExist(fileName)
    else:
        return File.Open()

#This cause a exception:

theFile = openFile('not exist')

# But this, not:

theFile | err = openFile('not exist')
mamcx
  • 412
  • 3
  • 8
2

There are two fundamental reasons for a divide by zero.

  1. In a precise model (like integers), you get a divide by zero DBZ because the input is wrong. This is the kind of DBZ that most of us think of.
  2. In non-precise model (like floating pt), you might get a DBZ because of rounding-error even though the input is valid. This is what we don't normally think of.

For 1. you have to communicate back to the users that they made a mistake because they are the one responsible and they are the one who know best how to remedy the situation.

For 2. This is not user fault, you can finger point to algorithm, hardware implementation, etc but this is not user's fault so you shouldn't terminate the program or even throw exception (if allowed which is not in this case). So a reasonable solution is to continue the operations in some reasonable manner.

I can see the the person asking this question asked for case 1. So you need to communicate back to the user. Using whatever floating point standard, Inf, -Inf, Nan, IEEE doesn't fit in this situation. Fundamentally wrong strategy.

InformedA
  • 2,991
  • 2
  • 19
  • 28
1

In my view your language should provide a generic mechanism for detecting and handling errors. Programming errors should be detected at compile time (or as early as possible) and should ordinarily lead to program termination. Errors that result from unexpected or erroneous data, or from unexpected external conditions, should be detected and made available for appropriate action, but allow the program to continue whenever possible.

Plausible actions include (a) terminate (b) prompt the user for an action (c) log the error (d) substitute a corrected value (e) set an indicator to be tested in code (f) invoke an error handling routine. Which of these you make available and by what means are choices you have to make.

From my experience, common data errors like faulty conversions, divide by zero, overflow and value out of range are benign and should by default be handled by substituting a different value and setting an error flag. The (non-programmer) using this language will see the faulty data and quickly understand the need to check for errors and handle them.

[For an example, consider an Excel spreadsheet. Excel does not terminate your spreadsheet because a number overflowed or whatever. The cell gets a strange value and you go find out why and fix it.]

So to answer your question: you should certainly not terminate. You might substitute NaN but you should not make that visible, just make sure the calculation completes and generates a strange high value. And set an error flag so that users who need it can determine that an error occurred.

Disclosure: I created just such a language implementation (Powerflex) and addressed exactly this problem (and many others) in the 1980s. There has been little or no progress on languages for non-programmers in the last 20 years or so, and you will attract heaps of criticism for trying, but I really hope you succeed.

david.pfx
  • 8,105
  • 2
  • 21
  • 44
1

I liked the ternary operator where you provide an alternate value in case the denumerator is 0.

One more idea I didn't see is to produce a general "invalid" value. A general "this variable doesn't have a value because the program did something bad", which carries a full stack trace with itself. Then, if you ever use that value anywhere, the result is again invalid, with the new operation attempted on top (i.e. if the invalid value ever appears in an expression, the entire expression yields invalid and no function calls are attempted; an exception would be boolean operators - true or invalid is true and false and invalid is false - there may be other exceptions to that, too). Once that value is no longer referenced anywhere, you log a nice long description of the entire chain where things were wrong and continue business as usual. Maybe email the trace to the project lead or something.

Something like the Maybe monad basically. It will work with anything else that can fail, too, and you can allow people to construct their own invalids. And the program will continue to run as long as the error isn't too deep, which is what is really wanted here, I think.

Moshev
  • 111
  • 1
0

Disallow it in the language. That is to say, disallow dividing by a number until it's provably not zero, usually by testing it first. Ie.

int div = random(0,100);
int b = 10000 / div; // Error E0000: div might be zero
MSalters
  • 8,692
  • 1
  • 20
  • 32
  • To do this you need a new numeric type, a natural number, as opposed to an integer. That could be...difficult...to deal with. – Servy Nov 15 '13 at 18:24
  • @Servy: No, you wouldn't. Why would you? You do need logic in the compiler to determine possible values, but you want that anyway (for optimizing reasons). – MSalters Nov 15 '13 at 18:27
  • If you don't have a different type, one for zero and one for non-zero values, then you wouldn't be able to solve the problem in the general case. You'd either have false positives, and force the user to check against zero way more often than they actually should, or you'll create situations where they can still divide by zero. – Servy Nov 15 '13 at 18:29
  • @Servy: You're mistaken: a compiler can track that state without needing such a type, and for instance GCC already does so. E.g. the C type `int` allows zero values, but GCC can still determine where in the code specific ints can't be zero. – MSalters Nov 15 '13 at 18:32
  • 2
    But only in certain cases; it can't do so, with 100% accuracy, in all cases. You'll either have false positives or false negatives. This is provably true. For example, I could create a snippet of code that may or may not even *complete*. If the compiler can't even know if it finished, how could it know if the resulting int is non-zero? It can catch simple obvious cases, but not *all* cases. – Servy Nov 15 '13 at 18:35
0

As you a writing a programming language, you should take advantage of the fact and make it mandatory to include an action for the devise by zero state. a <= n / c :0 div-by-zero-action

I know what I've just suggested is essentially adding a 'goto' to your PL.

Stephen
  • 397
  • 1
  • 3
  • 8