Project

General

Profile

Feature #16894

Integer division for Ruby 3

Added by ankane (Andrew Kane) about 2 months ago. Updated about 2 months ago.

Status:
Open
Priority:
Normal
Target version:
-
[ruby-core:98371]

Description

Hi Ruby team,

It'd be great if division in Ruby matched what we all learned in school.

1 / 2 == 0.5

New developers wouldn't immediately be confused when they try to do division and experienced developers could stop adding to_f to their code (typically after they get tripped up on the first run). In my experience, floating point division is way more common than floor division. This could definitely break existing code, so I understand it's a decision that shouldn't be made lightly. Overall, Ruby is really intuitive, but this is one place where it's not.

It looks like this was considered for Ruby 2.0 as well as few years ago in #5512, so feel free to close if it's not something you'd like to reconsider right now.


Files

integer division.png (14.3 KB) integer division.png NuriYuri (Youri Nouri), 05/15/2020 05:37 PM
#1

Updated by ankane (Andrew Kane) about 2 months ago

  • Description updated (diff)

Updated by ankane (Andrew Kane) about 2 months ago

  • Description updated (diff)

Sorry, for the edits, first issue! Changed normal to floating point.

Updated by marcandre (Marc-Andre Lafortune) about 2 months ago

  • Assignee set to matz (Yukihiro Matsumoto)

I would bet that a majority of Rubyists would agree that 1/2 == 0 is an unfortunate choice. Either 0.5 or 1/2r would probably be a better solution.

Your proposal has at least two issues.
1) Why 0.5 and not 1/2r
0) Much more importantly, is it worth the incompatibility.

My feeling for 0) is "absolutely not".

Notes:
a) mathn was already redefining Integer#/ and that was already a source of confusion and issues.
b) refinements make it easy for you to redefine Integer#/ to fit your purposes, and affect only your code and not that of other gems which may rely on 1/2 #=> 0. I recommend you go that route instead.

Updated by naruse (Yui NARUSE) about 2 months ago

1) Why 0.5 and not 1/2r

I'm for 1/2r.

Updated by sawa (Tsuyoshi Sawada) about 2 months ago

What would you do when you want integer division? Have you thought of it? (a / b).round (a / b).flooris not a solution. That is like saying, "write "a"[0, 0] when you want an empty string."

Updated by ankane (Andrew Kane) about 2 months ago

Thanks for the responses!

Re 0.5 vs 1/2r: In my experience, developers use floats way more than rationals.

Re "is it worth the incompatibility?": I like to imagine it's not confusing new developers 20 years for now. If it's a change we think should be made, better to do it sooner than later (since more code is written each day).

Re integer division: I personally think (a / b).floor is reasonable, since in my experience it occurs a lot less often than a / b.to_f.

Updated by jzakiya (Jabari Zakiya) about 2 months ago

Ruby already has a.div b for explicit integer division.

Nim also uses a div b for integer division.

Crystal v0.31 switched to a // b, so now 9 // 2 = 4 and 9 / 2 = 4.5.

Python, et al, also use //.

Updated by nobu (Nobuyoshi Nakada) about 2 months ago

jzakiya (Jabari Zakiya) wrote in #note-7:

Ruby already has a.div b for explicit integer division.

Also a.quo b for rational division, and a.fdiv b for float division.

Updated by sawa (Tsuyoshi Sawada) about 2 months ago

jzakiya (Jabari Zakiya) wrote in #note-7:

Ruby already has a.div b for explicit integer division.

Thanks, I had forgotten about that. But while the remainder can be achieved by the infix operator %, it would be strange if there is no infix operator for getting the quotient.

Crystal v0.31 switched to a // b, so now 9 // 2 = 4 and 9 / 2 = 4.5.

Python, et al, also use //.

That may be acceptable if it does not conflict with current syntax.

But that is ugly. Ruby is object oriented, and it makes clear sense that the same method name / working on integer and rational means different things. Converting an integer to float in order to do float division is the clearest API I can think of.

Integer division is taught at elementary school (contrary to what ankane describes), and is such a basic operation (perhaps contrary to what ankane feels).

Updated by ankane (Andrew Kane) about 2 months ago

Here's a good read on the thoughts and motivation behind Python changing it: https://www.python.org/dev/peps/pep-0238/

Updated by jzakiya (Jabari Zakiya) about 2 months ago

I was just pointing out that other languages recognized this issue and only fairly recently chose to make syntax changes to explicitly deal with it.

I think using // is probably the least worst choice because it's already being used by Python, which has a large user base, and Crystal, which would make it drop-in compatible, and you then wouldn't have to create a whole different process to create mindshare because the general coding community is becoming used to seeing it and knowing what it means.

Also, as pointed out in the Python PEP, it's the simplest one to edit to change / to // on a case-by-case basis is people's code.

I do allot of Crystal programming, so when it made the change to //|//= in v0.31 (current v0.34) it wasn't that difficult to upgrade old code.

Updated by NuriYuri (Youri Nouri) about 2 months ago

It'd be great if division in Ruby matched what we all learned in school.

In my time it was C++ or Java at school and in those language (like Ruby) if the two number are Integer, then the result is an Integer. 1 / 2 in those language = 0.

