33
29

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-07-09

定期的に「 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 を採用していて計算ができるという事実はとても興味深いです。では、なぜ Ada ではなく COBOL だったのか。そこから何が学べるのか?という過去からの学びを得て、同じ過ちをしないようにすることも大切だと思います。

TL;DR

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

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

です。

問題の再確認

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

上記ブログでも記載されていますが、小数点の取り扱いを BCD (二進化十進表現) として扱うことで、丸め誤差を回避する方法があります。

この 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

コメントをご覧ください

C#

標準に Decimal 型があります
こう見ると COBOL の次は 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 コマンド

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

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

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

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

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 3.6.5
a1 = 0.1
a2 = 0.2
wr = a1 + a2
if 0.3 == wr:
  print("TRUE")
else:
  print("FALSE")

# ==> FALSE

Go

// 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

// 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

// 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るときはこのくらい書いてもらいたいものです。

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

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

EoT

33
29
36

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
33
29

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?