2016-04-26 15 views
3

[0.01..1.2]の精度でint64_tの係数を補正したいと思います。精度は約0.01です。素朴な実装は次のようになります。精度を維持する方法int64_t = int64_t * float?

int64_t apply_correction(int64_t y, float32_t factor) 
{ 
    return y * factor; 
} 

残念ながら、私はint32factorをキャストする場合は、私はfloatyをキャストした場合のいずれかの精度を失うことになります。私はy1<<56以下その最大値を持って確保することができるかどう

しかし、私はこのトリックを使用することができます私の入力値が1<<56よりも大きくなることができるかどう

(1<<8) * (y/(int32_t)(factor * (1<<8))) 

どのように私はこの問題を解決することができますか?

プロットツイスト:

私はint64_tをエミュレートタイプとどこが倍精度のためのあらゆるサポートを持っていないで32ビットアーキテクチャ上で実行しています。アーキテクチャはAnalog DevicesのSHARCです。

+0

は 'Y *(int_64t)要因の何が問題なのですか;'? – luk32

+1

@ luk32 'factor 'が0.01-1.2の範囲にあるので、動作しません。 – fluter

+0

倍精度をサポートしていない場合は、そのライブラリを書くか、そこに倍数/倍精度のライブラリをたくさん使うことができます。しかし、倍精度のままでも53ビットの精度しかないので、助けにならないので完全な64ビット精度を得ることはできません –

答えて

3

((int64_t)1 << 57) * 100または* 256を計算すると、符号付き整数オーバーフローが発生し、コードに未定義の動作が発生します。代わりにuint64_tとその値を使用した場合、コードは明確に定義されていますが、定義どおりに動作しません。


しかし、ほとんどの場合、この数字を(1 << 63/1.2)まで上げることは可能です。

uint64_tの場合は、元の数を右に32シフトし、最下位の32ビットに(int32_t)(factor * (1 << 8))を掛けることができます。

次に、乗算の後に8で右シフトしませんが、24で左シフトします。その後、一緒に追加します。

uint64_t apply_uint64_correction(uint64_t y, float32_t factor) 
{ 
    uint64_t most_significant = (y >> 32) * (uint32_t)(factor * (1 << 8)); 
    uint64_t least_significant = (y & 0xFFFFFFFFULL) * (uint32_t)(factor * (1 << 8));  
    return (most_significant << 24) + (least_significant >> 8); 
} 

さて、apply_uint64_correction(1000000000000, 1.2)1199218750000につながる、とapply_uint64_correction(1000000000000, 1.25)1250000000000ことになります。


あなたがfactorの範囲を保証することができれば実際にあなたがそれからより多くの精度を行うことができます。

uint64_t apply_uint64_correction(uint64_t y, float32_t factor) 
{ 
    uint64_t most_significant = (y >> 32) * (uint32_t)(factor * (1 << 24)); 
    uint64_t least_significant = (y & 0xFFFFFFFFULL) * (uint32_t)(factor * (1 << 24));  
    return (most_significant << 8) + (least_significant >> 24); 
} 

apply_uint64_correction(1000000000000, 1.2)を自分のコンピュータ上1200000047683を与えるだろう。これは、float32_tに24ビットの仮数がある場合は、最大精度です。


上記のアルゴリズムは、同様に署名した正の数のために働くだろうが、負の数のために署名したシフトが、私はuint64_tに値を変換した後、記号のノートを取ると思い灰色の領域であるため、移植性の計算を行います元の符号が負の場合は否定します。

int64_t apply_correction(int64_t y, float32_t factor) { 
    int negative_result = 0; 
    uint64_t positive_y = y; 
    if (y < 0) { 
     negative_result = 1; 
     positive_y = -y; 
    } 

    uint64_t result = apply_uint64_correction(positive_y, factor); 
    return negative_result ? -(int64_t)result : result; 
} 
+0

最も重要な部分の残りの部分はどうなりますか? –

+0

@WeatherVaneがショートカットを実行する:D –

+0

@ WedeVaneが修正されました –

3

整数空間ではどうでしょうか?

/* factor precision is two decimal places */ 
int64_t apply_correction(int64_t y, float32_t factor) 
{ 
    return y * (int32_t)(factor * 100)/100; 
} 

これはyが最大値に非常に近いではありませんが、それは56ビットよりも少し近いあなたを取得前提としません。

+0

整数オーバーフロー。 –

+0

この 'int32_t(factor * 100)'はCではコンパイルされません。 – alk

+1

@AnttiHaapala:int64_tに最大1.2を掛けて同じ型として返すので、整数オーバーフローが可能です。私は特定の警告について言及するために私の答えを更新しました。 –

2

ただ浮動小数点数を使用しないでください。

int64_t apply_correction(int64_t y, float32_t factor) 
{ 
    int64_t factor_i64 = factor * 100f; 

    return (y * factor_i64)/100ll; 
} 

これはy * factor_i64 * 100がオーバーフローしないことを想定しています。

+0

これは 'long long'がシステム上で利用可能であると想定していませんか? – Qix

+0

@ Qixはい、確かに、標準Cが使用されていると仮定しています。 '(int64_t)0'と書くこともできます。いずれにしても、バランシングによる暗黙的なタイプのプロモーションのために、それは本当に重要ではありません。つまり、 '(y * factor)/ 100;'は等価で安全です。私は型を明示するのが好きです。 – Lundin

+0

@ cmaster実のところ、修正されました。 – Lundin

関連する問題