JavaScriptのfloatで正確な比較を行う

TLにこんなツイートが回ってきました

<blockquote class="twitter-tweet" data-partner="tweetdeck"><p lang="ja" dir="ltr">正確な実数計算をやらされるJavaScriptくん <a href="https://twitter.com/hashtag/%E6%93%AC%E7%AB%9C%E6%88%AF%E7%94%BB?src=hash&amp;ref_src=twsrc%5Etfw">#擬竜戯画</a> <a href="https://t.co/ipE56C2YbV">pic.twitter.com/ipE56C2YbV</a></p>&mdash; RAO(らお) (@RIORAO) <a href="https://twitter.com/RIORAO/status/923599110262874112?ref_src=twsrc%5Etfw">October 26, 2017</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

Yep. JavaScriptに限らず浮動小数点数の丸め誤差の問題はあります。
しかし、仕様通りですが、便利かと言えば便利ではありませんね？

## こんにちはAST
こんなときは、ASTで変換してやればいいです。
Babelで仕様とか色々大切なものを歪めてあげましょう。

[akameco/babel-plugin-float-equal: Babel plugin for float equal](https://github.com/akameco/babel-plugin-float-equal)

```
$ npm install --save-dev babel-plugin-float-equal 
```

```.babelrc
{
  plugins: ["float-equal"]
}
```

```js
// before
0.1 + 0.2 === 0.3
// => false


// after
0.1 + 0.2 === 0.3
// => true
```

Babelを使って、`===`で比較されていた場合、問答無用で以下のコードへ変換してやります。
Number.EPSILONは`2.220446049250313e-16`です。
いわゆる許容する誤差です。

```js
0.1 + 0.2 === 0.3

      ↓ ↓ ↓ ↓ ↓ ↓

typeof (0.1 + 0.2) === 'number' && typeof 0.3 === 'number'
  ? Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON
  : 0.1 + 0.2 === 0.3
```

## 変数
もちろん変数に対応してなくては使い道がないですね。

```js
var a = 0.1
var b = 0.2
a + b === 0.3
// => true
```

## How to create Babel Plugin?

以下がプラグインの全コードです。
`BinaryExpression`のときのオペレーターが`===`のときに、左右のnodeをtemplate関数を呼んで変換したものをpath.replaceWithを使って入れ替えてあげます。
単純ですね。

```js
export default ({ template } /*: {template: Function} */) => {
  const builder = template(`
  (typeof LEFT === 'number' && typeof RIGHT === 'number') ? Math.abs(LEFT - RIGHT) < Number.EPSILON : LEFT === RIGHT
  `)

  return {
    name: 'float-equal',
    visitor: {
      BinaryExpression(path /*: Object */) {
        if (path.get('operator').node !== '===') {
          return
        }

        const { node: left } = path.get('left')
        const { node: right } = path.get('right')

        const ast = builder({ LEFT: left, RIGHT: right })

        path.replaceWith(ast)
        path.skip()
      }
    }
  }
}
```

## おわりに
### プロダクションで使ってもいいですか？
いいえ、プロダクションで使ってはいけません。
まあ、危険を犯すのが好きならば、どうぞご自由に。

[akameco/babel-plugin-float-equal: Babel plugin for float equal](https://github.com/akameco/babel-plugin-float-equal)

<blockquote class="twitter-tweet" data-partner="tweetdeck"><p lang="ja" dir="ltr">君のJavaScriptはそうなのか。僕のJavaScriptは計算出来るのだけど <a href="https://t.co/XSvTE4h8tu">https://t.co/XSvTE4h8tu</a> <a href="https://t.co/6V5HskHExj">pic.twitter.com/6V5HskHExj</a></p>&mdash; あかめ@無職.js (@akameco) <a href="https://twitter.com/akameco/status/924619407686385665?ref_src=twsrc%5Etfw">October 29, 2017</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-partner="tweetdeck"><p lang="ja" dir="ltr">インチキ臭くて草</p>&mdash; wreulicke (@wreulicke) <a href="https://twitter.com/wreulicke/status/924619578704932864?ref_src=twsrc%5Etfw">October 29, 2017</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>