walkingmask’s development log

IT系の情報などを適当に書いていきます

MENU

ささやかなC言語の問題(解答編)

昨日出題した,C言語の問題の解答コードと実行結果を載せます.

問題のtwitterアンケート


投票結果は,10が最も多く,ついで9が多いですね.9は最初の投票に釣られたのかな...?12...?

それでは,解答としてソースコードと実行結果を見ていきといと思います.一応気になったので,複数言語で実行してみました.ほぼ一緒のコード内容だと思いますが,C以外は自信がないので修正があればご指摘お願いします.

わかりやすいように,実行回数を同時に出力して,for(i=0; i<10; i++)の場合のコードも一緒に実行しました.ついでに各versionも.
(注記:一部の言語では,同様の実装をするためにwhileを使いました.間違っていればご指摘お願いします.)

環境

OS X El Capitan (version 10.11.2)

C

ソースコード
#include <stdio.h>

int main(){

	printf("0.0 ~ 1.0\n");
	int n=1;
	for(double i=0.0; i<1.0; i+=0.1){
		printf("%d: Happy New Year!\n", n);
		n++;
	}

	printf("0 ~ 10\n");
	n=1;
	for(int j=0; j<10; j+=1){
		printf("%d: Happy New Year!\n", n);
		n++;
	}

	return 0;
}
実行結果
% gcc -v
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 7.0.2 (clang-700.1.81)
Target: x86_64-apple-darwin15.2.0
Thread model: posix
% ./test
0.0 ~ 1.0
1: Happy New Year!
2: Happy New Year!
3: Happy New Year!
4: Happy New Year!
5: Happy New Year!
6: Happy New Year!
7: Happy New Year!
8: Happy New Year!
9: Happy New Year!
10: Happy New Year!
11: Happy New Year!
0 ~ 10
1: Happy New Year!
2: Happy New Year!
3: Happy New Year!
4: Happy New Year!
5: Happy New Year!
6: Happy New Year!
7: Happy New Year!
8: Happy New Year!
9: Happy New Year!
10: Happy New Year!

Java

ソースコード
public class TEST {

	static public void main(String args[]){

		System.out.println("0.0 ~ 1.0");
		double i;
		int n=1;
		for(i=0.0; i<1.0; i+=0.1){
			System.out.println(n+": Happy New Year!");
			n++;
		}

		System.out.println("0 ~ 10");
		int j;
		n=1;
		for(j=0; j<10; j+=1){
			System.out.println(n+": Happy New Year!");
			n++;
		}
	}

}
実行結果
% java -version
java version "1.8.0_65"
Java(TM) SE Runtime Environment (build 1.8.0_65-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.65-b01, mixed mode)
% java TEST    
0.0 ~ 1.0
1: Happy New Year!
2: Happy New Year!
3: Happy New Year!
4: Happy New Year!
5: Happy New Year!
6: Happy New Year!
7: Happy New Year!
8: Happy New Year!
9: Happy New Year!
10: Happy New Year!
11: Happy New Year!
0 ~ 10
1: Happy New Year!
2: Happy New Year!
3: Happy New Year!
4: Happy New Year!
5: Happy New Year!
6: Happy New Year!
7: Happy New Year!
8: Happy New Year!
9: Happy New Year!
10: Happy New Year!

Perl

ソースコード
#!/usr/bin/env perl
use strict;
use warnings;

print "0.0 ~ 1.0\n";
my $n = 1;
for(my $i=0.0; $i<1.0; $i+=0.1, $n++){
	print $n, ": Happy New Year!\n";
}

print "0 ~ 10\n";
$n = 1;
for(my $j=0; $j<10; $j+=1, $n++){
	print $n, ": Happy New Year!\n";
}
実行結果
% perl -v
This is perl 5, version 18, subversion 2 (v5.18.2) built for darwin-thread-multi-2level
(with 2 registered patches, see perl -V for more detail)

Copyright 1987-2013, Larry Wall
(以下省略)
% perl test.pl
0.0 ~ 1.0
1: Happy New Year!
2: Happy New Year!
3: Happy New Year!
4: Happy New Year!
5: Happy New Year!
6: Happy New Year!
7: Happy New Year!
8: Happy New Year!
9: Happy New Year!
10: Happy New Year!
11: Happy New Year!
0 ~ 10
1: Happy New Year!
2: Happy New Year!
3: Happy New Year!
4: Happy New Year!
5: Happy New Year!
6: Happy New Year!
7: Happy New Year!
8: Happy New Year!
9: Happy New Year!
10: Happy New Year!

Python

ソースコード
#!/usr/bin/env python
print "0.0 ~ 1.0"
i = 0.0
n = 1
while i < 1.0:
	print str(n) + ": Happy New Year!"
	i += 0.1
	n += 1

print "0~ 10"
j = 0
n = 1
while j < 10:
	print str(n) + ": Happy New Year!"
	j += 1
	n += 1
実行結果
% python -V
Python 2.7.11
% python test.py 
0.0 ~ 1.0
1: Happy New Year!
2: Happy New Year!
3: Happy New Year!
4: Happy New Year!
5: Happy New Year!
6: Happy New Year!
7: Happy New Year!
8: Happy New Year!
9: Happy New Year!
10: Happy New Year!
11: Happy New Year!
0~ 10
1: Happy New Year!
2: Happy New Year!
3: Happy New Year!
4: Happy New Year!
5: Happy New Year!
6: Happy New Year!
7: Happy New Year!
8: Happy New Year!
9: Happy New Year!
10: Happy New Year!

