web-dev-qa-db-ja.com

整数除算で丸める

浮動小数点を使用せずに、最も近い整数に丸めるシンプルでPython的な方法はありますか?私は次のことをしたいのですが、整数演算を使用します:

skip = int(round(1.0 * total / surplus))

==============

@ジョン:浮動小数点はプラットフォーム間で再現できません。コードが異なるプラットフォーム間でテストをパスするようにしたい場合は、浮動小数点を回避する必要があります(またはテストにハッピーなエスピロンを追加して、それが機能することを願っています)。上記はほとんど/すべてのプラットフォームで同じになるほど簡単な場合がありますが、浮動小数点を完全に回避する方が簡単なので、そのような決定はしたくありません。 「Pythonの精神ではない」とはどういう意味ですか?

24
gaefan

あなたはこれを非常に簡単に行うことができます:

(n + d // 2) // d、ここでnは被除数、dは除数です。

(((n << 1) // d) + 1) >> 1または同等の(((n * 2) // d) + 1) // 2などの代替手段は、最近のCPythonではintが古いlongのように実装されているため、遅くなる可能性があります。

シンプルなメソッドは、3つの変数アクセス、1つの定数ロード、3つの整数演算を実行します。複雑なメソッドは、2つの変数アクセス、3つの定数ロード、4つの整数演算を実行します。整数演算は、関連する数値のサイズに応じて時間がかかる可能性があります。関数ローカルの変数アクセスには、「ルックアップ」は含まれません。

あなたが本当にスピードに夢中なら、ベンチマークをしてください。そうでなければ、KISS。

36
John Machin
skip = (((total << 1) // surplus) + 1) >> 1

左に1ビットシフトすると、実質的に2倍になり、右に1ビットシフトすると、2で切り捨てられます。真ん中に1を追加すると、結果が小数部.5を超える場合、「切り捨て」が実際に切り上げられます。

基本的には書いたのと同じです...

skip = int((1.0*total/surplus) + 0.5)

すべてが2で乗算され、後で2で除算される場合を除いて、これは整数演算で実行できるものです(ビットシフトは浮動小数点を必要としないため)。

6
Amber

zhmyh's answer 回答に触発され、

q, r = divmod(total, surplus)
skip = q + int(bool(r)) # rounds to next greater integer (always ceiling)

、私は次の解決策を思いつきました:

q, r = divmod(total, surplus) 
skip = q + int(2 * r >= surplus) # rounds to nearest integer (floor or ceiling)

OPがnearest整数への丸めを要求したため、zhmhsの解は実際にはわずかに正しくありません。常にnextに丸められるためです。より大きい整数、ただし私のソリューションは要求どおりに機能します。

(私の回答がzhmhの回答の編集またはコメントであるほうがよいと思われる場合は、提案された編集が拒否されたことを指摘しておきます。コメントするにはまだ十分な評判がありません!)

divmodがどのように定義されているのか疑問に思う場合:その ドキュメント に従って

整数の場合、結果は(a // b, a % b)と同じです。

したがって、OPからの要求に応じて、整数演算を使用します。

6
Daniel K

さらに別の面白い方法:

q, r = divmod(total, surplus)
skip = q + int(bool(r))
2
rmnff

分割する前に、単純に丸め規則に注意してください。最も単純な切り上げの場合:

if total % surplus < surplus / 2:
    return total / surplus
else:
    return (total / surplus) + 1

適切な四捨五入を行う必要がある場合は、少し調整してください。

0
hobbs