I though that python was the only language treating integer division as float division.

Also, I'm using a lot the Integer division, for array index, definition of max counter and just to get integer division. Having to replace 1000+ lines from / to .div just because Python does it wrong is really pleasing.

Updated by Eregon (Benoit Daloze) about 2 months ago

IMHO this is completely unrealistic for compatibility, and very clearly not worth breaking all the code (even more so for floats which lose precision).

Updated by ankane (Andrew Kane) about 2 months ago

fwiw, array indexing code likely wouldn't need changed.

a = ["zero", "one", "two"]
a[1/2]   # "zero"
a[1/2.0] # "zero"

Some code would break, but we have major versions to account for this. It seems like a smaller change than keyword arguments (which I realize is being reconsidered).

mruby uses float division, and other languages have made the change as jzakiya pointed out, so I don't think it's a crazy idea (even if Ruby decides not to do it).

// is the current syntax for regular expression, so I'm not sure that's an option. I personally don't think floor division is common enough to need an operator, but I realize others don't share that opinion.

Updated by jzakiya (Jabari Zakiya) about 2 months ago

The following is meant to be an objective assessment of the proposal, taking no stand on approval, or not.

The proposal is for inclusion of the "feature" in Ruby 3 . By definition, Ruby 3 will be a major version, which will incorporate new features, while trying not to break (too much) prior code. Anyone upgrading will know they will have to modify existing code to use any of the new features. Similarly, any possible breaking changes (e.g. for /, et al) will be psychologically less stressful on the whole, as it will be incorporated into the general upgrade process. I would imagine tools like Rubocop will evolve to incorporate these changes to assist people to upgrade.

Also, there will undoubtedly be a plethora of blogs, articles, and presentations on all the changes introduced by Ruby 3, so the aware user should not be caught flatfooted on what to look out for in the upgrade process. For a new user it wouldn't matter at all, since whatever the syntax is that's the syntax they would just learn to use (no unlearning needed).

There are two (2) major components to the proposal:

  1. should it occur, i.e. is there enough of a rational, et al, basis to make the change.
  2. what should be the agreed upon syntax to represent the change.

1) This "feature" is now codified in syntax in various languages, e.g. Python, Crystal, Nim, Sidef, et al (I haven't done an extensive search for a complete list). One explicit purpose (especially for compiled languages like, Nim and Crystal) is to consistently/visually establish the expected result of division.

In a compiled language with explicit types, it's easier for writing the compiler to know if the two arguments are integers then // tells the compiler to produce another integer, while / says convert to float. The opposite if the arguments are float.

Also, the source code now becomes visually consistent and explicit for the code writer and its readers.

As a matter of preference, Ruby made the decision to base the result of division using / on the argument types, so the programmer still had to know the desired outcome and then use the appropriate syntax.

Thus for explicit numbers: 9 / 2 = 4 and 9 / 2.0 = 4.5
But also then for variables which could be any type: a.to_i / b.to_i = c (integer) and a / b.to_f = c (float)
Either way, you need to know the proper operational syntax to get the desired outcome.

2) As a matter of operation syntax, it's probably "best" to use familiar syntax for easier/wider adoptability (Crystal, Sidef, and Python use // while Nim uses div), have it be short, and contexturally non-conflicting.

I don't know how the usage of // exists in regexs, but this question's answer is empirical, not subjective. Just do an entire code base check on the symbols / and // to produce a baseline profile of their use. I would guess their use is so specific as not to cause contextual parsing problems, especially since Ruby is rife with aliasing of symbols. Also, the use of / will occur most prominently in numerical heavy modules|code further specifying its use.

I hope this has been helpful.

Updated by shan (Shannon Skipper) about 2 months ago

I'd really rather not deal with Floats unless absolutely necessary.

Raku (Perl 6) and Clojure's default division to a rational seems really nice. I especially like how Raku's just appear as decimal digits.

nobu (Nobuyoshi Nakada) wrote in #note-15:

Just FYI, I've made these patches for division recently.
https://github.com/ruby/ruby/compare/master...nobu:feature/fract-slash?expand=1
https://github.com/ruby/ruby/compare/master...nobu:feature/division?expand=1

That's fun! It took me a few minutes longer than I anticipated to get "÷" working in my terminal. :)

Updated by shevegen (Robert A. Heiler) about 2 months ago

I am not necessarily against such a change per se, but I think it would come with a
high cost right now. It should perhaps be discussed more for ruby 4.0 or so.

By the way, mruby behaves in a similar way. Matz explained that years ago
in a video. :)

For example, if you do start mirb, and do this right now:

1/2 # => 0.5

So in some ways MRI would sync towards mruby here. ;)

But as said, I think if this were to be changed for MRI, I would recommend
a long transition time, so perhaps a discussion for this at ruby ~3.2 or
so (hard to predict the future!).

PS: I should also add that I have a slight favour in retaining the current
behaviour, but I also don't mind a change. I also agree with sawa in that
// is not really pretty; I actually prefer .to_i and .to_f rather than
//.

Nobu mentioned: a.quo b

I did not even know about that method! :) (Not sure if I would need it,
but it's interesting how you can learn from the bugtracker.)

Also available in: Atom PDF