質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

解決済

3回答

2842閲覧

計算して得たBMIの値によって出力内容を変えたいが、比較に使用する条件判定用の数字が浮動小数点だと動作が変わってしまいます。

orange_pencil

総合スコア7

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

0グッド

1クリップ

投稿2018/04/04 22:29

環境

Windows10
Anaconda5.0.1
Python3.6.5
XAMPPを利用してのローカル環境でのテストとなってます。

発生している問題・エラーメッセージ

割り出されたBMIをもとに「痩せすぎ」「普通」「肥満」などの分類も表示しようと思ったのですが、これが想定通りに動作しません。
フォームに height(身長)「100」(cm表記です), weight(体重)「10」と入力した場合、
BMI[10.0] Status[痩せすぎ]
という結果を想定していたのですが実際には
BMI[10.0] Status[痩せ]
と出力されます。BMIが1.0でも3.5でも同様で「痩せすぎ」が出力されるほしい部分すべてが「痩せ」と出力されてしまいます。
しかし他の「痩せ気味」「普通」「前肥満」、以降すべての要素は正しい結果出力がなされます。
問題はソースコード二つ目(bmi.py)の、辞書型を参照している変数 bmi_status にあると思うのですがなぜそのような動作になるのかがわかりません。

該当のソースコード

html

1<--index.html--> 2<html lang="ja"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Brython test</title> 6 <script type="text/javascript" src="./brython_files/brython.js"></script> 7 <script type="text/javascript" src="./brython_files/brython_stdlib.js"></script> 8 </head> 9 10 <body onload="brython()"> 11 <script type="text/python" src="./bmi.py"></script> 12 <h3>BMIを計算します。</h3> 13 <input type="text" id="height" placeholder="height [cm]" /> 14 <br><input type="text" id="weight" placeholder="weight [kg]" /> 15 <br><button id="execute">計算!</button> 16 17 <br><input id="result"> 18 </body> 19</html>

python

1# bmi.py 2# coding: utf-8 3from browser import document 4 5 6def calc_bmi(): 7 height = float(document["height"].value) / 100.0 8 weight = float(document["weight"].value) 9 10 bmi = weight/(height*height) 11 bmi_status = { 12 15.99: "痩せすぎ", 13 16.99: "痩せ", 14 18.49: "痩せぎみ", 15 24.99: "普通", 16 29.99: "前肥満", 17 34.99: "肥満(1度)", 18 39.99: "肥満(2度)" 19 } 20 21 status_text = "" 22 for status in bmi_status.keys(): 23 if bmi <= float(status): 24 status_text = bmi_status[status] 25 break 26 else: 27 status_text = "肥満(3度)" 28 res = document["result"] 29 res.value = "BMI[{0}] Status[{1}]".format(str(round(bmi, 3)), status_text) 30 31excute_button = document["execute"] 32excute_button.bind("click", calc_bmi) 33

試したこと

まずブラウザのキャッシュで変更が適用されてないのかと思いキャッシュの削除、ブラウザの再起動、複数のブラウザ(Google Chrome, Firefox, Edge)での動作確認をしましたがどれも結果は同様でした。

次に変数bmi_statusを次のように書き換えたところ正しく「痩せすぎ」の」判定が出力されました。
(それに伴い、bmi.pyの判定部分を「if bmi < float(status):」に書き換えました。)

python

1bmi_status = { 2 16.00: "痩せすぎ", 3 17.00: "痩せ", 4 18.50: "痩せぎみ", 5 25.00: "普通", 6 30.00: "前肥満", 7 35.00: "肥満(1度)", 8 40.00: "肥満(2度)" 9 }

しかし気になったので上記の変数の書き換えを行わず判定部分だけ取り出し少し変更を加えてターミナルから実行したところ、こちらは意図した結果の「痩せすぎ」が得られました。コードは以下です。

python

