小数点を扱うならBCDを採用している言語が圧倒的

定期的に「 COBOL はひどい」と聞くので「 COBOL はそういう事に使うのではない」という事を記録しておきます。

## Update (8/10)

「COBOLだけではない！」とコメントをいただきまして、旧タイトル「小数点を扱うならCOBOLが圧倒的」から変更し、記事の内容も変更しています。

(夜に追記) C# と bc が加わりました。 ReXX も 0.3 から BCD で処理しているらしいですが、再確認をしておきたいと思います。
このエントリーは **0.1 + 0.2 = 0.3 になる言語を探すのが目的ではありません**

冒頭でも書いたように「COBOL は (モダンなプログラム言語名に比べて) ひどい」とよく言われます。
「FIZZBUZZ も満足に書けないプログラム言語なんて(自主規制)」という事を言われたなら、COBOLの立場から言えば「`0.1 + 0.2 = 0.3` も満足に計算できないプログラム言語なんて」と言いたくもなるでしょう。
要するに **目的外の事に使おうとすると、大変な思いをするから道具は選びましょう** というのが主旨です。

「～を使えばできるよ」「～とすればできるよ」という話もありますが、現行のプログラム言語に **この程度の事に対する解方があるのは当たり前** なのです。
（テクニカル的にはとても面白いんですが！！！）

その点において 1983年産まれの Ada が [BCD を採用していて計算ができる](https://en.wikibooks.org/wiki/Ada_Programming/Types/delta)という事実はとても興味深いです。では、なぜ Ada ではなく COBOL だったのか。そこから何が学べるのか？という過去からの学びを得て、同じ過ちをしないようにすることも大切だと思います。

## TL;DR

COBOL は小数点処理が圧倒的に強い。だから金利などを取り扱う金融系で使われるづけるのです。
小数点処理を軽視する者はプログラムを書くこと無かれ。

BigDecimal 等の外部ライブラリなど使わずプログラム言語の標準仕様/機能のみで `0.1 + 0.2` が `0.3` となるのは ~~COBOL だけです~~ 小数点を含む数値を BCD で扱う言語となります。
今のところ判明しているのは

* COBOL
* [MSX-Basic](#comment-b14f80f5c705643f7f16)
* [Ada](#comment-c82bcf6a6b0a0ce1bfff)
* [C# (標準で decimal 型がある)](#comment-e87c94a88c88dd50a275)
* [bc (もはや言語(ry](#comment-25a8786644180869dc5b)

です。

## 問題の再確認

一般的に言われる少数点を持つ数値を演算すると誤差が生じるケースがあります。
`0.1 + 0.2` が `0.3` ではなく `0.30000000000000004` となる話です。
理由は[http://blog.tsukaby.net/archives/1706](http://blog.tsukaby.net/archives/1706)にまとめられていますのでご一読を。

上記ブログでも記載されていますが、小数点の取り扱いを [BCD (二進化十進表現)](https://ja.wikipedia.org/wiki/%E4%BA%8C%E9%80%B2%E5%8C%96%E5%8D%81%E9%80%B2%E8%A1%A8%E7%8F%BE) として扱うことで、丸め誤差を回避する方法があります。

この BCD を言語仕様として実装していたのが COBOL だけだと思っていたのですが、なんと MSX-Basic や Ada も扱えるという事がわかりました。

## 論よりコード

Ubuntu 18.04/Linux(amd64) 上に各ランタイムをインストールして確認しました
https://paiza.io/ja でも確認することができます（同様の結果でした）

### COBOL

```
# cobc (OpenCOBOL) 1.1.0
        IDENTIFICATION DIVISION.
        PROGRAM-ID. float-checker.
        DATA DIVISION.
        WORKING-STORAGE SECTION.
        01 A1 PIC 9(1)V9(17) VALUE 0.1.
        01 A2 PIC 9(1)V9(17) VALUE 0.2.
        01 WR PIC 9(1)V9(30).
        PROCEDURE DIVISION.
        COMPUTE WR = A1 + A2.
        IF 0.3 = WR THEN
           DISPLAY "TRUE"
        ELSE
           DISPLAY "FALSE"
        END-IF.
        STOP RUN.

# ==> TRUE
```

### Ada

※ コメントから引用しました

```
with Ada.Text_IO; use Ada.Text_IO;
procedure Hogera is
  type F is delta 0.00000000000000001 range 0.0 .. 9.99999999999999999;
  a1 : constant F := 0.1;
  a2 : constant F := 0.2;
  wr : F;
begin
  wr := a1 + a2;
  if F(0.3) = wr then
    Put_Line ("TRUE");
  else
    Put_Line ("FALSE");
  end if;
end Hogera;

-- => TRUE
```

### MSX-Basic

[コメントをご覧ください](#comment-b14f80f5c705643f7f16)

### C# 

標準に Decimal 型があります
こう見ると COBOL の次は C# なのかもしれない。

※ コメントから引用しました

```c#
using System;

public class Hogera
{
    public static void Main()
    {
        const decimal a1 = 0.1m;
        const decimal a2 = 0.2m;
        decimal wr = a1 + a2;
        if (0.3m == wr) {
            System.Console.WriteLine("TRUE");
        } else {
            System.Console.WriteLine("FALSE");
        }
    }
}
```

#### bc コマンド

```c
a1 = 0.1;
a2 = 0.2;
wr = a1 + a2;
if (0.3 == wr) {
    "TRUE";
}
if (0.3 != wr) {
    "FALSE";
}
```

コメントをご覧ください。

### 他のプログラム言語の結果

では他のプログラム言語ではどうなのか、調べてみました。
※ダサいコードなのは COBOL に合わせたからです、ごめんなさい

### Ruby

```ruby
# ruby 2.5.1p57
a1 = 0.1
a2 = 0.2
wr = a1 + a2
if 0.3 == wr
  print "TRUE"
else
  print "FALSE"
end

# ==> FALSE
```

### Python3

```python
# Python 3.6.5
a1 = 0.1
a2 = 0.2
wr = a1 + a2
if 0.3 == wr:
  print("TRUE")
else:
  print("FALSE")

# ==> FALSE
```

### Go

```golang
// go1.10.1
package main
import "fmt"

func main() {
  var a1 float64 = 0.1
  var a2 float64 = 0.2
  var wr float64
  wr = a1 + a2
  if 0.3 == wr {
     fmt.Println("TRUE")
  } else {
     fmt.Println("FALSE")
  }
}

//==> FALSE
```

### Javascript

```javascript
// V8 6.7.288.46
var a1 = 0.1;
var a2 = 0.2;
var wr = a1 + a2;
if (0.3 == wr) {
  console.log("TRUE");
} else {
  console.log("FALSE");
}

//==> FALSE
```

### Java

```java
// openjdk 10.0.1
import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {
        double a1 = 0.1;
        double a2 = 0.2;
        double wr = a1 + a2;
        if (0.3 == wr) {
            System.out.println("TRUE");
        } else {
            System.out.println("FALSE");
        }
     }
}

//==> FALSE
```

## まとめ

要するに FIZZBUZZ 書くプログラム言語じゃないんだよ。

### BigDecimal 使えばいいじゃん

はい、今はそうですね。今はね。

### Rational 使えばいいじゃん

その通りです。使えるものは使いましょう

## あとがき

COBOLコードがsyntax highlightされない悲しみ。

そしてdisるときは[このくらい](http://www.speech-lab.org/~hiroki/csh-whynot.euc)書いてもらいたいものです。

### コメントをいただいて (8/10 追記)

書いてみると知見が集まってありがたい！
そして Ada コードがsyntax highlightされない悲しみ。。。

EoT