Misleading `DivideError` message

The definition of DivideError has been unchanged for a long time, and I cannot decide whether it should be changed now, but the error message is unfriendly and misleading.
https://docs.julialang.org/en/v1/base/base/#Core.DivideError

Even though the type name is not IntegerDivisionError, it targets the “integer” division. I think this is due to the fact that “division by zero” on floating-point types does not raise an error.

However, DivideError can occur in many operations of various numeric types. Depending on the internal implementation, it may even occur in functions that take floating point numbers as input and output. Also, DivideError can be caused by overflow as well as division by zero, but IIUC, this is just a compromise in performance.

julia> typemin(Int)÷-1
ERROR: DivideError: integer division error

Therefore, the DivideError due to overflow is also not a good enough reason to limit the scope to integers only.

I think we should redefine DivideError as an error for the generic “division by zero” and just mention “integer division” as its typical cause. In that case, the error message would be something like “DivideError: division by zero or too small value”. (The “too small value” is not a formal phrase, though.)

Any thoughts?

Technically, this is a breaking change since currently it is documented to be an error for

Integer division was attempted with a denominator value of 0.

Following this, the ÷ -1 example should not throw this error, but this looks like a compromise, see

Frankly, I am not sure that not being able to perform a division warrants its own error. I don’t think a lot code relies on this, but since we cannot get rid of it now at least we should not extend its scope. Whenever an arithmetic operation does not make sense or is infeasible, DomainError should be fine.

2 Likes

Indeed, extending its scope is a breaking change. As a practical matter, changing the error message will require some changes to the tests.

Whenever an arithmetic operation does not make sense or is infeasible, DomainError should be fine.

I think DomainError is a valid error for “division by zero” in many cases, but it is just an error on the values of the input to a method. It can be even more misleading than “integer division error” if the condition for denominator to be zero is not simple.

I would expect that if div(::T, ::T) is defined on a field, so is zero(::T).

Generally, the granularity of exception hierarchies is tricky to get right, because of various considerations (informing the user, use in control flow/recovery paths, performance considerations). See the discussion for

FWIW, I think that callers should strive to handle exceptions that they have a solution for, but accept that not all methods will deliver the most specific error and it is OK to just let it through. Moreover, exceptions should only be used for control flow as a last resort because they are really expensive.

If in some specific context

  1. checking zero is expensive,
  2. but can be done as part of div or similar,

I would recommend defining a method

struct DivisionByZero end # not <: Exception

@inline div_or_dbz(a, b) = zero(b) ? DivisionByZero() : div(a, b)

function div_or_dbz(a::T, b::T) where {T <: MySpecialType}
    ... # do the special thing
end

and using it accordingly.

1 Like

As you probably got my point, it was not about the group theory, but about APIs. I think the following function are not suitable for throwing DomainError:

f(a::AbstractArray{<:Real}) = div(maximum(a), minimum(a))

I’m not trying to avoid DivideError because the message is misleading. We actually can get DivideErrors on operations of non-integer types, so I want to improve the message.

I think that the two are closely related — even though abstract algebras are not perfectly mapped to the numerical types we use, keeping their properties in mind is necessary for designing APIs. For <:Real, this is a very difficult task with a lot of open issues.

I don’t understand why — is there a mathematical counterpart to f that is well-defined when \min(a) = 0?

Maybe the example wasn’t a good one; what if it’s SHA256 instead of minimum? SHA256 is also mathematically well-defined. Of course, you can still use DomainError, but the error message should be a tautology.

In any case, my concern is with the DivideError message.

Sure, but if that can be 0 (which I am not sure about, AFAIK the image does not contain all possible values), then again f isn’t well-defined.

Yes, I realized that. I am just arguing against extending it to other errors.

It is still not clear to me why you want this. Do you want to catch it? Or just have a better error message for the user?

Primarily the latter. I don’t think the “integer” in the error message is helpful. If it’s useless anyway, I think “internal” is still more neutral and less misleading. However, if the “integer” is dropped from the error message, there is no longer any evidence that the error is an “integer division error” other than the documentation. So I thought it would be a good idea to change the specification (the documentation) to fit the actual situation.

Of course, I also agree with the importance of specifications in general. So if there is a reason why DivideError should only target integers, that should be kept. In that case, the error type just for the general “division by zero” does not exist in Julia’s Core/Base.

This is just a side note, but I made a PR open in FixedPointNumbers.jl that changes the internal implementation of div from an integer division to a floating-point division. Using floating-point is just an implementation matter, so the new implementation can also throw the DivideError. But this is nonsense in two ways. It does not use integer divisions, and the fixed-point numbers are not (always) integers.

I started this thread because if we want to change the DivideError messages and the specification, it is better to do it before the next LTS version is released.

If its only about the error message, are you aware that DomainError allows you to specify one and it is printed? Eg

julia> throw(DomainError("I can't divide by zero"))
ERROR: DomainError with I can't divide by zero:

I think that this is the best approach, unless the caller wants to catch errors by type.

1 Like

Sorry to mislead you, but FixedPointNumbers.jl is just about the motivation and is not technically relevant to this topic. This is a Julia’s issue, not a problem in packages or user codes.

Again, I have a problem with the DivideError message, and the friendly DomainError or ArgumentError or user-defined errors are not the solution.

Having said that, I appreciate your kind response.:+1: