roundé¢æ°ãã®6:åèã¨ãè©«ã³
Refererä¸è¦§ã®åå¨ã«æ°ã¥ããã®ã§ããªã³ã¯ãã¦ä¸ãã£ã¦ããæ¹ã ã®æç« ãä»æ´èªãã§ãã¾ãããã ãããããæµ®åå°æ°ç¹æ°ã®æ±ããæãã®ãroundé¢æ°ã®ããã«ãã¦ãããããªæç« ãè¦ã¤ãã¾ããããããããã誤解ãããã°é常ã«æ®å¿µã ã¨æãã¨åæã«ãããã ã注ç®ãéããããã«ã¯æµ®åå°æ°ç¹æ°ä¸è¬ã®ç½ ã«ã¤ãã¦ç°¡åã«ç´¹ä»ãã¦ã¿ã¦ãè¯ãããªãã¨æãã¤ãã¾ãããæµ®åå°æ°ç¹æ°ã¾ããã§ããã£ã人ãããã«æ¤ç´¢ã§ãã£ã¦ãããã¨ãããããããã¾ããã
ãããªããã§ãä»åã®ãã¼ãã¯åèã§ããæµ®åå°æ°ç¹æ°ã®ä¸æè°ãã«ã¤ãã¦ç°¡åãªç´¹ä»è¨äºãæ¸ãã¦ã¿ã¾ãããã¾ãã第1åè¨äºä¸ã®èª¤ã£ã¦ããç¹ã«ã¤ãã¦ã1ç¹è¨æ£ããã¾ããä»åã¯ã¤ã¡ã¼ã¸å è¡ã§æ¸ãã¦ã¿ã¾ããããä¼ãã¦ããªããã¨ã¯ãã£ã¦ãåã¯æ¸ãã¦ããªãã¤ããã§ããæ£ç¢ºãªå 容ãç¥ããããªã£ãæ¹ã¯Wikipediaã®è¨äºã浮動小数点数ããèªãã¨è¯ãã§ããããã¾ãã¯ããããããªã³ã¯ããã¦ããã浮動小数点演算についてããèªãã°å®ç§ãªç¥èãæã«å ¥ããããã¨æãã¾ã*1ã
以ä¸ããã®è¨äºä¸ã§ã¯IEEE64bitæµ®åå°æ°ç¹æ°ã®ãã¨ãåã«æµ®åå°æ°ç¹æ°ã¨æ¸ããã¨ã«ãã¾ããããã¯ãCã§è¨ãdoubleåã®ãã¨ã§ããã¾ããPHPãPerlãRubyãPythonã§åã«å°æ°ç¹ãå«ãã æ°ãæ¸ããå ´åã«ã¯IEEE64bitæµ®åå°æ°ç¹æ°ã¨ãã¦æ±ããã¾ãã
ãã¦ãæµ®åå°æ°ç¹æ°ã®æ£ä½ããã£ããè¨ãã¨ãã大ããæ°ãå°ããæ°ã表ãããæå¹æ°åã10é²16æ¡ç¨åº¦ã®æ°ãã¨ãã£ãã¨ããã§ããåããç¥ã£ã¦ããæ®æ®µã®å°æ°ã¨éãç¹ã¯ä¸è¨ã®2ç¹ã§ãã
- ä¸èº«ã2é²æ°ã§ãã
- 表ç¾ã§ããæ¡æ°ã決ã¾ã£ã¦ãã
ãããªãã¨ããã£ã¦ãããã¨æã人ãå¤ããã¨æãã¾ããã©ãæ¬å½ã«ããã§ãããããæµ®åå°æ°ç¹æ°ã®ä¸çã§ã0.1ã10å足ãã¦ããããã1ã«ãªããªããã¨ããã®ã¯æç§æ¸ã«ãæ¸ãã¦ãããããªãã¨ã§ããã©ããã®äºå®ãããããããããªããã©èª¤å·®ãå ¥ãããæããã ãªãããã使ããªãããã«ããããã¨ãããããã®èªèã®äººãæ¡å¤å¤ãã®ã§ã¯ãªãã§ãããã*2
0.1ã10å足ã話ãããå°ã詳ããè¦ã¦ã¿ã¾ããããã¨ããããPHPã§å®é¨ãã¦ã¿ã¾ãã
$ php -r '$x=0.1; $y=0; for($i=0; $i<10; $i++){$y+=$x;} printf("%.19f\n", $y);' 0.9999999999999998890 $ php -r '$x=0.1; $y=$x*10; printf("%.19f\n", $y);' 1.0000000000000000000
0.1ã10å足ãã¨1ããå°ãã ãå°ããæ°ã«ã0.1ã10åããã¨ã¡ããã©1.0ã«ãªãã¾ããã10å足ãã®ã¨10åããã®ã¨ã§çµæãéãã®ã¯æ®æ®µã®å¸¸èã§èããã¨ä¸æè°ã§ãããããã¯PHPãå¤ãªããã§ã¯ããã¾ããï¼æ¬ç¨¿æå¾ã®ãã¾ãåç §ã®ãã¨ï¼ãæµ®åå°æ°ç¹æ°ã®ä¸çã§ã¯æ®æ®µé¤ã£ã¦ããç´æãæãç«ããªãã ãã®ãã¨ã§ãã
0.1ãæµ®åå°æ°ç¹æ°ã§ããã¿ãªè¡¨ããªããã¨ãããã¨ã¯ãåç¥ã®æ¹ãå¤ãã¨æãã¾ããæµ®åå°æ°ç¹æ°ã§ããã¿ãªè¡¨ããªãæ°ã¯ãä¸çªè¿ãæ°ã§è¡¨ç¾ããã¾ããã³ã³ãã¥ã¼ã¿ä¸ã®è¡¨ç¾ã«ãªãã¨åæã«èª¤å·®ãå ¥ãããã§ãã
2é²æ°ã¯ãããã«ããã®ã§ã10é²æ°ã§ä¾ãæãã¦ã¿ã¾ãããã¨ã話ã«ãªã£ã¦ãã¾ãã¾ããã©ãããããããã®ããã«ææ ¢ãã¦ãã ããã10é²ã§3æ¡ã¾ã§ãã表ããªãä¸çãèãã¦ã¿ã¾ãããã®ä¸çã§1/3ã6å足ãã¦ã¿ã¾ãããããã®ä¸çã§ã¯1/3ã¯ããã¿ãªè¡¨ç¾ã§ããã0.333ã1/3ã«ä¸çªè¿ãæ°ã§ãããã®ä¸çã®è¨ç®ã«ã¼ã«ã¨ãã¦ãè¨ç®ã®çµæã3æ¡ãè¶ ããå ´åã«ã¯ã4æ¡ç®ãåæ¨äºå ¥ãã¾ãã
- 0.333+0.333=0.666
- 0.666+0.333=0.999
- 0.999+0.333=1.332â1.33
- 1.33+0.333=1.663â1.66
- 1.66+0.333=1.993â1.99
1/3ã6å足ãã¨1.99ã«ãªãã¾ãããã§ã¯ã6åãããã©ããªãã§ããããã
- 0.333*6=1.998â2.00
6å足ãã®ã¨6åããã®ã¨ã§ã¯ç°ãªãçµæã«ãªãã¾ãããåå ã¯2ç¹ã1/3ã10é²æ°ã§ããã¿ãªè¡¨ããªããã¨ã¨ãæ¡æ°ã«å¶éããããã¨ã§ããæµ®åå°æ°ç¹æ°ã§ãä¼¼ããã¨ãèµ·ããã¾ããããã0.1ã10å足ãã®ã¨10åããã®ã¨ã§çµæãéãçå±ã§ãã
ããã§æ³¨ç®ãã¦æ¬²ããã®ã¯ã1/3ãããã¿ãªè¡¨ããªãä¸çãªã®ã«ã1/3ã6åããçµæã¯ããã¿ãªã«ãªã£ã¦ãããã¨ã§ãããªããããªãã¨ãèµ·ãããã¨ããã¨ã1/3ããã®ä¸çã§ããã¿ãªè¡¨ããªãã£ãã®ã¨åæ§ã0.333ã6åããæ°ããã®ä¸çã§ã¯ããã¿ãªè¡¨ç¾ã§ããªãããã§ããç¾è±¡ã¨ãã¦è¦ãã¨ã1/3ã0.333ã«ããã¨ãã«çã¾ãã誤差ãã6åããã¨æ¶ãã¦ãã¾ããæµ®åå°æ°ç¹æ°ã§0.1ã10åããå ´åãåæ§ã§ããããã®ããã«ã誤差ã¯èç©ããå¯è½æ§ãããã¾ãããæã¡æ¶ãåããã¨ãããã¾ãããããæ®æ®µã®ç´æã¨ã¯ç°ãªãç¹ã§ã¯ãªãã§ããããã
ãã¦ãããã§æåã®è¨äºãæãåºãã¦ã¿ã¾ãããã
ãã®ä¸é£ã®ãã°å ±åãæãèªã¿ã§è¦ç´ããã¨ããç´ã¨ãã³ã§è¨ç®ããã¨5.045ã«ãªãã¯ãã®å¤ï¼å®éã«ã¯ã³ã³ãã¥ã¼ã¿ä¸ã§ã¯ç´5.04499999999999992894573ï¼ãå°æ°ç¹ä»¥ä¸ç¬¬äºä½ã¾ã§ã§åæ¨äºå ¥ãã¦ãã®ã«ãªãã5.04ã«ãªã£ãï¼ãã°ã ï¼ãã£ã¦é¨ãã§ããããã°ã©ãããã°å ±åããã¦ãã¦ãããã«å¯¾å¦ããããã«åæ¨äºå ¥ã®å¢çå¤ä»è¿ï¼0.00000000001ãããã®å·®ï¼ã ã£ããå ¨é¨0ããé ãæ¹ã«åãä¸ãããããªã³ã¼ãä¿®æ£ããããã¨ãããã¨ãã¨æãã¾ããä»ã®è¨èªãªãç¡ç¥ãªãã°å ±åè ãç½µåãã¦çµããã¯ãã®ã¨ãããããã°ä¿®æ£ã¨ãã¦å¯¾å¿ãã¦ãã¾ãã¨ãããPHPãããã®ããããã¾ããã
hnwの日記 - PHPの奇妙なround関数
æµ®åå°æ°ç¹æ°ã®æåãç´æã«åããªããã¨ãçµé¨ãã¦ããããã°ã©ãã»ã©ããã®èª¬æã§ããã°å ±åè ã®åéãã§ããã°ã§ã¯ãªããã ãªãã¨ãã風ã«ç´å¾ãã¦ãã¾ããããªæ°ããã¾ããããããèªåã§æ¸ãã¦ããã¦éåãªè©±ã§ããã©ãå®ã¯ããã§ã¯å ¨ç¶èª¬æã«ãªã£ã¦ãã¾ããã
roundã®å®è£ ãã©ããªã£ã¦ãããã¨ããã¨ãå°æ°ç¹ä»¥ä¸ç¬¬2ä½ã¾ã§ã«ä¸¸ããå ´åãå¼æ°ã100åãã¦0.5ã足ãã¦floorï¼å°æ°ç¹ä»¥ä¸åãæ¨ã¦ï¼ããã¦100ã§å²ããã¨ãããã¨ã§ãããã¨ããã§ã0.1ã10åããã¨èª¤å·®ãå°ãããªãä¾ãä»è¦ãã°ããã§ãããæµ®åå°æ°ç¹æ°ã®ä¸çã¯æ®æ®µã®10é²å°æ°ã®å¸¸èã¯éç¨ããªãããã§ãããã5.0449999â¦ã100åãã¦0.5ã足ãã¦ã¿ãå¿ è¦ãããããã§ããããã§ã¯å®é¨ãã¦ã¿ã¾ãããã
$ php -r '$x=5.045; printf("%.19f\n", $x);' 5.0449999999999999289 $ php -r '$x=5.045; $y=$x*100; printf("%.19f\n", $y);' 504.5000000000000000000 $ php -r '$x=5.045; $y=$x*100+0.5; printf("%.19f\n", $y);' 505.0000000000000000000 $ php -r '$x=5.045; $y=($x*100+0.5)/100; printf("%.19f\n", $y);' 5.0499999999999998224
ãããããããï¼round(5.045,2)ã¯5.05ã«ãªãã®ãæ£ãããã ï¼ããããã°å ±åè ã®æ¹ãæ£ãããï¼*3
ç¡ç¥ãªãã°å ±åè ã¨ãæ¸ãã¡ãã£ã¦æ¬å½ã«ããããªãããåçãã¦ã¾ããè¨ã訳ã«ãªãã¾ããã©ãæ¹ãã¦æµ®åå°æ°ç¹æ°ã£ã¦ç´æãæãç«ããªãä¸çã§ãããã
ãããªããã§ç¬¬1åè¨äºãè¨æ£ãã¦ããã¨ãPHPã¯ç¡ç¥ãªã¯ã¬ã¼ãã¼ã¸ã®å¯¾çã¨ãã¦ãã°ä¿®æ£ãããããã§ã¯ããã¾ããããã®ãã°å ±åãããæåã®åå ã¯3åç®ã®è¨äºãPHPのround関数の謎が少し解けたãã§è°è«ãããx86ç³»CPUã®æµ®åå°æ°ç¹æ°ã¬ã¸ã¹ã¿ã80bitãããã¨ããããè¨ç®çµæã®å·®ã§ããããããã¼ã¿ããªãã£ã«é¢ãããã°ã¨ã¿ãªãã¦ä¿®æ£ãããã¨èªä½ã¯ç¹ã«åé¡ãããã¾ããããã¨ã¯ä¿®æ£èªä½ãæ£ãããã©ããã®åé¡ã§ãã
å½åã¯åèªèº«ããã®è¾ºããç解ãã¦ããªãã£ãã«ããããããã3åç®ã®è¨äºãæ¸ãé ã«ã¯ãã®ãã¨ãèªè ãç解ãã¦ããã®ãåæã«è¨äºãæ¸ãã¦ããããã«æãã¾ãããã®ç¹ã«ã¤ãã¦ãããããªãããå®éã«ã¯æ°ã¥ãã¦ããªã人ã®æ¹ãå¤ããããã ã£ãã®ã§ã¯ãªãã§ããããã
ä»åã®ãããªåãç´ãè¾¼ãã§ãããªã©ãæ¬ã·ãªã¼ãºã¯å ¨é¨éãã¦èªãã§ãèªã¿ã«ãããªã£ã¦ããç¹ãããããã«æãã¾ãã次åã次ã åã«ã¯ã¾ã¨ãã®è¨äºãæ¸ãããã§ããã
ãã¾ãï¼è±ç·ãã¦è²ã ãªè¨èªã§ãã£ã¦ã¿ã¾ãã
$ perl -e '$x=0.1; $y=0; foreach(1..10){$y+=$x;} printf("%.19f\n", $y);' 0.9999999999999998890 $ perl -e '$x=0.1; $y=$x*10; printf("%.19f\n", $y);' 1.0000000000000000000
$ ruby -e 'x=0.1; y=0; (1..10).each{|i|y+=x}; printf("%.19f\n",y);' 0.9999999999999998890 $ ruby -e 'x=0.1; y=x*10; printf("%.19f\n",y);' 1.0000000000000000000
$ python -c 'x=0.1; print "%.19f" % reduce(lambda n,m:n+m, [ x for i in range(10)]);' 0.9999999999999998890 $ python -c 'x=0.1; y=x*10; print "%.19f" % y;' 1.0000000000000000000
$ echo 'int main(){double x=0.1,y=0;int i;for(i=0;i<10;i++){y+=x;}printf("%.19f\n",y);}' | gcc -xc -; ./a.out 0.9999999999999998890 $ echo 'int main(){double x=0.1,y=x*10;printf("%.19f\n",y);}' | gcc -xc -; ./a.out 1.0000000000000000000
ããããã¨ãã¯æ®éCã§å®é¨ãããã®ã ã¨æãã¾ããã©ãLLã§ãæ®éã«ç¢ºèªã§ãã¾ãããã¨ãã話ã§ããåè¨èªã®äººãã¡ãèªç¶ã«æ¸ãå 容ãã©ããã¯æ®å¿µãªãããããã¾ãããã©ã
追è¨:ãã©ãã¯ããã¯ãæ¥ã¦ããhttp://d.hatena.ne.jp/lethevert/20070612/p2ã«ã¤ãã¦ãåãå°ãä¸æè°ã ã£ããã§ããã続報ãããã¾ãããtoString()ããã¨ãã«10é²ã§ä¸çªçããªã表ç¾ã«ããã¨ããã®ã¯ç¾å®ä¸çã¨ã®èª¿åã¨ããæå³ã§ã¯é常ã«è¯ãè½ã¨ãã©ããã§ãããçåã解æ¶ãã¦ã¹ãããªã§ããçåç¹ããã£ã¦ãããããªè§£æ±ºã§ãããããªãããããããã¢ãã¯ãªæ¹ãè¦ãã¨æ°æã¡ãããã§ãããåãã§ããã ãé å¼µããããã®ã§ãã
*1:å®ã¯åãå ¨é¨ã¯èªãã§ãã¾ããã
*2:ãã¡ãããããã¯ããã§è¯ãå¤æã ã¨æãã¾ããååå±ããã«è¿å¯ãããã¨ãè¨ãã¾ããã
*3:æ£ç¢ºã«è¨ãã°ãPython風ã«roundé¢æ°ã®å é¨ã§æ¡å¼µç²¾åº¦ã§è©ä¾¡ããã¦ãæ§ããªãããªã·ã¼ã§ããã°5.04ã§ã5.05ã§ãééãã§ã¯ç¡ããã¨ãããã¨ã§ããã©ãã