- 締切済み
javascriptで浮動小数点の問題
javascriptで浮動小数点の問題を解決しつつevalをつかった電卓を作ることはできませんか? >>> //-が欲しい場合 var array2 = str.match(/-?[0-9]+\.?[0-9]*/g); for(var i = 0; i < array2.length; i++) { console.log(parseFloat(array2[i])); } な感じで数値を抜き出し 計算記号+-*/に従ってmathのメソッドを順次呼び出す関数を作成すればいいのです。 計算記号については抜き出した数値を文字列として数えれば抜き出せるはず。 このように教えてもらいましたが、正直難しすぎてさっぱりわかりません。 evalでは浮動小数点の問題は解決できないのでしょうか? 便利なライブラリなどもないでしょうか?
- みんなの回答 (21)
- 専門家の回答
みんなの回答
- amanojaku1
- ベストアンサー率54% (265/488)
>回答No.10 amanojaku1 また、桁数が少ない整数演算でも除算があると誤差が出る場合があります(下記は その実例です)。 これは浮動小数を整数で演算すると言うアイデアの致命的な欠陥と言えます。 (浮動小数を整数で演算しないで)「丸める」のが現実的な解決法でしょう。 ただし、(人間から見て見た目が良い感じでも)「丸める」のも誤差であると言う認識が必要です。 <head> <meta http-equiv="Content-Type" content="text/html; charset=Shift-JIS"> <TITLE>test</TITLE> </head> <body> <script type="text/javascript"> <!-- a=2042; a=a/100; a=a*150; alert(a+"\n"); // --> </script> </body> </html>
- amanojaku1
- ベストアンサー率54% (265/488)
>https://qiita.com/k_moto/items/0b576a3351b77fb0aa98 >上記の整数化の処理を導入すれば、ライブラリを使わなくて、evalでも浮動小数点の問題を解決した計算ができるようになると考えてよいでしょうか? いえ、evalのセキュリティ対策の正規表現を浮動小数点の四則演算に対応させるだけなので、浮動小数点の誤差には なんら影響しません。 そのサイトの方法は、前提として「桁数が少ない場合」に可能ですが、桁数が多い場合は不可能です。 浮動小数がDouble(倍精度)でも、整数の桁が多いと結局 誤差が生じます。 下記は その実例です、下記の桁数だと整数(実態は浮動小数)で誤差が生じるのですから、浮動小数を整数で演算すると言うアイデア自体の意味がありません。 下記を実行すると文字列と整数(実態は浮動小数)の値が違うのが確認できると思います。 <head> <meta http-equiv="Content-Type" content="text/html; charset=Shift-JIS"> <TITLE>test</TITLE> </head> <body> <script type="text/javascript"> <!-- alert("76287755398823936\n"+ 76287755398823936+"\n"); // --> </script> </body> </html>
- b0a0a
- ベストアンサー率49% (156/313)
>>難しすぎて自分ではどう作ればいいかさっぱりわかりません。 何事もまず一番単純化した流れを考えて、そこから広げていけばできます。 ただしこの手の問題で厄介なのが、GUI部分をどうしたいかでやや流れも変わってくることです。 ですのでとりあえずGoogle検索のような途中式が表示されるものではない一般的な電卓のような表示を考えましょう。 そしてまず演算キーは+だけを考えます。 +が押されたら、どういう挙動をすれば良いですか? もしそれ以前に1 + 2と入力しての+であれば、3と表示しないといけません。 となると、2つ前の数値と、1つ前の演算子を覚えておけば、あと現在の数値を使って演算が可能ですね つまり、+が押されたら数値を記録して、+が押されたことも記録する 記録されている演算子の種類で場合分けをして、記録されている数値2つを使って演算する という流れで実現できます そしてここで重要なのが、最初に1 +と入力したときの+のときの処理です。 このときはまだ2つ前の数値と1つ前の演算子がありません。 ただ、こういうケースを例外として場合分けするのはあまりオススメしません。 コードが複雑化しないように、できる限り共通の処理が使えるように工夫しましょう。 今回の場合、何も入力がない状態で、0 +から始まってるとすれば良いのです。 そうすれば1 + の段階でも、0 + 1で+が押されたときとみなせますから、例外処理を考えたり書かなくて済みます。 JSではこのように、その柔軟さを生かしてできるだけ手抜きをするよう心がけましょう。 そうすることで本当に大事な部分に集中できます。
- b0a0a
- ベストアンサー率49% (156/313)
四捨五入についてはまあやっても良いかもしれませんが、別にやらなくても良いと思いますよ >>恐らく~の下記の部分を自分の計算に導入するのが一番でしょうか? 質問者さんが勉強したいのならば、そういったのを良く参考にして自分で作って見る方が良いでしょう ただ完成形が欲しいのであれば見かけも含んだライブラリを探して丸ごと置くだけでよいのでは? まあこういったのは確かに始めてやると難しいでしょうが、要するにパーサーを作るということであり JavaScriptの実行環境が別の言語で作られているように、 あなたはごく限られた数式というオリジナル言語を、JavaScriptで実装していることになるのです この分野を極めれば自分の好きな言語を定義たり実行環境を作るということもできるということです あ、そう言えば最近この手の問題を言語レベルで取り組んでいるものの話を聞いたことがあります 色々なやり方のヒントくらいは得られるかもしれません https://turingcomplete.fm/17
お礼
mathjsの導入を頑張ったのですが、evalがないので不可能ということがわかりました。 eval可能なものがあれば教えてください。 evalを使わないと乗算などもすべて 自分で作らないといけないのですよね。難しすぎて自分ではどう作ればいいかさっぱりわかりません。
補足
JSで作った電卓で浮動小数点の問題を解決するのは実質不可能であれば、一つは小数点はすべて四捨五入する。 そしてもう一つ別の電卓を作ってそちらではそのままこの問題を放置するという二つのバージョンを作るしかないかなと思ったのですが、これでまず問題ないと思っていいでしょうか? あくまで一般論です。
- amanojaku1
- ベストアンサー率54% (265/488)
>回答No.6 amanojaku1 少し修正します。 <head> <meta http-equiv="Content-Type" content="text/html; charset=Shift-JIS"> <TITLE>test</TITLE> </head> <body> <input type="text" name="gakunen" value="" onKeyPress="Calculator(this.value, event.keyCode)"> <script type="text/javascript"> <!-- function Calculator(a, kcode){ var r; if (!(13==kcode)){return;} // ↑「13:Enterキー」でない場合は「return」 r = a; r = r.replace(/([^0-9\.\-\/\*\+\(\)eE ]+)/g, ' '); r = r.replace(/[ \+\-]*([\+\-])/g, '$1'); r = r.replace(/([eE]+)/g, 'e'); r = r.replace(/([eE][^\+\-0-9]+)/g, ' '); r = r.replace(/(\.+)/g, '.'); // r = r.replace(/([0-9]+\.?|[0-9]*\.[0-9]+)([eE][\+\-]?[0-9]+)/g, '$1$2'); r = r.replace(/([\*\/])[ \+\-\*\/]*([\+\-])[ ]*([0-9\.]+[eE][\+\-]?[0-9]+|\()/g, '$1$2$3'); r = r.replace(/[\+\-][ \+\-\*\/]*([\+\-])[ ]*([0-9\.]+[eE][\+\-]?[0-9]+|\()/g, '$1$2'); r = r.replace(/(^|\()[ \+\-\*\/]*([\+\-])/g, '$1$2'); r = r.replace(/(^|\()[ \+\-\*\/]*[\*\/]/g, '$1'); r = r.replace(/([\+\-\*\/])[ \+\-\*\/]*[\*\/][ ]*([0-9\.]+[eE][\+\-]?[0-9]+|\()/g, '$1$2'); r = r.replace(/([ ]+)/g, ' ');try{ alert(r+' = '+eval(r)); }catch(ex){ alert(ex.message+' : '+r); } } // --> </script> </body> </html>
お礼
ありがとうございます。 r = r.replace(/([^0-9\.\-\/\*\+\(\)eE ]+)/g, ' '); を見ていると乗算なども入っていそうなので、提示した機能は入ったうえでかつ小数点の問題を解決する整数化を行っても問題ないエスケープ処理にしていただいたという認識でよいでしょうか? これに変えてから https://qiita.com/k_moto/items/0b576a3351b77fb0aa98 上記の整数化の処理を導入すれば、ライブラリを使わなくて、evalでも浮動小数点の問題を解決した計算ができるようになると考えてよいでしょうか?
- amanojaku1
- ベストアンサー率54% (265/488)
>ご回答ありがとうございます。 >おっしゃる通りそちらと同じです。 >回答No.4 amanojaku1 セキュリティ対策の正規表現を浮動小数点の四則演算に対応させるには下記のようにして下さい。 <head> <meta http-equiv="Content-Type" content="text/html; charset=Shift-JIS"> <TITLE>test</TITLE> </head> <body> <input type="text" name="gakunen" value="" onKeyPress="Calculator(this.value, event.keyCode)"> <script type="text/javascript"> <!-- function Calculator(a, kcode){ var r; if (!(13==kcode)){return;} // ↑「13:Enterキー」でない場合は「return」 r = a; r = r.replace(/([^0-9\.\-\/\*\+\(\)eE ]+)/g, ' '); r = r.replace(/(\.+)/g, '.'); r = r.replace(/([\+\-]*\+)/g, '+'); r = r.replace(/([\+\-]*\-)/g, '-'); r = r.replace(/([eE]+)/g, 'e'); r = r.replace(/([eE][^\+\-0-9]+)/g, ' '); // r = r.replace(/([0-9]+\.?|[0-9]*\.[0-9]+)([eE][\+\-]?[0-9]+)/g, '$1$2'); r = r.replace(/([\*\/])[ \+\-\*\/]*([\+\-])[ ]*([0-9\.]+[eE][\+\-]?[0-9]+|\()/g, '$1$2$3'); r = r.replace(/[\+\-][ \+\-\*\/]*([\+\-])[ ]*([0-9\.]+[eE][\+\-]?[0-9]+|\()/g, '$1$2'); r = r.replace(/(^|\()[ \+\-\*\/]*([\+\-])/g, '$1$2'); r = r.replace(/(^|\()[ \+\-\*\/]*[\*\/]/g, '$1'); r = r.replace(/([\+\-\*\/])[ \+\-\*\/]*[\*\/][ ]*([0-9\.]+[eE][\+\-]?[0-9]+|\()/g, '$1$2'); r = r.replace(/([ ]+)/g, ' ');try{ alert(r+' = '+eval(r)); }catch(ex){ alert(ex.message+' : '+r); } } // --> </script> </body> </html>
- mitoneko
- ベストアンサー率58% (469/798)
「浮動小数点の問題」とは、2進浮動小数点演算における誤差の話でしょうか?それなら、本質的に、無理です。 浮動小数点で演算を行っている限り、10進と2進の基数の違うに伴う演算誤差が発生するのは本質的な問題で、これを工夫無しに解消することは出来ません。 そして、evalは、浮動小数点演算を工夫無しに使用します。その結果を、評価するためには、入力された数式をプログラム上で理解する必要があり、そのためには、入力された数式をパース(構文解析)する必要があります。 既存ライブラリーの利用ならmath.js(http://techc.omorita.com/topics/mathjs/)を使えば、かなり解消できるかしら?
- amanojaku1
- ベストアンサー率54% (265/488)
>回答No.3 amanojaku1 >ただし、evalの場合、セキュリティ上の問題があるので、想定外の文字がある場合はエラーにする必要があります(詳細は下記ページ参照)。 > >https://okwave.jp/qa/q9369932.html ↑これはエラー・チェックではなく、手抜きして想定外の文字を削除しているだけです。 この正規表現は整数の四則演算しか想定してないので、浮動小数点演算に対応させるには少し変更が必要です。
お礼
ご回答ありがとうございます。 おっしゃる通りそちらと同じです。 こちらでセキュリティ面はまず問題ないですが、浮動小数点の問題を解決するために、一度整数にしてから計算するとこのエスケープ処理では計算がうまくいかなくなるということでしょうか
- amanojaku1
- ベストアンサー率54% (265/488)
>回答No.2 amanojaku1 >evalで普通に浮動小数点演算できますが? ただし、evalの場合、セキュリティ上の問題があるので、想定外の文字がある場合はエラーにする必要があります(詳細は下記ページ参照)。 https://okwave.jp/qa/q9369932.html
お礼
https://codepen.io/anon/pen/NzdzJB このようにして、整数化して計算することで、基本的に問題なく小数点の計算もできているように見えます。 ただ前におっしゃっていた通りこちらだと小数点の計算と10桁くらいの計算なら問題ないが何億くらいの計算になるとまた計算がおかしくなるのでしょうか? 億単位ならほとんどだれも行わないので無視してもいいかなとも思っています。
- amanojaku1
- ベストアンサー率54% (265/488)
>javascriptで浮動小数点の問題を解決しつつevalをつかった evalで普通に浮動小数点演算できますが?(下記参照) <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=Shift-JIS"> <TITLE>test</TITLE> </head> <body> <script type="text/javascript"> <!-- v=eval("0.5*1.23e1"); console.log(v); --> </script> </body> </html>
お礼
JSで作った電卓で浮動小数点の問題を解決するのは実質不可能であれば、一つは小数点はすべて四捨五入する。 そしてもう一つ別の電卓を作ってそちらではそのままこの問題を放置するという二つのバージョンを作るしかないかなと思ったのですが、これでまず問題ないと思っていいでしょうか? あくまで一般論です。