ruby の数値の等値

# これは何？

https://qiita.com/scivola/items/8db8f88c9bb0ddcf41cd#comment-c26bb656a534b70ea86a
の続き。

# 等しいということ

たとえば float が普通の単精度、double が普通の倍精度である C言語（float が単精度で計算される環境の場合）で。

```C:C
double d1 = 1.1;
float f1 = 1.1f;
puts( d1 == f1 ? "true" : "false" ); // A

double d2 = 99999999.0;
float f2 = 99999999.0f;
int32_t i2 = 99999999;
puts( d2==f2 ? "true" : "false" ); // B
puts( f2==i2 ? "true" : "false" ); // C
puts( i2==d2 ? "true" : "false" ); // D
```

とした場合。

A は、両辺 `double` に変換される。
`d1` は 1.10000000000000009 ぐらい。
`f1` は 1.10000002 ぐらいで、 `double` に変換すると 1.10000002384185791 ぐらいになるので等しくない。

B も、両辺 `double` に変換される。
`d2` は、99999999.0 になるが、
`f2` は、単精度では 99999999 を表現できないので、 100000000.0 になる。
なので、等しくない。

C は、両辺 `float` に変換されての比較になる。
`i2` は 99999999 だが、比較に当たり `float` に変換されて 100000000.0 になる。
なので、等しい。

D は、両辺 `double` に変換されての比較になる。
`i2` は 99999999 で、比較に当たり `double` に変換される。 `double` なので桁落ちせず、 99999999.0 になる。
なので、等しい。

見てのとおり、推移律が成立していない。
しかし。C や Java の場合、同じ `==` 演算子で、何と何を比較するのかが動的に決まることはない（ないよね？）ので、推移律が成立しないような気分にはあまりならないと思う。

で。

ruby の場合はどうかというのがこの記事の主題。

# ruby の場合

ruby にもいくつかの数値型がある

|名前|内部|特徴|
|:--|:--|:--|
|Integer|略|整数ならメモリの許す限り何でもOK|
|Float|浮動小数点数の2進数|2進数なので、 0.1 のような値は正確に表現できない|
|Rational|Integer 2個|四則演算している限り誤差なし|
|BigDecimal|10進数(のような感じ)|10進数なので、1÷3 の値は正確に表現できない|

## Integer と Float の比較

想像より難しいことがわかった。

```ruby:ruby3.0
p 1.1==1 #=> false
p 1.0==1 #=> true
p 1==1.0.next_float #=> false
p 1==1.0.prev_float #=> false
```

このあたりを見ると、ああ Float にしてから比較してるのかなと思ったんだけど

```ruby:ruby3.0
s="7"*17
f = s.to_f
i = s.to_i
p [ f, i ] #=> [7.777777777777778e+16, 77777777777777777]
p [f==i, f.to_f==i.to_f, f-i==0, (f-i).class]
#=> [false, true, true, Float]
p f.to_f==i.to_f #=> true
```

`f==i` と `f.to_f == i.to_f` の結果が異なるのでそうでもない。
何が起こっているのかわからない。

## Float と Rational の比較

```ruby:ruby3.0
p 1.1==1r #=> false
p 1.0==1r #=> true
p 1.0==1.000000000000001r #=> false
p 1.0==1.0000000000000001r #=> true
s="7"*17
f = s.to_f
r = s.to_r
p [ f, r ] #=> [7.777777777777778e+16, (77777777777777777/1)]
p [f==r, f.to_f==r.to_f, f-r==0, (f-r).class]
#=> [true, true, true, Float]
p f.to_f==r.to_f #=> true
```

Float にしてから比較しているとしか言いようがない結果。

Float より Rational のほうが表現できる値が広いので、わりと予期せぬ結果になりがちだと思う。

## Float と BigDecimal の比較

こちらは BigDecimal に変換してから比較している感じ。

```ruby:rby3.0
p 1.1==BigDecimal("1") #=> false
p 1.0==BigDecimal("1") #=> true
p 1.0==BigDecimal("1.000000000000001") #=> false
p 1.0==BigDecimal("1."+"0"*200+"1") #=> false
p 1.0.next_float==BigDecimal("1")#=> true
p 1.0.prev_float==BigDecimal("1")#=> false
```

`p 1.0.next_float==BigDecimal("1")#=> true` が怪しげだが、これは `coerce` の実装がそうなっているから。

```ruby:ruby3.0
p BigDecimal("1").coerce( 1.0.next_float ) #=> [0.1e1, 0.1e1]
p BigDecimal("1").coerce( 1.0.prev_float ) #=> [0.9999999999999999e0, 0.1e1]
```

