Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

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.




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

Search: