I consider this a bug in Ruby...

  • Thread starter Just Another Victim of the Ambient Morality
  • Start date
J

Just Another Victim of the Ambient Morality

I would like to know why the following code doesn't work:


# This should be 1 followed by 13 zeroes...
range = 0..10000000000000

# This should be 1 followed by 12 zeroes...
range.step(1000000000000){|i| puts i}


The error I get is this:


RangeError: bignum too big to convert into 'long'


Why is there any "converting" to do? How can this not work?
My best guess is that the Range class is not written in Ruby and, hence,
has funny limitation on how it may work. If this is the case, it is totally
unnecessary. One of the things I like about Ruby is how much of Ruby can be
written in Ruby. "require" can be written in Ruby, "attr_accessor" can be
written in Ruby and Range can be written in ruby.
What's going on, here?
Thank you...
 
N

Nobuyoshi Nakada

Hi,

At Wed, 5 Dec 2007 08:40:11 +0900,
Just Another Victim of the Ambient Morality wrote in [ruby-talk:282100]:
Why is there any "converting" to do? How can this not work?
My best guess is that the Range class is not written in Ruby and, hence,
has funny limitation on how it may work.

Yes, and performance issue.


Index: stable/range.c
===================================================================
--- stable/range.c (revision 14103)
+++ stable/range.c (working copy)
@@ -255,10 +255,18 @@ range_each_func(range, func, v, e, arg)

