## Bug #5179

closed### Complex#rationalize and to_r with approximate zeros

**Description**

Currently, Complex#rationalize and Complex#to_r raise a RangeError if the imaginary part is nonzero *or is a Float*. Note that a BigDecimal(0) is accepted, though:

```
Complex(1, 0).to_r # => Rational(1,1)
Complex(1, BigDecimal("0.0")).to_r # => Rational(1,1)
Complex(1, 0.0).to_r # => RangeError
```

This is inconsistent. I recommend not raising an error for 0.0 (Float or BigDecimal). Any objection?

#### Updated by mrkn (Kenta Murata) almost 13 years ago

**Assignee**set to*mrkn (Kenta Murata)*

0.0 doesn't exactly represent zero. It may be 0.0+-10.0**(Float::MIN_10_EXP-17).

BigDecimal(0) doesn't exactly represent zero, too.

I believe this issue should be resolved by introducing Numeric#exact? and/or Numeric#inexact? methods.

#### Updated by shyouhei (Shyouhei Urabe) over 12 years ago

**Status**changed from*Open*to*Assigned*

#### Updated by jeremyevans0 (Jeremy Evans) over 4 years ago

As Numeric#exact? has been rejected, and BigDecimal is not a core class, I'm not sure what to do about this issue. @mrkn (Kenta Murata) seems to recommend RangeError for `Complex(1, BigDecimal("0.0")).to_r`

(the same as with `Complex(1, 0.0).to_r `

). I think the bigdecimal library would have to override Kernel#Complex for that behavior. Would that be considered acceptable?

#### Updated by mame (Yusuke Endoh) over 4 years ago

We discussed #5321 at the dev-meeting. Whether a value is exact or inexact, is not decidable based on a class. For example, a literal `0.0`

may be considered as precise and exact. But, if the same value is gained from an inaccurate calculation, it may be considered as inexact.

In regard to this paricular issue, Naruse pointed out that we already have `Float#to_r`

. So I think that it is reasonable that `Complex(1, 0.0).to_r`

returns `Rational(1, 1)`

instead of RangeError.

#### Updated by kjtsanaktsidis (KJ Tsanaktsidis) about 1 year ago

I've explored the behaviour here a bit, and I think I do believe that `0.0`

really does represent the concept of "zero". Specifically, it's the number that satisfies the following properties, for all possible non-NaN floats N:

`abs(0.0 * N) == 0.0`

`0.0 + N == N`

You might obtain a bit pattern for 0.0 from the result of a calculation which might have otherwise produced a non-zero result if there was more precision; however that doesn't change the fact that the bit pattern you have really does satisfy the mathematical properties of zero.

Additionally, I think the distinction that complex.c currently draws between floating-point and non-floating-point zero also results in some other surprising results:

```
irb(main):030:0> Complex(3, 7) ** 0.0
=> (1.0+0.0i)
irb(main):031:0> Complex(3, 7) ** 0
=> (1+0i)
irb(main):032:0> Complex(3.2, 7) ** 0
=> (1+0i)
irb(main):033:0> Complex(3.2, 7) ** 0.0
=> (1.0+0.0i)
```

Why does the type of `A ** B`

depend on whether B is a float or not, but not A?

```
irb(main):034:0> Complex(3.2, 7) ** Complex(1, 0.0)
=> (3.200000000000001+6.999999999999999i)
irb(main):035:0> Complex(3.2, 7) ** Complex(1, 0)
=> (3.2+7i)
```

This doesn't *need* to accumulate floating point error - the code in `rb_complex_pow`

special-cases `(a + bi) ** (c + 0i) -->(a + bi) ** c`

, but the special-casing is skipped if `0i`

is `0.0i`

instead.

Also, `#to_i`

and `#to_f`

have the same issue as `#to_r`

:

```
irb(main):037:0> Complex(3, 0.0).to_i
(irb):37:in `to_i': can't convert 3+0.0i into Integer (RangeError)
irb(main):038:0> Complex(3, 0.0).to_f
(irb):38:in `to_f': can't convert 3+0.0i into Float (RangeError)
```

This is especially odd for `#to_i`

because it of course has no problems truncating away the real part:

```
irb(main):040:0> Complex(3.1, 0).to_i
=> 3
```

I think we should change all the checks for `k_exact_zero_p`

in `complex.c`

to `f_zero_p`

; i.e. in all the places where `complex.c`

special-cases integer zero, also make it special-case floating-point zero. If people agree with this (especially people who actually use Complex in their code - I have pretty much never used it!) I can send a pretty simple patch to cloe this out.

#### Updated by mrkn (Kenta Murata) 6 months ago

`Complex(re, im.to_r).to_r`

looks good to me if `im`

is not `0`

or `0r`

. `Complex(re, im.to_i).to_i`

is also OK for me in the same manner.

`Complex(re, im.to_f).to_f`

cannot resolve the problem the current `to_f`

holds. I don't have other ideas to resolve this right now.

I don't think we can discuss `**`

along with `to_r`

because `**`

is not type conversion.

#### Updated by mrkn (Kenta Murata) 6 months ago

I'm working on to_r at https://github.com/ruby/ruby/pull/9581.

I will do for other methods later.