45

2つの変数間で線形補間を行い、分数aを指定するために、現在次のコードを使用しています。bf

float lerp(float a, float b, float f) 
{
    return (a * (1.0 - f)) + (b * f);
}

おそらくもっと効率的な方法があると思います。FPUのないマイクロコントローラーを使用しているので、浮動小数点演算はソフトウェアで行われます。それらは適度に高速ですが、それでも加算または乗算するのに100サイクルのようなものです。

助言がありますか?

上記のコードの式を明確にするために、nbは1.0、明示的な浮動小数点リテラルとしての指定を省略できます。

4

7 に答える 7

36

精度の違いを無視すると、その式は次のようになります。

float lerp(float a, float b, float f)
{
    return a + f * (b - a);
}

これは、2つの加算/減算と2つの乗算ではなく、2つの加算/減算と1つの乗算です。

于 2010-12-04T12:43:56.750 に答える
9

FPUのないマイクロコントローラーを使用している場合、浮動小数点は非常に高価になります。浮動小数点演算の場合、簡単に20倍遅くなる可能性があります。最速の解決策は、整数を使用してすべての計算を実行することです。

固定小数点(http://blog.credland.net/2013/09/binary-fixed-point-explanation.html?q=fixed+binary+point)の後の場所の数は、XY_TABLE_FRAC_BITSです。

これが私が使用する関数です:

inline uint16_t unsignedInterpolate(uint16_t a, uint16_t b, uint16_t position) {
    uint32_t r1;
    uint16_t r2;

    /* 
     * Only one multiply, and one divide/shift right.  Shame about having to
     * cast to long int and back again.
     */

    r1 = (uint32_t) position * (b-a);
    r2 = (r1 >> XY_TABLE_FRAC_BITS) + a;
    return r2;    
}

インライン化された関数で、それはおよそであるはずです。10〜20サイクル。

32ビットマイクロコントローラーを使用している場合は、パフォーマンスを低下させることなく、より大きな整数を使用して、より大きな数値またはより高い精度を得ることができます。この関数は16ビットシステムで使用されていました。

于 2014-01-27T11:11:45.753 に答える
9

浮動小数点演算が利用可能であると仮定すると、OPのアルゴリズムは優れたものであり、大きさが大幅に異なる場合はa + f * (b - a)精度が低下するため、常に代替アルゴリズムよりも優れています。ab

例えば:

// OP's algorithm
float lint1 (float a, float b, float f) {
    return (a * (1.0f - f)) + (b * f);
}

// Algebraically simplified algorithm
float lint2 (float a, float b, float f) {
    return a + f * (b - a);
}

この例では、32ビットのfloatを想定lint1(1.0e20, 1.0, 1.0)すると1.0が正しく返されますが、lint2誤って0.0が返されます。

オペランドの大きさが大幅に異なる場合、精度の低下の大部分は加算演算子と減算演算子にあります。上記の場合、原因は、の減算b - aとの加算ですa + f * (b - a)。OPのアルゴリズムは、加算前にコンポーネントが完全に乗算されるため、これに悩まされることはありません。


a = 1e20、b = 1の場合、異なる結果の例を次に示します。テストプログラム:

#include <stdio.h>
#include <math.h>

float lint1 (float a, float b, float f) {
    return (a * (1.0f - f)) + (b * f);
}

float lint2 (float a, float b, float f) {
    return a + f * (b - a);
}

int main () {
    const float a = 1.0e20;
    const float b = 1.0;
    int n;
    for (n = 0; n <= 1024; ++ n) {
        float f = (float)n / 1024.0f;
        float p1 = lint1(a, b, f);
        float p2 = lint2(a, b, f);
        if (p1 != p2) {
            printf("%i %.6f %f %f %.6e\n", n, f, p1, p2, p2 - p1);
        }
    }
    return 0;
}

出力、フォーマット用にわずかに調整:

    f lint1 lint2 lint2-lint1
0.828125 17187500894208393216 17187499794696765440 -1.099512e + 12
0.890625 10937500768952909824 10937499669441282048 -1.099512e + 12
0.914062 8593750447104196608 8593749897348382720 -5.497558e + 11
0.945312 5468750384476454912 5468749834720641024 -5.497558e + 11
0.957031 4296875223552098304 4296874948674191360 -2.748779e + 11
0.972656 2734375192238227456 2734374917360320512 -2.748779e + 11
0.978516 2148437611776049152 2148437474337095680 -1.374390e + 11
0.986328 1367187596119113728 1367187458680160256 -1.374390e + 11
0.989258 1074218805888024576 1074218737168547840 -6.871948e + 10
0.993164 683593798059556864 683593729340080128 -6.871948e + 10
1.000000 1 0 -1.000000e + 00
于 2014-05-17T23:03:54.603 に答える
3

浮動小数点演算なしでマイクロコントローラーをコーディングしている場合は、浮動小数点数をまったく使用せず、代わりに固定小数点演算を使用することをお勧めします。

于 2010-12-04T13:12:29.573 に答える
3

標準の線形補間式f1(t)= a + t(ba)、f2(t)= b-(ba)(1-t)、およびf3(t)= a(1- t)+ btは、浮動小数点演算を使用するときに正常に動作することを保証するものではありません。つまり、a!= bの場合、f1(1.0)== bまたはf2(0.0)== aであることが保証されませんが、a == bの場合、f3(t)がaに等しいとは保証されません。 、0<t<1の場合。

この関数は、結果が適切に動作し、エンドポイントを正確にヒットする必要がある場合に、IEEE754浮動小数点をサポートするプロセッサで機能しました(倍精度で使用しますが、floatも機能するはずです)。

double lerp(double a, double b, double t) 
{
    if (t <= 0.5)
        return a+(b-a)*t;
    else
        return b-(b-a)*(1.0-t);
}
于 2019-10-31T16:56:22.443 に答える
2

C ++ 20以降、を使用できますstd::lerp()。これは、ターゲットに最適な実装である可能性があります。

于 2020-03-03T12:56:59.933 に答える
0

最終結果を整数にしたい場合は、入力にも整数を使用する方が速い場合があります。

int lerp_int(int a, int b, float f)
{
    //float diff = (float)(b-a);
    //float frac = f*diff;
    //return a + (int)frac;
    return a + (int)(f * (float)(b-a));
}

これにより、2つのキャストと1つのフロート乗算が実行されます。プラットフォームでキャストがfloatの加算/減算よりも高速であり、整数の回答が役立つ場合は、これが妥当な代替手段になる可能性があります。

于 2010-12-04T13:14:23.127 に答える