そうなってしまうのはなぜかというと、たぶん、 `bigdecimal.h` で 
`#define BIGDECIMAL_DOUBLE_FIGURES (1+DBL_DIG)`
としているのが原因で
`#define BIGDECIMAL_DOUBLE_FIGURES (2+DBL_DIG)`
とすれば治るかも、と思っているけど、思っているだけで確かめてはいない。

## Rational と BigDecimal の比較

こちらも BigDecimal に coerce しているけど、Float とはちょっと振る舞いがちがう。

```ruby:ruby3.0
p 1.1r==BigDecimal("1") #=> false
p 1.0r==BigDecimal("1") #=> true
p 1.0r==BigDecimal("1.000000000000001") #=> false
p 1.0r==BigDecimal("1."+"0"*200+"1") #=> false
p 1.0.next_float.to_r==BigDecimal("1")#=> true
p 1.0.prev_float.to_r==BigDecimal("1")#=> true
```

`next_float` も `prev_float` も比較結果が `true` だが、これもそういう `coerce` だから。

```ruby:ruby3.0
p BigDecimal("1").coerce( 1.0.next_float.to_r ) #=> [0.1e1, 0.1e1]
p BigDecimal("1").coerce( 1.0.prev_float.to_r ) #=> [0.1e1, 0.1e1]
```

ちなみに。 `coerce` で有理数を 10進数にまるめる桁数は、レシーバの桁数で決まるので

```ruby:ruby3.0
s="1.00000000001000000000010000000000100000000001000000000010000000000100000000001"
BigDecimal(s[0,10])==s.to_r # => true
BigDecimal(s[0,20])==s.to_r # => false
BigDecimal(s[0,50])==s.to_r # => true
BigDecimal(s[0,60])==s.to_r # => false
```
と、奇妙なことが起こる。

## 様々な型で等値比較

`BigDecimal(a)==Float(a)` みたいなことを、 `a` を色々変えて試してみた。

全部 **TRUE** になっていないということは、推移律が成立していないということで、わりと気持ち悪いことではある。

### `"77777777777777777"`
||Integer|Float|Rational|BigDecimal|
|:--|:--|:--|:--|:--|
|Integer|**TRUE**|false|**TRUE**|**TRUE**|
|Float|false|**TRUE**|**TRUE**|false|
|Rational|**TRUE**|**TRUE**|**TRUE**|**TRUE**|
|BigDecimal|**TRUE**|false|**TRUE**|**TRUE**|

前述のとおり、 `Integer` と `Float` の部分が謎。なんだろう。

### `"100..001"` (400桁)
||Integer|Float|Rational|BigDecimal|
|:--|:--|:--|:--|:--|
|Integer|**TRUE**|false|**TRUE**|**TRUE**|
|Float|false|**TRUE**|**TRUE**|false|
|Rational|**TRUE**|**TRUE**|**TRUE**|**TRUE**|
|BigDecimal|**TRUE**|false|**TRUE**|**TRUE**|

`Float` は無限大になる。
にもかかわらず、 `Float(略)==Rational(略)` が `true` になるのは、右辺も `Float` に変換された結果無限大になってしまうから。

### `"1.0000000000000002"`

この値は `1.0.next_float.to_s` 。

||Float|Rational|BigDecimal|
|:--|:--|:--|:--|
|Float|**TRUE**|**TRUE**|false|
|Rational|**TRUE**|**TRUE**|**TRUE**|
|BigDecimal|false|**TRUE**|**TRUE**|

次の `"0.9999999999999999"` と結果が違うところがおそろしい。

### `"0.9999999999999999"`

この値は `1.0.prev_float.to_s` 。

||Float|Rational|BigDecimal|
|:--|:--|:--|:--|
|Float|**TRUE**|false|**TRUE**|
|Rational|false|**TRUE**|**TRUE**|
|BigDecimal|**TRUE**|**TRUE**|**TRUE**|

ちなみに、

```ruby:ruby3.0
0.9999999999999999==0.9999999999999999.to_r
#=> true
0.9999999999999999==0.9999999999999999r
#=> false
```
である。


### `"1e-400"`
||Float|Rational|BigDecimal|
|:--|:--|:--|:--|
|Float|**TRUE**|**TRUE**|false|
|Rational|**TRUE**|**TRUE**|**TRUE**|
|BigDecimal|false|**TRUE**|**TRUE**|

`Float("1e-400")` は `0.0` なので、わかりやすく書くと下記のような感じ。

```ruby:ruby3.0
r=Rational("1e-400");[r==0.0, r==0]
#=> [true, false]
```

難しい。