HSPポータル
サイトマップ お問い合わせ


HSPTV!掲示板


未解決 解決 停止 削除要請

2013
0320
FunnyMaker限りなく0に近い数ができてしまう6解決


FunnyMaker

リンク

2013/3/20(Wed) 22:44:32|NO.53111

見慣れない現象が起こっています。

簡単に言うと、ある値が 0 なのかどうか( というよりは十分小さいかどうか...? )が簡単には分からなくなる現象です。

下のスクリプトを実行すると分かると思いますが、変数Bに代入されている数値が、見かけ上0になっているのに、
それが0かどうかをチェックしようとすると「0でない」と言われてしまいます。



mes "B = 1.0*cos(m_pi/2.0)"
B = 1.0*cos(m_pi/2.0)
mes "B = "+B+""

if B = 0.0 : mes "w"
if B = -0.0 : mes "w"

mes "嗚呼...、 2.0/B = "+str(2.0/B)+""

if double(str(B)) = 0 : mes "Bは本当は0だと思います。"


if B > 0 : mes "ですが、Bは0よりほんの少し大きいらしいです。"

H = B + 1.0 : if H = 1.0 : mes "ですが、 B+1.0 は 1.0 だそうです。";Bに何かを足すと、結果は上手く認識できる。
;が、いつもそうとは限らない。とあるモジュール内で同じ事をやったら何故かダメだった。
if B + 1.0 - 1.0 = 0.0 : mes "また、B+1.0-1.0 は 0.0 だそうです。"

mes ""

Y = 1.0*sin(m_pi/2.0)
if Y = 1.0 : mes "1.0*sin(m_pi/2.0) = 1.000000 はちゃんと分かっているそうです"


このBの値を出現させるのに苦労しました。もうひとつ、-0.000000となって同様の現象が起こっている
スクリプトがあるのですが、原因箇所が分からない上にサイズが大きすぎて載せられません。

上記現象に今、非常に困っております。
というのも、空間ベクトルを扱うための計算用モジュール群を作っていて、わりと頻繁に「理論上はキッカリ0」
な数値が現れるので、その度にこの問題にはまって計算がおかしくなって、やりきれません。
今のところ、double(str(ターゲット)) としてとりあえずifによる0判定をクリアしています。
しかしこれだと無駄な計算コストがかっています。ループするとなると遅延が無視できないのです。

何か解決法を教えていただけないでしょうか?



この記事に返信する


てれてれ

リンク

2013/3/20(Wed) 22:59:39|NO.53112

解決法では無いですが・・・
HSPのmes命令では小数点第七位以下は表示されません。
peek命令で変数の中身を覗いてみると表示されない部分が異なっていることがわかります。


mes "A = 0.0" A = 0.0 mes a repeat 8 mes " "+cnt+" Byte : "+peek(A,cnt) loop wait 100 : mes "" mes "B = 1.0*cos(m_pi/2.0)" B = 1.0*cos(m_pi/2.0) repeat 8 mes " "+cnt+" Byte : "+peek(B,cnt) loop wait 100 : mes "" mes "よって A ≠ B"



FunnyMaker

リンク

2013/3/20(Wed) 23:06:00|NO.53113

そうだったんですね。

私もついさっき気づきました。

対数をとったりしていたらどうも0より若干大きいようで。
思い返せば、m_pi がすでに完璧なπでないから 余弦をとった時点でアウトだなあと。

とにかく、納得できました。ありがとうございます。



暇人

リンク

2013/3/20(Wed) 23:26:23|NO.53115

str()よりは桁を上げて小数点以下切り捨てた方が処理は軽い

mes "B = 1.0*cos(m_pi/2.0)" B = 1.0*cos(m_pi/2.0) mes "B = "+strf("%.35f",B) if int(B*1000000) = 0 :B=0.0: mes "w" mes "嗚呼...、 2.0/B = "+str(2.0/B)+""



晩御飯

リンク

2013/3/20(Wed) 23:29:38|NO.53116

HSPに限らないけど基本的に浮動小数点型同士の比較にイコールを使うのはご法度



YSR

リンク

2013/3/21(Thu) 00:56:25|NO.53118

・m_piが「3.1415926535897931」(実数値)と定義されているので、2.0で割った際に計算誤差が生じている。
・そもそもmes命令だけだと小数点以下6桁しか表示されない。strf命令も併用するべき。

;例1 mes strf("%.16f",M_PI) //「3.1415926535897931」と表示
・「ある小ささ以下なら0」をやりたければ、

;例2 #define EPS 0.000001 b=1.0*cos(M_PI/2.0) if absf(b)<EPS { mes "B=0" } else { mes "B="+strf("%.16f",num) }
のように、ある(絶対値が)小さな値を使えばいい。
・「計算機イプシロン」と呼ばれるものがあり、絶対値がその値以下なら1.0に足し引きしても「1.0と同じ」と判定される。
 詳しくはWikipediaとかを見ればいいかも。



FunnyMaker

リンク

2013/3/23(Sat) 20:37:06|NO.53183

暇人さん、晩御飯さん、YSRさん、アドバイスありがとうございます。
これからは小さい数により気をつけたいと思います。
位の切り上げ・切り捨てについては暇人さんの方法を参考にさせていただきます。

あと余談ですが、
正弦・余弦・正接でピッタリ0や±1の戻り値が欲しいことが多いので、これらについてはm_piを完璧なπとして扱う三角関数を適当に定義して通常の命令とケースバイケースで使い分けることにしました。



ONION software Copyright 1997-2023(c) All rights reserved.