2011-12-31 12 views
4

私のプラットフォームでは、unsigned long longは64ビット(8バイト)です。私は2つのそのような変数があるとします。2つの(64ビット)整数から信頼できる整数パーセンテージ比を取得

unsigned long long partialSize; 
unsigned long long totalSize; 
//somehow determine partialSize and totalSize 

私は確実partialSizetotalSizeであるどのように多くの割合(近くの整数に丸め)を決定することができますどのように? (可能であれば、前者が後者よりも少ないと仮定する必要がないなら、私はこの仮定を本当にしなければならないといいですが、それは問題ありません。 - 負)。

たとえば、次のコードは完全に防弾ですか?私の恐れは、ある種の丸め、キャスト、または変換エラーが含まれているため、ある条件下では比率が低下する可能性があります。

unsigned long long ratioPercentage 
    = (unsigned long long)(((double)partialSize)/((double)totalSize) * 100.0); 
+1

非常に極端な場合には、簡単な方法が「うまくいかない」ということは間違いありません。これは64ビット - > 53ビットの2つのレベルの丸めと精度の低下があるからです。 – Mysticial

+1

@Mysticial。結果は、たかだか7ビットの精度しか必要としない。私は64→53はまったく問題ではないと言っています。 – kennytm

+0

あなたの興味を引くかもしれない2つのリンク:1)http://kanooth.com/numbers/ 2)http://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic – paulsm4

答えて

4

完全に防弾ではありません。 double仮数は53ビット(52 + 1暗黙的)なので、数字が2^53より大きい場合、doubleへの変換は一般に丸め誤差を導入します。ただし、丸め誤差は数値そのものに比べて非常に小さいので、整数値になる計算の割合が変換よりも不正確になります。

おそらくさらに重大な懸念は、これが常に下に丸くなることです。 totalSize = 1000partialSize = 99の場合は、より近い値10ではなく、9を返します。 unsigned long longにキャストする前に0.5を追加すると、より良い丸めを得ることができます。

(最終結果がオーバーフローしない場合)にのみ、整数演算を使用して、正確な結果を得ることができpartialSizeがあまり大きくない場合、それはかなり簡単です:

if (partialSize <= ULLONG_MAX/100) { 
    unsigned long long a = partialSize * 100ULL; 
    unsigned long long q = a/totalSize, r = a % totalSize; 
    if (r == 0) return q; 
    unsigned long long b = totalSize/r; 
    switch(b) { 
     case 1: return q+1; 
     case 2: return totalSize % r ? q : q+1; // round half up 
     default: return q; 
    } 
} 

簡単に修正あなたが床をしたい場合は、天井やラウンドハーフトゥーイーブン。 totalSize >= 100ULLONG_MAX/100 >= partialSize % totalSize場合

、それが他のケースでは、より手間のかかる取得、

unsigned long long q0 = partialSize/totalSize; 
unsigned long long r = partialSize % totalSize; 
return 100*q0 + theAbove(r); 

大丈夫です、私はそれをやってに熱心ではないんだけど、あなたがそれを必要とする場合、私は説得することができます。

+0

+1。私はこれをテストしていませんが、実行可能に見え、さまざまな丸め動作に対応しています。 – Mysticial

+0

しかし、ラウンドハーフアップとオーバーフローのリスクのない数値の場合、より簡単で高速です。 –

2

単一の数式は、常にオーバーフローしたり、クラッシュしたり、いくつかの値に大きな間違いを与えます。
この組み合わせは、ほとんど常にうまく機能:

if (totalSize > 1000000) { 
    pct = partialSize/(totalSize/100); 
} else { 
    pct = (partialSize*100)/totalSize; 
} 

partialSizeがMAX_U_LONG_LONG/100よりも大きく、totalSizeはこの場合は1000000以下で、正しい割合はそう、100%よりはるかに大きい場合にのみ失敗します。それはあまり面白くない。

+0

すばらしいアドバイス。しかし、あなたはバイアスを逃しています:これは丸めの代わりにゼロに向かって切り捨てられます。 –

+0

実際、このコードは正確に正しく切り詰められません。さらに悪いことに、partialSize /(totalSize/100)ロジックはいくらかのパーシッションを失います。しかし、この質問は「最も近い整数に丸められた」と言っています。 – ugoren

3

最も近い丸めに必要な+0.5が省略されているため、数式が正しくないことに注意してください。

だから私は、この修正式と仮定進みましょう:私はコメントで述べてきたように、単純なが、正確な結果を四捨五入することは保証されませんが、ストレートフォワード方式は、

(unsigned long long)(((double)partialSize)/((double)totalSize) * 100.0 + 0.5); 

を。だからあなたの直感は弾丸ではないという点で正しいです。

大部分の場合でも正しいですが、正しく丸められない境界線の小さなセットがあります。それらの問題があなた次第であるかどうか。しかし、大抵の場合、通常はストレートフォワードの方法で十分です。

それが失敗することがあり理由:丸めの4つのレベルがあります

。 100

  • 最終鋳造により> 53ビット
  • 分割
  • 乗算 -

    1. ザは、64ビットをキャスト(Iコメントで言及2から修正)。

    丸めのソースが複数ある場合はいつも、通常の浮動小数点エラーの原因があります。

    カウンターの例:

    850536266682995018/3335436339933313800 // Correct: 25% Formula: 26% 
    3552239702028979196/10006309019799941400 // Correct: 35% Formula: 36% 
    1680850982666015624/2384185791015625000 // Correct: 70% Formula: 71% 
    

    はソリューション:

    珍しいが、私はストレートフォワード式が間違って丸められた結果を与えるいくつかの例をリストアップします

    私はarbitrary precision arithmeticを使用する以外に、これに対してクリーンな100%防弾ソリューションを考えることはできません。

    最終的には、常に完全に丸くする必要がありますか?


    EDIT:小さい番号について

    は、ここに0.5に切り上げ非常に簡単な解決策があります:

    return (x * 100 + y/2)/y; 
    

    これは限りx * 100 + y/2がオーバーフローしないように動作します。

    @Daniel Fischer答えは、他の丸めの振る舞いについてより包括的な解を持っています。丸めようとするにはこれを修正するのは難しいことではありません。

  • +0

    私の数字が2^53未満であることが分かったらどうなりますか?正確な結果が得られますか? (私はファイルサイズを扱っていますが、私の場合はファイルがそれほど大きくないでしょう) – pf85

    +1

    あなたの数字が2^53未満なら、それはポイント1を削除します。私はどちらかといえば、浮動小数点の丸めが0.5(または整数)の境界を越えて結果をプッシュするが、実際の問題の「xx.5±ε」のパーセンテージのために誤って丸められた結果の場合そうであれば、私の答えに追加した整数メソッドは、オーバーフローが問題ではないので、正確に丸められた結果を保証します。 –

    +0

    +0.5はハックのようです。代わりにこれはどうでしょうか? - > 'static unsigned int d(unsigned long long p、unsigned long long t){return round((double)p * 100/t); } ' –

    関連する問題