static VALUE
-step_i(i, iter)
+step_i(i, arg)
VALUE i;
- long *iter;
+ VALUE arg;
{
- iter[0]--;
- if (iter[0] == 0) {
+ VALUE *iter = (VALUE *)arg;
+
+ if (FIXNUM_P(iter[0])) {
+ iter[0] -= INT2FIX(1) & ~FIXNUM_FLAG;
+ }
+ else {
+ VALUE one = INT2FIX(1);
+ iter[0] = rb_funcall(iter[0], '-', 1, &one);
+ }
+ if (iter[0] == INT2FIX(0)) {
rb_yield(i);
iter[0] = iter[1];
@@ -308,11 +316,20 @@ range_step(argc, argv, range)
if (rb_scan_args(argc, argv, "01", &step) == 0) {
step = INT2FIX(1);
+ unit = 1;
+ }
+ else if (FIXNUM_P(step)) {
+ unit = NUM2LONG(step);
+ }
+ else {
+ VALUE tmp = rb_to_int(step);
+ unit = rb_cmpint(tmp, step, INT2FIX(0));
+ step = tmp;
}
-
- unit = NUM2LONG(step);
if (unit < 0) {
rb_raise(rb_eArgError, "step can't be negative");
- }
- if (FIXNUM_P(b) && FIXNUM_P(e)) { /* fixnums are special */
+ }
+ if (unit == 0)
+ rb_raise(rb_eArgError, "step can't be 0");
+ if (FIXNUM_P(b) && FIXNUM_P(e) && FIXNUM_P(step)) { /* fixnums are special */
long end = FIX2LONG(e);
long i;
@@ -331,11 +348,9 @@ range_step(argc, argv, range)

if (!NIL_P(tmp)) {
- VALUE args[5];
- long iter[2];
+ VALUE args[5], iter[2];

b = tmp;
- if (unit == 0) rb_raise(rb_eArgError, "step can't be 0");
args[0] = b; args[1] = e; args[2] = range;
- iter[0] = 1; iter[1] = unit;
+ iter[0] = INT2FIX(1); iter[1] = step;
rb_iterate((VALUE(*)_((VALUE)))str_step, (VALUE)args, step_i,
(VALUE)iter);
@@ -344,5 +359,4 @@ range_step(argc, argv, range)
ID c = rb_intern(EXCL(range) ? "<" : "<=");

- if (rb_equal(step, INT2FIX(0))) rb_raise(rb_eArgError, "step can't be 0");
while (RTEST(rb_funcall(b, c, 1, e))) {
rb_yield(b);
@@ -351,7 +365,6 @@ range_step(argc, argv, range)
}
else {
- long args[2];
+ VALUE args[2];

- if (unit == 0) rb_raise(rb_eArgError, "step can't be 0");
if (!rb_respond_to(b, id_succ)) {
rb_raise(rb_eTypeError, "can't iterate from %s",
@@ -359,6 +372,6 @@ range_step(argc, argv, range)
}

- args[0] = 1;
- args[1] = unit;
+ args[0] = INT2FIX(1);
+ args[1] = step;
range_each_func(range, step_i, b, e, args);
}
Index: trunk/range.c
===================================================================
--- trunk/range.c (revision 14103)
+++ trunk/range.c (working copy)
@@ -248,8 +248,14 @@ static VALUE
step_i(VALUE i, void *arg)
{
- long *iter = (long *)arg;
+ VALUE *iter = arg;

- iter[0]--;
- if (iter[0] == 0) {
+ if (FIXNUM_P(iter[0])) {
+ iter[0] -= INT2FIX(1) & ~FIXNUM_FLAG;
+ }
+ else {
+ VALUE one = INT2FIX(1);
+ iter[0] = rb_funcall(iter[0], '-', 1, &one);
+ }
+ if (iter[0] == INT2FIX(0)) {
rb_yield(i);
iter[0] = iter[1];
@@ -298,13 +304,20 @@ range_step(int argc, VALUE *argv, VALUE
if (rb_scan_args(argc, argv, "01", &step) == 0) {
step = INT2FIX(1);
+ unit = 1;
+ }
+ else if (FIXNUM_P(step)) {
+ unit = NUM2LONG(step);
+ }
+ else {
+ VALUE tmp = rb_to_int(step);
+ unit = rb_cmpint(tmp, step, INT2FIX(0));
+ step = tmp;
}
-
- unit = NUM2LONG(step);
if (unit < 0) {
rb_raise(rb_eArgError, "step can't be negative");
- }
+ }
if (unit == 0)
rb_raise(rb_eArgError, "step can't be 0");
- if (FIXNUM_P(b) && FIXNUM_P(e)) { /* fixnums are special */
+ if (FIXNUM_P(b) && FIXNUM_P(e) && FIXNUM_P(step)) { /* fixnums are special */
long end = FIX2LONG(e);
long i;
@@ -323,12 +336,11 @@ range_step(int argc, VALUE *argv, VALUE

if (!NIL_P(tmp)) {
- VALUE args[2];
- long iter[2];
+ VALUE args[2], iter[2];

b = tmp;
args[0] = e;
args[1] = EXCL(range) ? Qtrue : Qfalse;
- iter[0] = 1;
- iter[1] = unit;
+ iter[0] = INT2FIX(1);
+ iter[1] = step;
rb_block_call(b, rb_intern("upto"), 2, args, step_i, (VALUE)iter);
}
@@ -344,5 +356,5 @@ range_step(int argc, VALUE *argv, VALUE
}
else {
- long args[2];
+ VALUE args[2];

if (!rb_respond_to(b, id_succ)) {
@@ -350,6 +362,6 @@ range_step(int argc, VALUE *argv, VALUE
rb_obj_classname(b));
}
- args[0] = 1;
- args[1] = unit;
+ args[0] = INT2FIX(1);
+ args[1] = step;
range_each_func(range, step_i, b, e, args);
}
@@ -867,6 +879,4 @@ void
Init_Range(void)
{
- VALUE members;
-
id_cmp = rb_intern("<=>");
id_succ = rb_intern("succ");
 
B

botp

RangeError: bignum too big to convert into 'long'

possible bug. bignum is really too big for long :)
Why is there any "converting" to do? How can this not work?

range is expensive.
try numeric step,

irb(main):010:0> r=1_000_000_000_000
=> 1000000000000
irb(main):011:0> r.step(r*10,r){|x| p x}
1000000000000
2000000000000
3000000000000
4000000000000
5000000000000
6000000000000
7000000000000
8000000000000
9000000000000
10000000000000
=> 1000000000000
irb(main):012:0>

kind regards -botp
 
J

Just Another Victim of the Ambient Morality

Nobuyoshi Nakada said:
Hi,

At Wed, 5 Dec 2007 08:40:11 +0900,
Just Another Victim of the Ambient Morality wrote in [ruby-talk:282100]:
Why is there any "converting" to do? How can this not work?
My best guess is that the Range class is not written in Ruby and,
hence,
has funny limitation on how it may work.

Yes, and performance issue.

Just so I'm clear on this; the Range class is implemented in C for
performance reasons? Was the performance of a Ruby version actually an
issue? I don't think anybody here uses Ruby for its blazing speed. I've
said this before but I guess it bears repeating. I don't use Ruby to write
fast programs, I use Ruby to write programs fast. This is, obviously, just
a personal opinion but that's the role Ruby plays for me and I think it's
the most common case.
Furthermore, I think there is an important lesson to be had, here.
There are many different stories, out there, that exemplify this but they
all end with the same phrase: I can make my program fast, too, if it didn't
have to work. This implementation of Range doesn't work. At the very
least, it's buggy, which, to some people, is the same thing.
Even if performance were important, it didn't even fall back onto a Ruby
implementation. It might be a little harder to maintain but you could write
a Ruby version and use that whenever you encounter a "RangerError: bignum
too big to convert into 'long'" error. I don't think Range changes too much
between Ruby releases, so I really don't see it being a maintenance
headache. Again, generally, speaking, a slow program that works is better
than a fast program that doesn't work...
Finally, the .each method worked! I didn't let it run all the way to
MAX_LONG, or whatever the constant is, if there is a constant, but you all
know what I mean. Maybe a bug occurs there but what little I let run
worked. How can .each work but .step not work? Especially ironic since
..step produced less iterations than .each. I mean, where's the concern for
performance, there? You'd imagine that there'd be more concern for
performance for .each since it will produce more iterations...
So, perhaps all this can be fixed for Ruby 1.9 and Rite? What do you
all think?
Thank you...
 
J

Just Another Victim of the Ambient Morality

botp said:
possible bug. bignum is really too big for long :)


range is expensive.
try numeric step,

irb(main):010:0> r=1_000_000_000_000
=> 1000000000000
irb(main):011:0> r.step(r*10,r){|x| p x}
1000000000000
2000000000000
3000000000000
4000000000000
5000000000000
6000000000000
7000000000000
8000000000000
9000000000000
10000000000000
=> 1000000000000
irb(main):012:0>

In what way is Range expensive? Why is it so expensive?
Thank you...
 
N

Nobuyoshi Nakada

Hi,

At Wed, 5 Dec 2007 14:25:07 +0900,
Just Another Victim of the Ambient Morality wrote in [ruby-talk:282132]:
Nobuyoshi Nakada said:
Hi,

At Wed, 5 Dec 2007 08:40:11 +0900,
Just Another Victim of the Ambient Morality wrote in [ruby-talk:282100]:
Why is there any "converting" to do? How can this not work?
My best guess is that the Range class is not written in Ruby and,
hence,
has funny limitation on how it may work.

Yes, and performance issue.

Just so I'm clear on this; the Range class is implemented in C for
performance reasons?

It isn't the only reason why Range is implemented in C, I wrote
about your "limitation."

If Range were written in Ruby, it couldn't be used until
something was loaded. It wasn't acceptable for intrinsic
classes.
 
C

Charles Oliver Nutter

Just said:
Just so I'm clear on this; the Range class is implemented in C for
performance reasons? Was the performance of a Ruby version actually an
issue? I don't think anybody here uses Ruby for its blazing speed.

Perhaps not yet, or not 1.8.6. But this is going to change.

- Charlie
 
L

Lloyd Linklater

Just Another Victim...:
I would like to know why the following code doesn't work:


# This should be 1 followed by 13 zeroes...
range = 0..10000000000000

# This should be 1 followed by 12 zeroes...
range.step(1000000000000){|i| puts i}

fyi, to help with counting zeros, you can use an underscore like a
comma:

range = 0..10_000_000_000_000
 
J

Just Another Victim of the Ambient Morality

Nobuyoshi Nakada said:
Hi,

At Wed, 5 Dec 2007 14:25:07 +0900,
Just Another Victim of the Ambient Morality wrote in [ruby-talk:282132]:
Nobuyoshi Nakada said:
Hi,

At Wed, 5 Dec 2007 08:40:11 +0900,
Just Another Victim of the Ambient Morality wrote in
[ruby-talk:282100]:
Why is there any "converting" to do? How can this not work?
My best guess is that the Range class is not written in Ruby and,
hence,
has funny limitation on how it may work.

Yes, and performance issue.

Just so I'm clear on this; the Range class is implemented in C for
performance reasons?

It isn't the only reason why Range is implemented in C, I wrote
about your "limitation."

Where did you write about this? I'd be happy to read it...

If Range were written in Ruby, it couldn't be used until
something was loaded. It wasn't acceptable for intrinsic
classes.

Okay, this makes more sense to me. So, some built-in classes use Range
and they couldn't if it were written in Ruby.
This brings up several issues. One would be, why not? Wouldn't it be
nice if the Ruby interpreter's built in classes could be written in Ruby?
Now, I understand that implementation issues can be hard to solve but it's
certainly something to keep in mind for something like Ruby 1.9 or Rite...
Another issue would be: did they have to use a long? Just 'cause the
implementation of Range is written in C doesn't mean that they couldn't
still use Bignum, does it? More to the point, couldn't the Ruby C code be
written so that it didn't care what type was passed in, as long as they
supported integral operations? I'd be surprised if it couldn't...
Thank you...
 
M

MonkeeSage

At Wed, 5 Dec 2007 14:25:07 +0900,
Just Another Victim of the Ambient Morality wrote in [ruby-talk:282132]:
Hi,
At Wed, 5 Dec 2007 08:40:11 +0900,
Just Another Victim of the Ambient Morality wrote in
[ruby-talk:282100]:
Why is there any "converting" to do? How can this not work?
My best guess is that the Range class is not written in Ruby and,
hence,
has funny limitation on how it may work.
Yes, and performance issue.
Just so I'm clear on this; the Range class is implemented in C for
performance reasons?
It isn't the only reason why Range is implemented in C, I wrote
about your "limitation."

Where did you write about this? I'd be happy to read it...
If Range were written in Ruby, it couldn't be used until
something was loaded. It wasn't acceptable for intrinsic
classes.

Okay, this makes more sense to me. So, some built-in classes use Range
and they couldn't if it were written in Ruby.
This brings up several issues. One would be, why not? Wouldn't it be
nice if the Ruby interpreter's built in classes could be written in Ruby?

Well, might be nice for people who want to hack on very low-level ruby
stuff without going into the C, but not a good idea. PyPy (a python
interpreter written in python) is a factor of 2000 times slower(!)
than cPython when running on top of cPython. Implementing the base
classes of ruby in ruby, would have similar negative performance
impact.
Now, I understand that implementation issues can be hard to solve but it's
certainly something to keep in mind for something like Ruby 1.9 or Rite...
Another issue would be: did they have to use a long? Just 'cause the
implementation of Range is written in C doesn't mean that they couldn't
still use Bignum, does it? More to the point, couldn't the Ruby C code be
written so that it didn't care what type was passed in, as long as they
supported integral operations? I'd be surprised if it couldn't...
Thank you...

Looks like Nobu posted the patches above.

Regards,
Jordan
 
G

Gerardo Santana Gómez Garrido

Nobuyoshi Nakada said:
Hi,

At Wed, 5 Dec 2007 14:25:07 +0900,
Just Another Victim of the Ambient Morality wrote in [ruby-talk:282132]:
Hi,

At Wed, 5 Dec 2007 08:40:11 +0900,
Just Another Victim of the Ambient Morality wrote in
[ruby-talk:282100]:
Why is there any "converting" to do? How can this not work?
My best guess is that the Range class is not written in Ruby and,
hence,
has funny limitation on how it may work.

Yes, and performance issue.

Just so I'm clear on this; the Range class is implemented in C for
performance reasons?

It isn't the only reason why Range is implemented in C, I wrote
about your "limitation."

Where did you write about this? I'd be happy to read it...

If Range were written in Ruby, it couldn't be used until
something was loaded. It wasn't acceptable for intrinsic
classes.

Okay, this makes more sense to me. So, some built-in classes use Range
and they couldn't if it were written in Ruby.
This brings up several issues. One would be, why not? Wouldn't it be
nice if the Ruby interpreter's built in classes could be written in Ruby?

That's what Rubinius is.

http://rubini.us/

Now, I understand that implementation issues can be hard to solve but it's
certainly something to keep in mind for something like Ruby 1.9 or Rite...
Another issue would be: did they have to use a long? Just 'cause the
implementation of Range is written in C doesn't mean that they couldn't
still use Bignum, does it? More to the point, couldn't the Ruby C code be
written so that it didn't care what type was passed in, as long as they
supported integral operations? I'd be surprised if it couldn't...
Thank you...


That's what Nobuyoshi's patch is about.
 
J

Just Another Victim of the Ambient Morality

Gerardo Santana Gómez Garrido said:
Nobuyoshi Nakada said:
Hi,

At Wed, 5 Dec 2007 14:25:07 +0900,
Just Another Victim of the Ambient Morality wrote in
[ruby-talk:282132]:
Hi,

At Wed, 5 Dec 2007 08:40:11 +0900,
Just Another Victim of the Ambient Morality wrote in
[ruby-talk:282100]:
Why is there any "converting" to do? How can this not work?
My best guess is that the Range class is not written in Ruby
and,
hence,
has funny limitation on how it may work.

Yes, and performance issue.

Just so I'm clear on this; the Range class is implemented in C for
performance reasons?

It isn't the only reason why Range is implemented in C, I wrote
about your "limitation."

Where did you write about this? I'd be happy to read it...

If Range were written in Ruby, it couldn't be used until
something was loaded. It wasn't acceptable for intrinsic
classes.

Okay, this makes more sense to me. So, some built-in classes use
Range
and they couldn't if it were written in Ruby.
This brings up several issues. One would be, why not? Wouldn't it
be
nice if the Ruby interpreter's built in classes could be written in Ruby?

That's what Rubinius is.

http://rubini.us/

Rubinius is a virtual machine implementation. I'm not sure how this
relates...

That's what Nobuyoshi's patch is about.

Oh, so the code that Nobuyoshi posted was a patch that fixed this
problem? MonkeeSage also mentioned that and it would explain Nobuyoshi's
comment about "wrote about your limitation." I didn't understand the code
and he posted it with no context (no summary on what the code was or why he
posted it).
In that case, excellent! I'm glad to see Ruby's implementation
improving...
 
G

Gerardo Santana Gómez Garrido

Hi,

At Wed, 5 Dec 2007 14:25:07 +0900,
Just Another Victim of the Ambient Morality wrote in
[ruby-talk:282132]:
Hi,

At Wed, 5 Dec 2007 08:40:11 +0900,
Just Another Victim of the Ambient Morality wrote in
[ruby-talk:282100]:
Why is there any "converting" to do? How can this not work?
My best guess is that the Range class is not written in Ruby
and,
hence,
has funny limitation on how it may work.

Yes, and performance issue.

Just so I'm clear on this; the Range class is implemented in C = for
performance reasons?

It isn't the only reason why Range is implemented in C, I wrote
about your "limitation."

Where did you write about this? I'd be happy to read it...


If Range were written in Ruby, it couldn't be used until
something was loaded. It wasn't acceptable for intrinsic
classes.

Okay, this makes more sense to me. So, some built-in classes use
Range
and they couldn't if it were written in Ruby.
This brings up several issues. One would be, why not? Wouldn't i= t
be
nice if the Ruby interpreter's built in classes could be written in Ru=
by?

That's what Rubinius is.

http://rubini.us/

Rubinius is a virtual machine implementation. I'm not sure how this
relates...

From the web page I linked to:

"Rubinius implements the core libraries in Ruby, providing a system
that is much more accessible, easier to develop and to extend."

--=20
Gerardo Santana
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top