Pythonは次に高い10の累乗に丸めます


44

math.ceil数値が次に高い10の累乗に割り当てられるようにするにはどうすればよいですか?

# 0.04  ->  0.1
# 0.7   ->  1
# 1.1   ->  10  
# 90    ->  100  
# ...

私の現在の解決策は入力数の範囲をチェックする辞書ですが、それはハードコードされており、1行の解決策を好みます。多分私はここで単純な数学的トリックまたは対応する派手な関数を逃していますか?


3
@boldは、これらのソリューションが上から機能10するように見えますlog10。これには、たとえばが必要になります。
jonrsharpe

3
あなたが望む言葉は「力」です。たぶんあなたはあなたの母国語の言葉に対して間違った翻訳を得ました。
user2357112は

ありがとう、モニカ!@bold:この質問を見つけましたが、別の問題です。Jonrsharpeが完璧な答えを提供してくれました
offeltoffel

2
これは桁数にも関係します。1 0次、10、100等、2次であり、1次である
wjandrea

回答:


60

これを行うには、math.ceilwith math.log10を使用できます。

>>> 10 ** math.ceil(math.log10(0.04))
0.1
>>> 10 ** math.ceil(math.log10(0.7))
1
>>> 10 ** math.ceil(math.log10(1.1))
10
>>> 10 ** math.ceil(math.log10(90))
100

log10(n)は、xを満たす解を与える10 ** x == nのでx、切り上げると、次に高い10のべき乗の指数が得られます。

音符値のそれ既に整数である「10の次に高いパワー」であろう。nxn

>>> 10 ** math.ceil(math.log10(0.1))
0.1
>>> 10 ** math.ceil(math.log10(1))
1
>>> 10 ** math.ceil(math.log10(10))
10

1
ログ機能を使用することは、私が思いつかなかったトリックのようです。これがまさに私が望んでいたことだと思います!
どうも

2
注:希望する動作に応じて、これは10の累乗では機能しません。たとえば10 ** math.ceil(math.log10(1)) == 1、「次の最高の累乗」ではありません
Cireo

5
注:この回答は浮動小数点演算に依存しているため、丸めエラーが原因で失敗する可能性があります。たとえば、1000000000000001を入力してみてください。
プラグウォッシュ

2
@plugwashは必ずしも必要ではありません。数学関数は、たとえばdecimal.Decimalsも受け入れます。
jonrsharpe

5
はい、他の型を渡すことができますが、それらは倍精度浮動小数点数に変換され、Cの「log10」関数に渡されます。大量のログのオーバーフローを防ぐ特別なケースがありますが、丸めエラーを防ぐものはありません。
プラグウォッシュ

21

あなたの問題は十分に特定されておらず、一歩下がっていくつかの質問をする必要があります。

  • 入力はどのタイプですか?
  • 出力にどのタイプを使用しますか?
  • 1未満の結果の場合、正確に何に丸めたいですか?10の実際の累乗または10の累乗の浮動小数点近似が必要ですか?あなたは、10の負の累乗が浮動小数点で正確に表現できないことに気づいていますか?ここでは、10のべき乗の浮動小数点近似が必要であると仮定します。
  • 入力が正確に10の累乗(または10の累乗の最も近い浮動小数点近似)である場合、出力は入力と同じですか?それとも、次の10のべき乗か?「10-> 10」または「10-> 100」?今のところ前者を想定しましょう。
  • 入力値は、問題のタイプの可能な値にすることができますか?またはそれらはより制約されていますか?

別の回答では、対数を取り、次に切り上げ(天井関数)、次にべき乗することが提案されました。

def nextpow10(n):
    return 10 ** math.ceil(math.log10(n))

残念ながら、これには丸め誤差があります。まず最初に、nはたまたまあるデータ型から倍精度浮動小数点数に変換され、丸め誤差が発生する可能性があります。次に、対数が計算され、内部計算と結果の両方でより多くの丸め誤差が発生する可能性があります。

そのため、誤った結果が出る例を見つけるのに長い時間はかかりませんでした。

>>> import math
>>> from numpy import nextafter
>>> n = 1
>>> while (10 ** math.ceil(math.log10(nextafter(n,math.inf)))) > n:
...     n *= 10
... 
>>> n
10
>>> nextafter(n,math.inf)
10.000000000000002
>>> 10 ** math.ceil(math.log10(10.000000000000002))
10

理論的には、他の方向に失敗する可能性もありますが、これを引き起こすのははるかに難しいようです。

したがって、floatとintの堅牢なソリューションでは、対数の値は近似値であると想定する必要があるため、いくつかの可能性をテストする必要があります。の線に沿った何か

def nextpow10(n):
    p = round(math.log10(n))
    r = 10 ** p
    if r < n:
        r = 10 ** (p+1) 
    return r;

私は、このコードが実用的な範囲のマグニチュードですべての引数に対して正しい結果を与えるはずだと思います。浮動小数点への変換に問題があるため、非常に少数または非常に多数の非整数型および非浮動小数点型では壊れます。Pythonは、オーバーフローを防止するためにlog10関数の整数引数を特殊なケースで処理しますが、十分に大きな整数を使用しても、丸めエラーが原因で誤った結果を強制する可能性があります。

