ささやかなC言語の問題(解答編)
昨日出題した,C言語の問題の解答コードと実行結果を載せます.
問題のtwitterアンケート
for(double i=0.0; i<1.0; i+=0.1){
printf("Happy new year!\n");
}
この"Happy new year"は何回表示されるでしょうか?
#アンケート #拡散希望
— あるマス (@walkingmask) 2015, 12月 28
投票結果は,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)