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

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

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

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

2444閲覧

Pythonで有効数字を考慮した計算をする

ssm

総合スコア1

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2020/10/20 05:59

編集2020/10/21 00:02

前提・実現したいこと

計算の際に有効数字を考慮したく、一応は目的の動作をするものが書けたもののプログラムに欠陥がないか、またより洗練された実装方法がないか知りたいです。
加えて、例えば3桁の有効数字を考える場合に1.2ではなく1.20と表示する、0.01ではなく0.0100と表示するなど、表示方法についても洗練させられればと考えています。

該当のソースコード

Python

1def sig_digits(x, n): 2 #xの桁数を取得する 3 import math 4 if x==0: 5 return 0 6 else: 7 digits=math.floor(math.log10(abs(x)))+1 8 #n桁に丸める 9 return round(x, -digits+n)

試したこと

負の小数なども含め、大体の数字はこのプログラムで一応正しく丸められました。

追記:
いただいた回答やコメントを参考にプログラムを書き換えました。このコードではうまくいかない事例などありましたらご教示願います。

Python

1import math 2import decimal 3def sig_digits(x, n): 4 if x==0: 5 return 0 6 else: 7 digits=math.floor(decimal.Decimal(abs(x)).log10())+1 #桁数を取得する 8 x=decimal.Decimal(x) #十進数に直す 9 return round(x, -digits+n) #n桁に丸める 10sig_digits(35.089667,5)

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

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

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

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

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

guest

回答1

0

ベストアンサー

有効数字って、十進表記することを前提に決められていますよね?
それを浮動小数で表現するのには限界があります。decimalモジュールなどを利用しましょう。

Python

1import decimal 2 3with decimal.localcontext() as ctx: 4 ctx.prec = 3 # 有効桁数を設定 5 ctx.rounding = decimal.ROUND_HALF_UP # いわゆる四捨五入を丸め方に設定 6 7 # 有効桁数は即座に適用される 8 n = ctx.create_decimal('0.2345') 9 print(n) # => 0.235

参考: decimal --- 十進固定及び浮動小数点数の算術演算 — Python 3.9.0 ドキュメント

加えて、例えば3桁の有効数字を考える場合に1.2ではなく1.20と表示する、0.01ではなく0.0100と表示するなど、表示方法についても洗練させられればと考えています。

これについては、簡便な方法が無いか検討中です。

Python

1with decimal.localcontext() as ctx: 2 ... 3 n += 1 4 print(n) # => 1.24 5 6 n -= 1 7 print(n) # => 0.24 これが 0.240 にならないか

投稿2020/10/20 06:41

編集2020/10/20 07:09
LouiS0616

総合スコア35660

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

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

LouiS0616

2020/10/20 07:30

よく考えたら 1.24 - 1.00 の有効桁数が2ケタになるのは妥当なのか...? 科学系の出身では無いのでちょっと確証付かないですが、そんな気がします。
ssm

2020/10/20 11:18

大変ご丁寧にありがとうございます! decimalモジュール、あまり馴染みがありませんでしたが、float型で計算していると誤差が蓄積されていってしまうのですね。 表示については、format関数などを使ってなんとかできないかなと考えているので、時間のある時にもう少し考えてみます。 加減算では、お示しいただいた例のように場合によって有効桁数が落ちるので、加減算のみの計算では今回作ったような有効桁数を指定する関数を使うよりも、何の位まで計算するのかを指定するround関数のような関数を使ったほうが都合がいいのではないかと考えています。(大学の課題でPythonを使う程度なので詳しく知らないのですが、decimalを使って計算する場合はroundではなく専用の関数を用意しないといけないのでしょうか…?)
LouiS0616

2020/10/20 11:55

かっちょ悪いですけど n = ctx.create_decimal(f'{n}0000') みたくして無理やり有効桁数を回復(?)する方法はあります。 > decimalを使って計算する場合はroundではなく専用の関数を用意しないといけないのでしょうか…? Decimalは特殊メソッド__round__を持ちますので、roundの引数として利用可能です。 例えばこんなふうに。 >>> n = decimal.Decimal('3.14') >>> print(type(n), n) <class 'decimal.Decimal'> 3.14 >>> >>> m = round(n, 1) >>> print(type(m), m) <class 'decimal.Decimal'> 3.1
LouiS0616