2つの実装をテストするために、次のテストプログラムを使用しました。

n = -323 # 10**-324 == 0
while n < 1000:
    v = 10 ** n
    if v != nextpow10(v): print(str(v)+" bad")
    try:
        v = min(nextafter(v,math.inf),v+1)
    except:
        v += 1
    if v > nextpow10(v): print(str(v)+" bad")
    n += 1

これにより、単純な実装では多くの障害が見つかりますが、改善された実装では見つかりません。


ここでさらに詳しく説明していただき、ありがとうございます。jonrsharpeの回答はすでに私の問題を解決していますが、この回答は、類似しているがより具体的な質問を持つ他のユーザーに役立つ場合があります。
offeltoffel

1
round代わりになぜ使用するのmath.ceilですか?これにより、r < ntrueである多くの不要なケースが発生し、追加の作業を実行する必要があります。
a_guest

1
ログがどちらの方向にもずれている可能性があるためです。
11:47にプラグウォッシュ

1
「改善」コードが、ラウンドでハイエンドにローエンドと100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000に1E-317のための障害でmath.ceil結果によって置き換えを使用。
プラグウォッシュ

1
(実際にはおそらくそれでも大丈夫です)
プラグウォッシュ

3

次の10の累乗のほうが望ましいと思われます。ここでは、純粋な数学を使用し、ログを使用せずに再帰を使用する方法を示します。

def ceiling10(x):
    if (x > 10):
        return ceiling10(x / 10) * 10
    else:
        if (x <= 1):
            return ceiling10(10 * x) / 10
        else:
            return 10
for x in [1 / 1235, 0.5, 1, 3, 10, 125, 12345]:
    print(x, ceiling10(x))

これをテストしたところ、ほとんどの実際的なケースではうまく機能するように思われますが、十分に小さな入力で丸め誤差が発生するようです。ceiling10(1e-6)は1.0000000000000002e-06を返します
プラグウォッシュ

0
y = math.ceil(x)
z = y + (10 - (y % 10))

このような何か?それは私の頭の上にありますが、ターミナルでいくつかの数字を試したときにうまくいきました。


0

これをチェック!

>>> i = 0.04123; print i, 10 ** len( str( int( i ) ) ) if int( i ) > 1  else 10 if i > 1.0 else 1 if i > 0.1 else  10 ** ( 1 - min( [ ("%.100f" % i ).replace('.','').index( k ) for k in [ str( j ) for j in xrange( 1, 10 ) if str( j ) in "%.100f" % i  ] ]  ) )               
0.04123 0.1
>>> i = 0.712; print i, 10 ** len( str( int( i ) ) ) if int( i ) > 1  else 10 if i > 1.0 else 1 if i > 0.1 else  10 ** ( 1 - min( [ ("%.100f" % i ).replace('.','').index( k ) for k in [ str( j ) for j in xrange( 1, 10 ) if str( j ) in "%.100f" % i  ] ]  ) )                 
0.712 1
>>> i = 1.1; print i, 10 ** len( str( int( i ) ) ) if int( i ) > 1  else 10 if i > 1.0 else 1 if i > 0.1 else  10 ** ( 1 - min( [ ("%.100f" % i ).replace('.','').index( k ) for k in [ str( j ) for j in xrange( 1, 10 ) if str( j ) in "%.100f" % i  ] ]  ) )                   
1.1 10
>>> i = 90; print i, 10 ** len( str( int( i ) ) ) if int( i ) > 1  else 10 if i > 1.0 else 1 if i > 0.1 else  10 ** ( 1 - min( [ ("%.100f" % i ).replace('.','').index( k ) for k in [ str( j ) for j in xrange( 1, 10 ) if str( j ) in "%.100f" % i  ] ]  ) )                    
90 100

このコードは、の10のべき乗の原理に基づいていlen( str( int( float_number ) ) )ます。

4つのケースがあります。

    1. int( i ) > 1

    Float数値-に変換されint、その後それstr()から文字列に変換されると、正確に見ているstringwith が得られlengthます。したがって、最初の部分は入力用ですi > 1.0- 10この長さの10 乗です。

    1. &3.少し分岐:i > 1.0およびi > 0.1<=>あり101それぞれです。
    1. そして最後の場合、次の場合i < 0.1:ここでは、10は負のパワーになります。コンマの後に最初のゼロ以外の要素を取得するために、私はそのような構成を使用しました。("%.100f" % i ).replace('.','').index( k )ここで、kは[1:10]間隔にわたって実行されます。その後、結果リストを最小限にします。そして、1ずつ減少します。これは最初のゼロであり、カウントされます。また、interval index()からゼロ以外の要素が少なくとも1つも見つからない場合、ここで標準のPython がクラッシュする可能性があります[1:10]。そのため、結局、発生によってリストを「フィルタリング」する必要がありますif str( j ) in "%.100f" % i。さらに、より深く正確にするために- %.100f異なる場合があります。

説明を追加してください。
Mobin Ranjbar
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.