2進法で計算するコンピュータにおいて、10進法で0.1と表される数値を10回足しても1になりません。
丸め誤差
実際、10進法の0.1は無限級数で
\begin{equation}
0.1=2^{-4}\sum_{i=0}^{\infty}a_i2^{-i}
\text{ where }a_i=\begin{cases}1&\text{ if }&i\equiv0,1\mod4\\0&\text{ if }&i\equiv2,3\mod4\end{cases}
\end{equation}
ですが、倍精度浮動小数点数では$i=52$で打ち切ってしまい、
\begin{equation}
0.1=2^{-4}\left(1+2^{-1}+2^{-4}+2^{-5}+\dots+2^{-48}+2^{-49}+2^{-52}+2^{-53}+\dots\right)
\end{equation}
更に、$a_{53}=1$を0捨1入して
\begin{equation}\begin{split}
0.1&=2^{-4}\left(1+2^{-1}+2^{-4}+2^{-5}+\dots+2^{-48}+2^{-49}+2^{-52}+2^{-52}\right)\\
&=2^{-4}\left(1+2^{-1}+2^{-4}+2^{-5}+\dots+2^{-48}+2^{-49}+2^{-51}\right)
\end{split}\end{equation}
となります(これを丸め誤差といいます)。
情報落ち
今から、
- 絶対値の大きい方に指数(先頭の$2$の右肩の数)を合わせて足す
- 仮数部(数式の括弧内)の先頭が$2$になれば指数部を+1
- 仮数部に$2^{-53}$が残れば$2^{-52}$に切り上げ、それ以降は切り捨て(0捨1入)
という方針で$0.1+0.1+\dots+0.1$を計算していきます。
まず、これで0.1+0.1を計算すると
\begin{flalign}
0.1+0.1
&=2^{-4}\left(1+2^{-1}+2^{-4}+2^{-5}+\dots+2^{-44}+2^{-45}+2^{-48}+2^{-49}+2^{-51}\right)&\\
&+2^{-4}\left(1+2^{-1}+2^{-4}+2^{-5}+\dots+2^{-44}+2^{-45}+2^{-48}+2^{-49}+2^{-51}\right)&\\
&=2^{-4}\left(2+1+2^{-3}+2^{-4}+\dots+2^{-43}+2^{-44}+2^{-47}+2^{-48}+2^{-50}\right)&\\
&=2^{-3}\left(1+2^{-1}+2^{-4}+2^{-5}+\dots+2^{-44}+2^{-45}+2^{-48}+2^{-49}+2^{-51}\right)&
\end{flalign}
となります。
更に計算を進めていきます。
\begin{flalign}
0.1+0.1+0.1&=(0.1+0.1)+0.1&\\
&=2^{-3}\left(1+2^{-1}+2^{-4}+2^{-5}+\dots+2^{-48}+2^{-49}+2^{-51}\right)&\\
&+2^{-4}\left(1+2^{-1}+2^{-4}+2^{-5}+\dots+2^{-48}+2^{-49}+2^{-51}\right)&\\
&=2^{-3}\left(1+2^{-1}+2^{-4}+2^{-5}+\dots+2^{-48}+2^{-49}+2^{-51}\right)&\\
&+2^{-3}\left(2^{-1}+2^{-2}+2^{-5}+2^{-6}+\dots+2^{-49}+2^{-50}+2^{-52}\right)&\\
&=2^{-3}\left(2+2^{-2}+2^{-3}+2^{-6}+\dots+2^{-47}+2^{-50}+2^{-51}+2^{-52}\right)&\\
&=2^{-2}\left(1+2^{-3}+2^{-4}+2^{-7}+\dots+2^{-48}+2^{-51}+2^{-52}+2^{-53}\right)&\\
&\sim2^{-2}\left(1+2^{-3}+2^{-4}+2^{-7}+\dots+2^{-47}+2^{-48}+2^{-50}\right)&
\end{flalign}
\begin{flalign}
0.1+0.1+0.1+0.1&=(0.1+0.1+0.1)+0.1&\\
&=2^{-2}\left(1+2^{-3}+2^{-4}+2^{-7}+\dots+2^{-47}+2^{-48}+2^{-50}\right)&\\
&+2^{-4}\left(1+2^{-1}+2^{-4}+2^{-5}+\dots+2^{-48}+2^{-49}+2^{-51}\right)&\\
&=2^{-2}\left(1+2^{-3}+2^{-4}+2^{-7}+\dots+2^{-47}+2^{-48}+2^{-50}\right)&\\
&+2^{-2}\left(2^{-2}+2^{-3}+2^{-6}+2^{-7}+\dots+2^{-50}+2^{-51}+2^{-53}\right)&\\
&=2^{-2}\left(1+2^{-1}+2^{-4}+2^{-5}+\dots+2^{-48}+2^{-49}+2^{-51}+2^{-53}\right)&\\
&\sim2^{-2}\left(1+2^{-1}+2^{-4}+2^{-5}+\dots+2^{-48}+2^{-49}+2^{-51}+2^{-52}\right)&
\end{flalign}
\begin{flalign}
&0.1+0.1+0.1+0.1+0.1=(0.1+0.1+0.1+0.1)+0.1&\\
&=2^{-2}\left(1+2^{-1}+2^{-4}+2^{-5}+\dots+2^{-48}+2^{-49}+2^{-51}\right)&\\
&+2^{-4}\left(1+2^{-1}+2^{-4}+2^{-5}+\dots+2^{-48}+2^{-49}+2^{-51}\right)&\\
&=2^{-2}\left(1+2^{-1}+2^{-4}+2^{-5}+\dots+2^{-48}+2^{-49}+2^{-51}\right)&\\
&+2^{-2}\left(2^{-2}+2^{-3}+2^{-6}+2^{-7}+\dots+2^{-50}+2^{-51}+2^{-53}\right)&\\
&=2^{-2}\left(1+2^{-1}+2^{-2}+2^{-3}+\dots+2^{-48}+2^{-49}+2^{-50}+2^{-51}+2^{-51}+2^{-53}\right)&\\
&=2^{-2}\left(2+2^{-53}\right)=2^{-1}\left(1+2^{-54}\right)\sim2^{-1}&
\end{flalign}
計算が間違っていなければ、0.1を5回足した結果は0.5に一致しました。
\begin{flalign}
&0.1+0.1+0.1+0.1+0.1+0.1=(0.1+0.1+0.1+0.1+0.1)+0.1&\\
&=2^{-1}+2^{-4}\left(1+2^{-1}+2^{-4}+2^{-5}+\dots+2^{-48}+2^{-49}+2^{-51}\right)&\\
&=2^{-1}\left(1+2^{-3}+2^{-4}+2^{-7}+\dots+2^{-48}+2^{-51}+2^{-52}+2^{-54}\right)&\\
&\sim2^{-1}\left(1+2^{-3}+2^{-4}+2^{-7}+\dots+2^{-48}+2^{-51}+2^{-52}\right)&\\
\end{flalign}
\begin{flalign}
&0.1+0.1+0.1+0.1+0.1+0.1+0.1=(0.1+0.1+0.1+0.1+0.1+0.1)+0.1&\\
&=2^{-1}\left(1+2^{-3}+2^{-4}+2^{-7}+\dots+2^{-48}+2^{-51}+2^{-52}\right)&\\
&+2^{-4}\left(1+2^{-1}+2^{-4}+2^{-5}+\dots+2^{-48}+2^{-49}+2^{-51}\right)&\\
&=2^{-1}\left(1+2^{-3}+2^{-4}+2^{-7}+\dots+2^{-48}+2^{-51}+2^{-52}\right)&\\
&+2^{-1}\left(2^{-3}+2^{-4}+2^{-7}+2^{-8}+\dots+2^{-51}+2^{-52}+2^{-54}\right)&\\
&=2^{-1}\left(1+2^{-2}+2^{-3}+2^{-6}+2^{-7}+\dots+2^{-50}+2^{-51}+2^{-54}\right)&\\
&\sim2^{-1}\left(1+2^{-2}+2^{-3}+2^{-6}+2^{-7}+\dots+2^{-50}+2^{-51}\right)&\\
\end{flalign}
\begin{flalign}
&0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1\\&=(0.1+0.1+0.1+0.1+0.1+0.1+0.1)+0.1&\\
&=2^{-1}\left(1+2^{-2}+2^{-3}+2^{-6}+2^{-7}+\dots+2^{-50}+2^{-51}\right)&\\
&+2^{-4}\left(1+2^{-1}+2^{-4}+2^{-5}+\dots+2^{-48}+2^{-49}+2^{-51}\right)&\\
&=2^{-1}\left(1+2^{-2}+2^{-3}+2^{-6}+2^{-7}+\dots+2^{-50}+2^{-51}\right)&\\
&+2^{-1}\left(2^{-3}+2^{-4}+2^{-7}+2^{-8}+\dots+2^{-51}+2^{-52}+2^{-54}\right)&\\
&=2^{-1}\left(1+2^{-1}+2^{-4}+2^{-5}+2^{-8}+\dots+2^{-48}+2^{-49}+2^{-52}+2^{-54}\right)&\\
&\sim2^{-1}\left(1+2^{-1}+2^{-4}+2^{-5}+2^{-8}+\dots+2^{-48}+2^{-49}+2^{-52}\right)&\\
\end{flalign}
既に、0.1を8倍した値$2^{-1}\left(1+\dots+2^{-51}\right)$と、0.1を8回足した値$2^{-1}\left(1+\dots+2^{-52}\right)$で、$2^{-53}$の誤差が出ていますね。
絶対値の小さい0.1の指数部-4を、絶対値の大きい0.8の指数部-1に合わせた際、仮数部に残った$2^{-54}$が落ちています。これを情報落ちといいます。(この現象はすでに発生している)
\begin{flalign}
&0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1\\&=(0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1)+0.1&\\
&=2^{-1}\left(1+2^{-1}+2^{-4}+2^{-5}+2^{-8}+\dots+2^{-48}+2^{-49}+2^{-52}\right)&\\
&+2^{-4}\left(1+2^{-1}+2^{-4}+2^{-5}+\dots+2^{-48}+2^{-49}+2^{-51}\right)&\\
&=2^{-1}\left(1+2^{-1}+2^{-4}+2^{-5}+2^{-8}+\dots+2^{-48}+2^{-49}+2^{-52}\right)&\\
&+2^{-1}\left(2^{-3}+2^{-4}+2^{-7}+2^{-8}+\dots+2^{-51}+2^{-52}+2^{-54}\right)&\\
&=2^{-1}\left(1+2^{-1}+2^{-2}+2^{-5}+2^{-6}+\dots+2^{-49}+2^{-50}+2^{-54}\right)&\\
&\sim2^{-1}\left(1+2^{-1}+2^{-2}+2^{-5}+2^{-6}+\dots+2^{-49}+2^{-50}\right)&\\
\end{flalign}
\begin{flalign}
&0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1\\&=(0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1)+0.1&\\
&=2^{-1}\left(1+2^{-1}+2^{-2}+2^{-5}+2^{-6}+\dots+2^{-49}+2^{-50}\right)&\\
&+2^{-4}\left(1+2^{-1}+2^{-4}+2^{-5}+\dots+2^{-48}+2^{-49}+2^{-51}\right)&\\
&=2^{-1}\left(1+2^{-1}+2^{-2}+2^{-5}+2^{-6}+\dots+2^{-49}+2^{-50}\right)&\\
&+2^{-1}\left(2^{-3}+2^{-4}+2^{-7}+2^{-8}+\dots+2^{-51}+2^{-52}+2^{-54}\right)&\\
&=2^{-1}\left(1+2^{-1}+2^{-2}+2^{-3}+2^{-4}+\dots+2^{-49}+2^{-50}+2^{-51}+2^{-52}+2^{-54}\right)&\\
&\sim2^{-1}\left(1+2^{-1}+2^{-2}+2^{-3}+2^{-4}+\dots+2^{-49}+2^{-50}+2^{-51}+2^{-52}\right)&\\
\end{flalign}
残念ながら、0.1を10回足した結果は、1より$2^{-1}\times2^{-52}=2^{-53}$だけ少ないことが分かりました(途中の計算が間違っていなければ)。
折角なので、足して1になってほしい浮動小数点数の組み合わせで、誤差をチェックしてみましょう。
以下のsum
関数のテストをするつもりで、テストケースを作成します。
#include <stdarg.h>
/*
* 入力された浮動小数点数の合計値を返す
* n : 入力する浮動小数点数の個数
*/
double sum(int n,...){
va_list ap;
va_start(ap,n);
double total=0.0;
for(int i=0;i<n;i++){
total+=va_arg(ap,double);
}
va_end(ap);
return total;
}
この関数に対して、可変長引数の部分に整数が入力されると、正しく処理されません。1
ではなく、1.0
や(double)1
として渡してください。
- 答えが1になってほしいテストを実施し、テスト結果を表示する
#include <stdio.h>
#include <stdarg.h>
#include <math.h>
/* 総テストケース数 */
int tests_run=0;
/* 成功したテストケース数 */
int success=0;
/*
* 答えが1になってほしいテストを実施し、テスト結果を表示する
* n : 入力する値の個数
* 可変長引数の部分には合計が1となることが期待される浮動小数点数列を入力する
*/
void test(int n,...){
va_list ap;
va_start(ap,n);
//入力された浮動小数点数を配列に格納する
double A[10] = {0};
for(int i=0;i<n;i++){
A[i]=va_arg(ap,double);
//入力値を画面表示
putchar(i?'+':'$');
printf("%.1lf",A[i]);
}
va_end(ap);
//テスト:合計値を計算する※n以上の添字の値は関数呼び出し先で使われない
double result=sum(n,A[0],A[1],A[2],A[3],A[4],A[5],A[6],A[7],A[8],A[9]);
tests_run++;
//誤差をm*2^nの形で出力する
int exponent;
double mantissa=frexp(result-1,&exponent);
printf("=1");
if(mantissa)printf("%+lf\\times2^{%d}",mantissa*2,exponent-1);
else success++;
printf("$\n");
}
- テストケース
- 1~24 {0.1,0.2,0.3,0.4}の順列
- 25~66 小数点以下1桁以下の正の実数値を昇順ソートした数列(絶対値の小さい値から足したほうが精度が上がる)
※#1と#47は重複しています。
void test1(){test(4,.1,.2,.3,.4);}
void test2(){test(4,.1,.2,.4,.3);}
void test3(){test(4,.1,.3,.2,.4);}
void test4(){test(4,.1,.3,.4,.2);}
void test5(){test(4,.1,.4,.2,.3);}
void test6(){test(4,.1,.4,.3,.2);}
void test7(){test(4,.2,.1,.3,.4);}
void test8(){test(4,.2,.1,.4,.3);}
void test9(){test(4,.2,.3,.1,.4);}
void test10(){test(4,.2,.3,.4,.1);}
void test11(){test(4,.2,.4,.1,.3);}
void test12(){test(4,.2,.4,.3,.1);}
void test13(){test(4,.3,.1,.2,.4);}
void test14(){test(4,.3,.1,.4,.2);}
void test15(){test(4,.3,.2,.1,.4);}
void test16(){test(4,.3,.2,.4,.1);}
void test17(){test(4,.3,.4,.1,.2);}
void test18(){test(4,.3,.4,.2,.1);}
void test19(){test(4,.4,.1,.2,.3);}
void test20(){test(4,.4,.1,.3,.2);}
void test21(){test(4,.4,.2,.1,.3);}
void test22(){test(4,.4,.2,.3,.1);}
void test23(){test(4,.4,.3,.1,.2);}
void test24(){test(4,.4,.3,.2,.1);}
void test25(){test(1,1.);}
void test26(){test(2,.1,.9);}
void test27(){test(2,.2,.8);}
void test28(){test(3,.1,.1,.8);}
void test29(){test(2,.3,.7);}
void test30(){test(3,.1,.2,.7);}
void test31(){test(4,.1,.1,.1,.7);}
void test32(){test(2,.4,.6);}
void test33(){test(3,.1,.3,.6);}
void test34(){test(3,.2,.2,.6);}
void test35(){test(4,.1,.1,.2,.6);}
void test36(){test(5,.1,.1,.1,.1,.6);}
void test37(){test(2,.5,.5);}
void test38(){test(3,.1,.4,.5);}
void test39(){test(3,.2,.3,.5);}
void test40(){test(4,.1,.1,.3,.5);}
void test41(){test(4,.1,.2,.2,.5);}
void test42(){test(5,.1,.1,.1,.2,.5);}
void test43(){test(6,.1,.1,.1,.1,.1,.5);}
void test44(){test(3,.2,.4,.4);}
void test45(){test(4,.1,.1,.4,.4);}
void test46(){test(3,.3,.3,.4);}
void test47(){test(4,.1,.2,.3,.4);}
void test48(){test(5,.1,.1,.1,.3,.4);}
void test49(){test(4,.2,.2,.2,.4);}
void test50(){test(5,.1,.1,.2,.2,.4);}
void test51(){test(6,.1,.1,.1,.1,.2,.4);}
void test52(){test(7,.1,.1,.1,.1,.1,.1,.4);}
void test53(){test(4,.1,.3,.3,.3);}
void test54(){test(4,.2,.2,.3,.3);}
void test55(){test(5,.1,.1,.2,.3,.3);}
void test56(){test(6,.1,.1,.1,.1,.3,.3);}
void test57(){test(5,.1,.2,.2,.2,.3);}
void test58(){test(6,.1,.1,.1,.2,.2,.3);}
void test59(){test(7,.1,.1,.1,.1,.1,.2,.3);}
void test60(){test(8,.1,.1,.1,.1,.1,.1,.1,.3);}
void test61(){test(5,.2,.2,.2,.2,.2);}
void test62(){test(6,.1,.1,.2,.2,.2,.2);}
void test63(){test(7,.1,.1,.1,.1,.2,.2,.2);}
void test64(){test(8,.1,.1,.1,.1,.1,.1,.2,.2);}
void test65(){test(9,.1,.1,.1,.1,.1,.1,.1,.1,.2);}
void test66(){test(10,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1);}
-
main
関数
int main(int argc, char* argv[]){
test1();
test2();
test3();
test4();
test5();
test6();
test7();
test8();
test9();
test10();
test11();
test12();
test13();
test14();
test15();
test16();
test17();
test18();
test19();
test20();
test21();
test22();
test23();
test24();
test25();
test26();
test27();
test28();
test29();
test30();
test31();
test32();
test33();
test34();
test35();
test36();
test37();
test38();
test39();
test40();
test41();
test42();
test43();
test44();
test45();
test46();
test47();
test48();
test49();
test50();
test51();
test52();
test53();
test54();
test55();
test56();
test57();
test58();
test59();
test60();
test61();
test62();
test63();
test64();
test65();
test66();
printf("success...%d/%d\n",success,tests_run);
return 0;
}
- 出力結果
※test
関数にてわざと$
記号付きで出力したものをそのまま貼り付けているので、Markdown記法では数式のように表示されています。
$0.1+0.2+0.3+0.4=1$
$0.1+0.2+0.4+0.3=1$
$0.1+0.3+0.2+0.4=1$
$0.1+0.3+0.4+0.2=1$
$0.1+0.4+0.2+0.3=1$
$0.1+0.4+0.3+0.2=1$
$0.2+0.1+0.3+0.4=1$
$0.2+0.1+0.4+0.3=1$
$0.2+0.3+0.1+0.4=1$
$0.2+0.3+0.4+0.1=1$
$0.2+0.4+0.1+0.3=1$
$0.2+0.4+0.3+0.1=1+1.000000\times2^{-52}$
$0.3+0.1+0.2+0.4=1$
$0.3+0.1+0.4+0.2=1$
$0.3+0.2+0.1+0.4=1$
$0.3+0.2+0.4+0.1=1$
$0.3+0.4+0.1+0.2=1$
$0.3+0.4+0.2+0.1=1-1.000000\times2^{-53}$
$0.4+0.1+0.2+0.3=1$
$0.4+0.1+0.3+0.2=1$
$0.4+0.2+0.1+0.3=1$
$0.4+0.2+0.3+0.1=1+1.000000\times2^{-52}$
$0.4+0.3+0.1+0.2=1$
$0.4+0.3+0.2+0.1=1-1.000000\times2^{-53}$
$1.0=1$
$0.1+0.9=1$
$0.2+0.8=1$
$0.1+0.1+0.8=1$
$0.3+0.7=1$
$0.1+0.2+0.7=1$
$0.1+0.1+0.1+0.7=1$
$0.4+0.6=1$
$0.1+0.3+0.6=1$
$0.2+0.2+0.6=1$
$0.1+0.1+0.2+0.6=1$
$0.1+0.1+0.1+0.1+0.6=1$
$0.5+0.5=1$
$0.1+0.4+0.5=1$
$0.2+0.3+0.5=1$
$0.1+0.1+0.3+0.5=1$
$0.1+0.2+0.2+0.5=1$
$0.1+0.1+0.1+0.2+0.5=1$
$0.1+0.1+0.1+0.1+0.1+0.5=1$
$0.2+0.4+0.4=1$
$0.1+0.1+0.4+0.4=1$
$0.3+0.3+0.4=1$
$0.1+0.2+0.3+0.4=1$
$0.1+0.1+0.1+0.3+0.4=1$
$0.2+0.2+0.2+0.4=1$
$0.1+0.1+0.2+0.2+0.4=1$
$0.1+0.1+0.1+0.1+0.2+0.4=1$
$0.1+0.1+0.1+0.1+0.1+0.1+0.4=1$
$0.1+0.3+0.3+0.3=1$
$0.2+0.2+0.3+0.3=1$
$0.1+0.1+0.2+0.3+0.3=1$
$0.1+0.1+0.1+0.1+0.3+0.3=1$
$0.1+0.2+0.2+0.2+0.3=1$
$0.1+0.1+0.1+0.2+0.2+0.3=1$
$0.1+0.1+0.1+0.1+0.1+0.2+0.3=1$
$0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.3=1$
$0.2+0.2+0.2+0.2+0.2=1$
$0.1+0.1+0.2+0.2+0.2+0.2=1$
$0.1+0.1+0.1+0.1+0.2+0.2+0.2=1$
$0.1+0.1+0.1+0.1+0.1+0.1+0.2+0.2=1$
$0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.2=1$
$0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1=1-1.000000\times2^{-53}$
success...61/66
前半24個のテストケースより、絶対値の小さい値を最後に足してしまうと、誤差が発生しやすくなることが分かります。
けた落ち
ついでに、$0.9-0.8$と$0.1$を比較してみたいと思います
\begin{flalign}
0.9
&=2^{-1}\left(1+2^{-1}+2^{-2}+2^{-5}+2^{-6}+\dots+2^{-45}+2^{-46}+2^{-49}+2^{-50}+2^{-52}\right)&\\
0.8
&=2^{-1}\left(1+2^{-1}+2^{-4}+2^{-5}+2^{-8}+\dots+2^{-44}+2^{-45}+2^{-48}+2^{-49}+2^{-51}\right)&\\
0.9-0.8&=2^{-1}\left(2^{-3}+2^{-4}+2^{-7}+2^{-8}+\dots+2^{-47}+2^{-48}+2^{-51}+2^{-52}\right)&\\
&=2^{-4}\left(1+2^{-1}+2^{-4}+2^{-5}+\dots+2^{-44}+2^{-45}+2^{-48}+2^{-49}\right)&\\
\end{flalign}
一方、
\begin{flalign}
&0.1=2^{-4}\left(1+2^{-1}+2^{-4}+2^{-5}+\dots+2^{-48}+2^{-49}+2^{-51}\right)&
\end{flalign}
なので、$0.9-0.8$は$0.1$より$2^{-55}$小さい値となりました。
$0.9-0.8$を計算する際、指数部が元々-1だったのが-4になったことにより、2進法での有効桁数が3桁落ちています。これをけた落ちといいます。
#include <stdio.h>
#include <math.h>
int main(int argc, char* argv[]){
double x=.9;
x-=.8;
int exponent;
double mantissa=frexp(x-.1,&exponent);
printf("$0.9-0.8=0.1%+lf\\times2^{%d}$\n",mantissa*2,exponent-1);
return 0;
}
実行結果
$0.9-0.8=0.1-1.000000\times2^{-55}$
※実行環境によって、$0.9-0.8$と$0.1$は等しくなることがあります。
- 言語によってはDecimal型のような十進法で扱う型があります。より正確に計算したい場合はそちらを使うようにしましょう。