2020/10/20 11:56

コメントを書いている途中で気付きましたが、有効数字の回復(?)はround関数を使っても可能ですね。 >>> n = decimal.Decimal('1.23') >>> n -= 1 >>> >>> print(n) 0.23 >>> print(round(n, 3)) 0.230
toast-uz

2020/10/20 12:06

xを有効桁数nで表示するのは、print(f'{x:.{n}f}') ですね
toast-uz

2020/10/20 12:15

roundだと元がDeciomalじゃないと、うまく有効数字まで表示できないかもです。
LouiS0616

2020/10/20 12:31 編集

@toast-uz さん 有効数字とは、小数点以下の桁数のことではありません。 :.{n}f で解決するのはnが0.1以上1未満のときだけです。
LouiS0616

2020/10/20 12:32

> roundだと元がDeciomalじゃないと、うまく有効数字まで表示できないかもです。 十進数で扱わないとそもそも有効数字を扱えないというのが私の回答の軸なので、Decimalを使わないという選択肢は無いです。
toast-uz

2020/10/20 12:40

じゃあ、print(f'{x:.{n}f}')でnを調整するのではなく、roundを使った方が良いのですね。お騒がせしました。
shiro-to

2020/10/20 13:52 編集

roundを使うと例えば6.240*10^2などが624.0000など目的と少し違う表示になりそうな気がします。
LouiS0616

2020/10/20 14:10

@shiro-to さん 確かに。roundは有効ですが、そのままは使えませんね。 有効数字を回復(そもそもこの発想が的外れということなのでしょうが)する為の方法があると良いのですが...
ssm

2020/10/20 15:24

皆様色々とコメントくださりありがとうございます。特にLouiS0616様、素人の質問に時間を割いて非常にご丁寧に対応くださり、感謝申し上げます。これまでいただいたコメントを参考に、プログラムを少し改良いたしました。 また、toast-uz様、私はプログラミングについては本当に初心者で、format関数も比較的最近知ったのですが、f文字列という機能は初めてお目にかかりました。また一つ新たな知識が増え、大変嬉しく思います。時間のある時に使い方をきちんと勉強させていただきたく思います。
ssm

2020/10/20 15:27

夜分遅くに失礼いたしました。
toast-uz

2020/10/20 23:31

ssm様、f表記はマイブームなので使っています。ご参考になれば。 ただし、f表記は本質ではなく、普通のformat文で小数の有効数字表記はできるということを、ご認識ください。 https://note.nkmk.me/python-format-zero-hex/ もともと有効数字表記をしたい、という命題なので、数学的な四捨五入の厳密さは必要ない場面ですよね。なので、個人的な嗜好としては、decimalは選択しない場面です。これで論文書くとかならdecimal使った方がよいかもです。 https://note.nkmk.me/python-round-decimal-quantize/
ssm

2020/10/21 00:04

toast-uz様、ありがとうございます。 実は、私がPythonを使う場面では、指数や対数が色々絡む計算をした後に最後の出力だけ四捨五入して簡便に表示したいという場面が多いので、これまでずっとfloatを使ってきたもののdecimalの方が好ましいのかなという気がしています。(decimalで対数関数などの複雑な計算をするにはどうすれば良いかまだ勉強できていないのですが…。) ただ、無闇矢鱈にdecimalを使うのではなくfloatで事足りそうな問題であればfloatで計算するなど、柔軟に使い分けていければと思います。 また、不躾な頼みですがもしお時間に余裕がございましたら私の修正後のプログラムにつきましてもご意見いただけますと幸いです。
toast-uz

2020/10/21 03:56 編集

