「
数値型の計算誤差
」を編集中
2022年11月20日 (日) 05:19時点における
Master
(
トーク
|
投稿記録
)
による版
(WSOFTDocsへ移行しました)
(
差分
)
← 古い版
|
最新版
(
差分
) |
新しい版 →
(
差分
)
ナビゲーションに移動
検索に移動
警告: このページの古い版を編集しています。
公開すると、この版以降になされた変更がすべて失われます。
警告:
ログインしていません。編集を行うと、あなたの IP アドレスが公開されます。
ログイン
または
アカウントを作成
すれば、あなたの編集はその利用者名とともに表示されるほか、その他の利点もあります。
スパム攻撃防止用のチェックです。 決して、ここには、値の入力は
しない
でください!
<script type="text/javascript"> document.location.href = "http://docs.wsoft.ws/alice/tutorial/calculation-error/"; </script> [[Category:チュートリアル]] AliceScriptの数値型は、倍精度浮動小数点数数値型と定められており、この規格はIEEE754として標準化されています。 しかしこの数値型で小数の計算をしているとき、その計算結果が期待通りでないことがあります。この記事では、その理由について説明します。 =計算誤差の例= 0.1 + 0.2 == 0.3 数学では、上記の式は正しく<code>true</code>と評価されますし、ほとんどの開発者も、それが正しいことを期待します。 では、AliceScriptでこの式を評価してみます。 print(0.1 + 0.2 == 0.3); このコードを実行すると、<code>false</code>が出力されます。 =理由= これらの計算誤差はバグではありません。AliceScriptの数値型のような浮動小数点数数値型は、値を2進数で格納していますが、ほとんどの10進数の小数は2進数で表すことができず、その近似値として表現されます。近似値の誤差が例に示したような計算誤差として現れます。 知的好奇心旺盛なあなたのために、もう少し説明します。例えば10進数の<code>0.1</code>を2進数に変換すると<code>0.0001100110011…</code>となり、<code>0011</code>が永遠と循環します。そのため<code>0.1</code>を倍精度浮動小数点数数値型に格納するには、適当な桁で丸める必要があります。このとき、最も近い偶数に値を丸めます。その結果として<code>0.1</code>は数値型では2進数で<code>0.0001100110011001100110011001100110011001100110011001101</code>となります。これを10進数に戻す<code>0.1000000000000000055511151231257827021181583404541015625</code>となり、<code>0.1</code>ではなくなります。 ただし整数は、有効桁数15桁の範囲内であれば、正確に格納できます。また小数であっても、k/2^n (k,nは整数)で表すこともできる小数は2進数で表現できるため、正確に格納できます。 なおこのような誤差は、AliceScript固有のものではありません。IEEE 754の2進浮動小数点形式を採用しているシステムでは、同じことが起こりえます。 =回避策= ==許容範囲を決めて値を比較する== 二つの浮動小数点数数値型数を比較するとき、例えばそれらが等しいかを調べるときに<code>==</code>を使うのは危険です。両者が全く等しい場合のみ等しいと判断するのではなく、「両者の差の絶対値がある程度であれば等しいと認める」など許容範囲を決めて比較する方が安全です。次の例は、許容範囲を定めt、<code>0.000001</code>未満の差の場合に等しいと判断します。 import “Alice.Math”; ///二つの小数が等しいと認められるかどうかを評価します ///パラメータ a:一方の値 /// b:もう一方の値 /// tolerance:許容できる誤差の最大の値(この数値は常に正) function NumEqual(number a,number b,number tolerance) { return (math_abs(a - b) < tolerance); } var numA = 0.1 + 0.2; var numB = 0.3; ///許容範囲 var tolerance = 0.000001; if(NumEqual(numA,numB,tolerance)) { print(“二値は等しいと認められました”); } ==一度整数にした後で変換する== 他には、例えば一度整数にしてから計算する方法も考えられます。次の例では、二つの数を整数にすることができる数<code>dis ** 10</code>を求めて二数を整数にした後、計算を行うことで誤差を防ぎます。ただしこの場合でも、有効数字15桁以上の数は正確に扱うことができません。 import “Alice.Math”; ///与えられた数の小数点以下の桁数を取得します function GetDisitsUnder1(number num) { var priceString = price.ToString().TrimEnd('0'); int index = priceString.IndexOf('.'); if (index == -1){return 0;} return priceString.Substring(index + 1).Length; } ///二つの小数が等しいかどうかを判断します function NumEqual(number numA,numbner numB) { //numA,numBの小数点以下の桁数(すなわち、10の何乗倍すれば整数になるか) var nAd = GetDisitsUnder1(numA); var nBd = GetDisitsUnder1(numB); //二つの小数の小数点以下桁数のうち多い方 var dis = math_max(nAd,nBd); //numA,numBを整数化したもの var rA = numA * (dis ** 10); var rB = numB * (dis ** 10); return (rA == rB); } ///二つの小数の和を求めます function Sum(number numA,number numB) { //numA,numBの小数点以下の桁数(すなわち、10の何乗倍すれば整数になるか) var nAd = GetDisitsUnder1(numA); var nBd = GetDisitsUnder1(numB); //二つの小数の小数点以下桁数のうち多い方 var dis = math_max(nAd,nBd); //numA,numBを整数化したもの var rA = numA * (dis ** 10); var rB = numB * (dis ** 10); //整数化されたものの計算結果 var result = rA + rB; return (result / (dis ** 10)); } print(NumEqual(0.1+0.2,0.3));//出力例:true print(Sum(0.1,0.2));//出力例:0.3
編集内容の要約:
AliceScript Wikiへの投稿はすべて、他の投稿者によって編集、変更、除去される場合があります。 自分が書いたものが他の人に容赦なく編集されるのを望まない場合は、ここに投稿しないでください。
また、投稿するのは、自分で書いたものか、パブリック ドメインまたはそれに類するフリーな資料からの複製であることを約束してください(詳細は
AliceScript Wiki:著作権
を参照)。
著作権保護されている作品は、許諾なしに投稿しないでください!
キャンセル
編集ヘルプ
(新しいウィンドウで開きます)
このページで使用されているテンプレート:
テンプレート:Alert
(
編集
)
テンプレート:Func-getcolor
(
編集
)
テンプレート:Func-getsafe
(
編集
)
ナビゲーション メニュー
個人用ツール
ログインしていません
トーク
投稿記録
アカウント作成
ログイン
名前空間
ページ
議論
日本語
表示
閲覧
編集
履歴表示
その他
検索
案内
メインページ
ツアー
ダウンロード
規約
おまかせ表示
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報