Ruby

ソースコード
print("0.0 ~ 1.0\n")
i = 0.0
n = 1
while i < 1.0 do
	print(n, ": Happy New Year!\n")
	i += 0.1
	n += 1
end

print("0 ~ 10\n")
j = 0
n = 1
while j < 10 do
	print(n, ": Happy New Year!\n")
	j += 1
	n += 1
end
実行結果
% ruby -v
ruby 2.0.0p645 (2015-04-13 revision 50299) [universal.x86_64-darwin15]
% ruby test.rb 
0.0 ~ 1.0
1: Happy New Year!
2: Happy New Year!
3: Happy New Year!
4: Happy New Year!
5: Happy New Year!
6: Happy New Year!
7: Happy New Year!
8: Happy New Year!
9: Happy New Year!
10: Happy New Year!
11: Happy New Year!
0 ~ 10
1: Happy New Year!
2: Happy New Year!
3: Happy New Year!
4: Happy New Year!
5: Happy New Year!
6: Happy New Year!
7: Happy New Year!
8: Happy New Year!
9: Happy New Year!
10: Happy New Year!

Shell Script

ソースコード
#!/bin/sh
echo "0.0 ~ 1.0"
i=0.0
n=1
while [ `echo "$i < 1.0" | bc` -eq 1 ]
do
	echo "$n: Happy New Year!"
	i=$(echo "$i + 0.1" | bc)
	n=$((n + 1))
done

echo "0 ~ 10"
j=0
n=1
while [ `echo "$j < 10" | bc` -eq 1 ]
do
	echo "$n: Happy New Year!"
	j=$(echo "$j + 1" | bc)
	n=$((n + 1))
done

#echo $((0.0+0.1))
#0.10000000000000001
実行結果
% sh test.sh
0.0 ~ 1.0
1: Happy New Year!
2: Happy New Year!
3: Happy New Year!
4: Happy New Year!
5: Happy New Year!
6: Happy New Year!
7: Happy New Year!
8: Happy New Year!
9: Happy New Year!
10: Happy New Year!
0 ~ 10
1: Happy New Year!
2: Happy New Year!
3: Happy New Year!
4: Happy New Year!
5: Happy New Year!
6: Happy New Year!
7: Happy New Year!
8: Happy New Year!
9: Happy New Year!
10: Happy New Year!

Swift

ソースコード
print("0.0 ~ 1.0")
var i = 0.0
var n = 1
for(i=0.0; i<1.0; i+=0.1){
    print(n, "Happy New Year!")
    n++
}

print("0 ~ 10")
var j = 0
n = 1
for(j=0; j<10; j+=1){
    print(n, "Happy New Year!")
    n++
}
実行結果
% swift -version  
Apple Swift version 2.1.1 (swiftlang-700.1.101.15 clang-700.1.81)
Target: x86_64-apple-darwin15.2.0
% swift test.swift 
0.0 ~ 1.0
1 Happy New Year!
2 Happy New Year!
3 Happy New Year!
4 Happy New Year!
5 Happy New Year!
6 Happy New Year!
7 Happy New Year!
8 Happy New Year!
9 Happy New Year!
10 Happy New Year!
11 Happy New Year!
0 ~ 10
1 Happy New Year!
2 Happy New Year!
3 Happy New Year!
4 Happy New Year!
5 Happy New Year!
6 Happy New Year!
7 Happy New Year!
8 Happy New Year!
9 Happy New Year!
10 Happy New Year!

解説

ということで,ほとんどの言語で"Happy New Year"が11回出力される結果となりました.なぜこうなるのでしょうか?

それは,コンピュータの内部では演算が2進数で行われているせいなようです.小数を2進数で表現しようとすると

2^-1 = 0.5
2^-2=0.25
2^-3=0.125
...

といったように,2の負の指数で表現されるわけですが,これを,いくら足しあわせても0.1ぴったりにはならいのです(コンピュータ内部では).だから,コンピュータの内部では0.1は

0.1 = 0.099999...

のような値となっていて,これを10回足しても0.9999...にしかならず,上記のfor条件では終了しない,といったトリックのようです.「小数 プログラミング」などで調べると,より丁寧でわかりやすい解説があると思うので,気になる方は調べてみてください.

まとめ

友人と,課題用のPerlのscriptで,0.0から0.9までforで実行した結果を見て,驚いたので調べてみんなにも知ってもらおうとアンケートをとったのですが,知らない人が多かったようで安心(?)しました.正解した人は素晴らしいですね!

あまり小数をfor条件に使うということも少ないとは思うのですが,とはいえ,今後そのような機会があった時に,知らないで大変なミスをしてもいけないので,この機会に学べて良かったと思います.

最後に,先日リリースされたPerl 6では,10回で終了したみたいです.Shell Scriptは,10回で終了してますが,コメントアウトしてある部分でわかるように,0.1=0.1000000001みたいな実装になっているおかげなようですね.これは言語の仕様に依るようです.

アンケートに答えてくれた方,記事を読んでくれた方,みなさんありがとうございました!

(2015/12/29 22:55)

追記

whileを使ってることの注記を追加しました.

(2015/12/29 23:05)