1# bmi_test.py 2# coding: utf-8 3 4def calc_bmi(height, weight): 5 weight = float(weight) 6 height = float(height) / 100.0 7 8 bmi = weight/(height*height) 9 bmi_status = { 10 15.99: "痩せすぎ", 11 16.99: "痩せ", 12 18.49: "痩せぎみ", 13 24.99: "普通", 14 29.99: "前肥満", 15 34.99: "肥満(1度)", 16 39.99: "肥満(2度)" 17 } 18 status_text = "" 19 for status in bmi_status: 20 print(status) 21 if bmi <= status: 22 status_text = bmi_status[status] 23 break 24 else: 25 status_text = "[肥満(3度)]" 26 27 print(status_text) 28 29 30if __name__ == "__main__": 31 calc_bmi(100, 10)

これはどういった理由で「該当のソースコード」で示したコードでは意図した動作にならないのでしょうか。
何か答えになりそうなことに心当たりがございましたらご教示お願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

dictのkeysは順番を保証しません。もちろん呼べば何らかの順番のイテレータが返る訳ですが、質問のコードは順番通りのイテレータを期待していると思うので、問題が起こるでしょう。意図しないタイミングでifの判定がTrueになってしまいます。

参考の実行例:

python

1>>> bmi_status = {16.00: "痩せすぎ", 17.00: "痩せ", 18.50: "痩せぎみ", 25.00: "普通", 30.00: "前肥満", 35.00: "肥満(1度)", 40.00: "肥満(2度)"} 2>>> for k in bmi_status.keys(): 3... print(k) 4... 516.0 617.0 718.5 835.0 940.0 1025.0 1130.0

今回の例なら解決策は簡単です。ソートしてあげてください。

python

1>>> for k in sorted(bmi_status.keys()): 2... print(k) 3... 416.0 517.0 618.5 725.0 830.0 935.0 1040.0

投稿2018/04/04 22:40

hayataka2049

総合スコア30933

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

orange_pencil

2018/04/04 23:13

今まさに「該当のソースコード」に提示した浮動小数点まみれの辞書型bmi_statusをソートして実行したところ、期待通りの動作をしてくれました。 自分が正常に動作すると思っていたコードもキーを全部出力して順番を確認してみれば、おっしゃる通りで「偶然」意図していた動作をしてくれるキーが最初に返っていただけでした。 一晩あれこれ試しても変わらなかったので非常にスッキリしました。 本当にありがとうございます。
hayataka2049

2018/04/04 23:23

偶然それっぽく動いたり、なんとなく再現性があるように見えたりするので、こういうのは気づくまでが厄介ですね。 それと、ソートする方法は判定の度にソートが走るので、もし無駄っぽいのが気になるとかパフォーマンス上問題になるようでしたらOrderedDictというものを使ってください。標準ライブラリのcollectionsモジュールにそういうものがあるらしいので。
orange_pencil

2018/04/05 00:03

わざわざ補足の情報まで…! 今回痛い目を見たので他の基本的なデータ型などについてももう一度きちんと復習しようと決めました。 目先の問題だけでなく、ほかの代わりになる手段も親切に教えていただいて頭が上がりません。 重ねてになりますが、本当にありがとうございました。
guest

0

bmi_statusを無理にdictにせず、[閾値, status]のlistにしてみてはどうでしょうか?

投稿2018/04/08 16:06

arch_

総合スコア158

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

浮動小数点によるよくある誤差のはなしですね
人間が扱うのは10進数ですが、コードの中では2進数で扱われます。
これが整数ならいいんですが、小数点以下の数となると、10進数は正確に2進数に変換できません。
ここらへんは「浮動小数点 誤差」でぐぐれば説明が出てきますね

投稿2018/04/04 22:33

y_waiwai

総合スコア87774

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

orange_pencil

2018/04/04 23:01

ありがとうございます。 今しがたちょっと調べた程度でまるで理解できていませんが、15.99なんかという値では何かしら問題を引き起こす可能性があるのは十分に察しがつきました。 今後注意したいと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問