[ruby-core:75324] [Ruby trunk Bug#12337] inconsistency between Fixnum#coerce and Bignum#coerce

From: akr@...
Date: 2016-05-03 10:07:23 UTC
List: ruby-core #75324
Issue #12337 has been updated by Akira Tanaka.


Bignum.coerce(Fixnum) is used to implement fixnum binop bignum.
(binop is binary operator such as +, -, etc.)

x binop y is implemented as follows if x's class doesn't know y's class.

```
u, v = y.coerce(x)
u binop v
```

This is the extension mechanism that we can implement new class
which can be used with numeric objects.
For example, 3 * Vector[4,5] works because Fixnum#* invokes
Vector#coerce which returns [Matrix::Scalar, Vector] and
invokes Matrix::Scalar#* which calculates actual scalar-vector multiplication.

Bignum#coerce is just an instance of this extension mechanism.
If bignum.coerce(fixnum) returns [fixnum, bignum] as you said,
"u binop v" can cause infinite recursion.

This mechanism is cleary useful for classes implemented in Ruby
script such as matrix.rb.
However matz also use it for builtin numeric classes: Fixnum, Bignum and Float.


----------------------------------------
Bug #12337: inconsistency between Fixnum#coerce and Bignum#coerce
https://bugs.ruby-lang.org/issues/12337#change-58454

* Author: Akira Tanaka
* Status: Open
* Priority: Normal
* Assignee: 
* ruby -v: ruby 2.4.0dev (2016-05-01 trunk 54866) [x86_64-linux]
* Backport: 2.1: UNKNOWN, 2.2: UNKNOWN, 2.3: UNKNOWN
----------------------------------------
I found 1.coerce(2.0) is [2.0, 1.0] but
(2**100).coerce(2.0) raises TypeError

```
% ./ruby -ve 'p 1.coerce(2.0)'       
ruby 2.4.0dev (2016-05-01 trunk 54866) [x86_64-linux]
[2.0, 1.0]
% ./ruby -ve 'p (2**100).coerce(2.0)'
ruby 2.4.0dev (2016-05-01 trunk 54866) [x86_64-linux]
-e:1:in `coerce': can't coerce Float to Bignum (TypeError)
	from -e:1:in `<main>'
```

This is a documented behavior.

```
% ri Bignum.coerce|cat
= Bignum.coerce

(from ruby core)
------------------------------------------------------------------------------
  big.coerce(numeric)  ->  array

------------------------------------------------------------------------------

Returns an array with both a numeric and a big represented as Bignum objects.

This is achieved by converting numeric to a Bignum.

A TypeError is raised if the numeric is not a Fixnum or Bignum type.

  (0x3FFFFFFFFFFFFFFF+1).coerce(42)   #=> [42, 4611686018427387904]
```

But I think this is bad bahavior.
Fixnum and Bignum should work seamlessly.

For example, this exposes the platform is 32-bit or 64-bit.
2**40 is Fixnum on 32-bit environment and Bignum on 64-bit environment.
So, (2**40).coerce(2.0) behaves differently: returns an array on 64-bit and
raises TypeError on 32-bit platform.

```
32bit-platform% ./ruby -ve 'p (2**40).coerce(2.0)'
ruby 2.4.0dev (2016-05-01 trunk 54866) [x86_64-linux]
[2.0, 1099511627776.0]

64bit-platform% ./ruby -ve 'p (2**40).coerce(2.0)'
ruby 2.4.0dev (2016-05-01 trunk 54864) [i686-linux]
-e:1:in `coerce': can't coerce Float to Bignum (TypeError)
	from -e:1:in `<main>'
```

I think the behavior of Bignum#coerce should be changed
to match Fixnum#coerce (actually defined at Numeric).


---Files--------------------------------
int-coerce.patch (2.37 KB)


-- 
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:ruby-core-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>

In This Thread

Prev Next