現状のコードで問題ないと思います。なお、decimalを使う時の有効桁数の扱いは、コンテキストというdecimal組み込みの仕組みを使うのが良さそうです。表示だけでなく計算過程でも有効桁数が利用されますし、with構文で一時的にコンテキストを切り替えることもできそうです。 https://docs.python.org/ja/3/library/decimal.html#context-objects decimalの意義として、「指数や対数が色々絡む計算」ではdecimalは使わない方が良いのでは、と私は思います。decimalが役立つのは10進数で誤差ゼロの小数を求める場面です。0.1+0.1+0.1-0.3が厳密に0になって欲しい場面ですね。0.01円単位で為替取引計算するとかの場面です。しかし科学技術計算で有効数字3桁の議論をしている際に、0.4999999は切り捨てで0.5000001は切り上げの中、0.5000000を切り上げるのか切り捨てるのかを厳密判断するためにdecimalを使うのって、矛盾だと思います。
LouiS0616

2020/10/21 04:13 編集

> decimalが役立つのは10進数で誤差ゼロの小数を求める場面です。 ① 有効数字は誤差を含む計算を行う際に用いる指標です。 ② どの程度の誤差が潜在的に含まれ得るかを厳密に評価します。 ③ 有効数字は有効桁からの桁数で表現しますが、これは十進表記を前提とします。 ④ floatの計算がかなり厳密だからと言って、有効数字を無視して良い理由にはなりません。有効数字を定めた以上、寧ろ精度を超えた演算をしてはいけません。誤差の評価ができなくなります。 誤差を出さないためにDecimalを使っているのではありません。 コントロールできる範囲で誤差を出すこと、そしてそれを評価するために用います。
LouiS0616

2020/10/21 04:19

0.333333 * 1.0 を有効数字を加味して計算すると 0.33 になります。 その演算結果に 3.0 を掛けると 0.99 になります。 floatで計算して最後だけ帳尻を合わせると、同じ演算でも結果が 1.0 になってしまいます。これではいけません。
LouiS0616

2020/10/21 04:25

あまり厳密に考えず、『だいたい正しい値』を『だいたい保証できそうな桁数』で出力するだけならfloatでも構いません。
toast-uz

2020/10/21 08:47

LouiS0616様、ありがとうごさいます。確かに私の言い方は正確ではありませんね。勉強になります。 有効数字を厳密に制御したい場合、すなわち 0.333333 * 1.0 * 3.0 = 0.99 であることが必要な場合はdecimalを使い、だいたいでよく 0.333333 * 1.0 * 3.0 = 1.0 の方がよい場合はfloatを使うということですね。私は個人的には後者の場合が多いです、と言いたかったです。物理実験とかは前者なんでしょうかね。
toast-uz

2020/10/21 08:49

そうすると、decimalの場合は、有効数字の扱いにはコンテキストを活用する方が、よさそうですね。
LouiS0616

2020/10/21 09:12

ただ現実問題としては、よほど込み入った計算をしない限り(同じ値を何百回も足すなど)浮動小数の誤差が表出することは無いでしょう。また自分で言ったことを否定するようでアレですが、わざと精度を落とす必要があるほど厳密に進めなければならないケースは稀な気がします。 なんだかんだ言ってコスパを考えるとfloatで扱うのが現実解だとは思います。floatと言っても倍精度ですし。あるいはdecimalより使い良いサードパーティモジュールを放り込むか。
toast-uz

2020/10/21 09:15

しっかり解説いただき理解できたので、そういう話が制限含めてスっと入ります。ありがとうございます。
LouiS0616

2020/10/21 09:17

@ssm さん 修正後のプログラムですが、概ね良いと思います。 細かいところを突っ込むならば引数xは文字列で受け取るべきですが(数値オブジェクトを作ると2進化誤差が生じ得るので)、コメントでもやり取りがあったように細かすぎる話だったかもしれません。 別の観点のアドバイスですが、数値を指数表記(123.45 ⇒ 1.2345e2)した状態で保持するのも良いアイデアだと思います。小数部の桁数+1がイコール有効桁数になるので。
ssm

2020/10/21 11:40

LouiS0616様、toast-uz様 本当にご親切にありがとうございます。最近取り組んでいた大学の課題ではexp(21*x)のように計算過程でスケールが大幅に変わるような式を扱うこともあったのですが、計算は繰り返しても多くて20回くらいなので、floatでも特に問題ないのかなという気もしてきました。 いただいたコメントは全てじっくり理解したいところではあるのですが、大学のコマ数の関係で(私のキャパシティがないのを大学に責任転嫁しているだけですが)時間も限られているので、必要に応じて理解を深めていければと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問