簡體   English   中英

c中的浮點定點轉換有什么問題?

[英]What is the problem with this fixed point conversion from floating point in c?

我正在嘗試將浮點轉換為定點,因為硬件沒有浮點加速。 我似乎找不到變量screen_X的錯誤,該值始終為 320.0。 精度為 Q38,26(編輯有用位為 Q6.26,64 位變量用於 mul 和 div 兼容性)。

#define fixed_precision 26
#define CONV_F(A) (A*conv_fixed_precision)
#define CONV_DF(A) (A/(1<<fixed_precision))
#define MUL38_26(A, B)((long long)(A*B)>>fixed_precision)
#define DIV38_26(A, B)((long long)(A<<fixed_precision)/B)

// global variable
double conv_fixed_precision = (1 << fixed_precision);

浮點代碼如下:

void draw_balls(VGA& vga) {
   int i;
   for (i = 0; i < NB_BALL; i++) {
       if (R_Ball[i] == 0) continue;   // the pearl has already been found
       double screen_X = X_Ball[i];
       double screen_Y = Y_Ball[i];

       screen_X -= X_position;
       screen_Y -= Y_position;
       screen_X *= Scale;
       screen_Y *= Scale;

       double screen_Radius = R_Ball[i] * Scale;
       if ((screen_X * screen_X + screen_Y * screen_Y <= screen_Radius * screen_Radius)) {
           //La perle est trouvee
           R_Ball[i] = 0;
           int time = get_time();
           printf("Found %i pearl(s) at time %i\n", ++found_pearl, time);
       }

       double tmp_X = screen_X;
       double tmp_Y = screen_Y;
       screen_X = (tmp_X * cosAngle - tmp_Y * sinAngle) * 240 + 320;
       screen_Y = (tmp_X * sinAngle + tmp_Y * cosAngle) * 240 + 240;
       screen_Radius *= 240;

       if (screen_X + screen_Radius < 0) break;
       if (screen_X - screen_Radius >= 640) break;
       if (screen_Y + screen_Radius < 0) break;
       if (screen_Y - screen_Radius >= 480) break;

       drawBall3D(vga, screen_X, screen_Y, screen_Radius, i % 7);
   }
#ifdef USE_OPEN_GL
  glFlush();
#endif

定點代碼如下:

void draw_balls(VGA & vga) {
    int i;

    long long cosAngF = CONV_F(cosAngle);
    long long sinAngF = CONV_F(sinAngle);
    long long scaleF = CONV_F(Scale);
    long long xPosF = CONV_F(X_position);
    long long yPosF = CONV_F(Y_position);

    for (i = 0; i < NB_BALL; i++) {
        if (R_Ball[i] == 0) continue;   // the pearl has already been found
        long long screen_X = CONV_F(X_Ball[i]);
        long long screen_Y = CONV_F(Y_Ball[i]);

        screen_X -= xPosF;
        screen_Y -= yPosF;
        screen_X = MUL38_26(screen_X, scaleF);
        screen_Y = MUL38_26(screen_Y, scaleF);

        long long screen_Radius = CONV_F(R_Ball[i] * Scale);
        if ((MUL38_26(screen_X, screen_X) + MUL38_26(screen_Y, screen_Y)) <= MUL38_26(screen_Radius, screen_Radius)) {
            //La perle est trouvee
            R_Ball[i] = 0;
            int time = get_time();
            printf("Found %i pearl(s) at time %i\n", ++found_pearl, time);
        }

        long long tmp_X = screen_X;
        long long tmp_Y = screen_Y;
        long long F240 = ((long long)240)<<fixed_precision;
        long long F320 = ((long long)320) << fixed_precision;
        
        screen_X = MUL38_26(MUL38_26(tmp_X, cosAngF) - MUL38_26(tmp_Y, sinAngF), F240) + F320;
        screen_Y = MUL38_26(MUL38_26(tmp_X, sinAngF) + MUL38_26(tmp_Y, cosAngF), F240) + F240;
        screen_Radius = MUL38_26(screen_Radius, F240);

        long long F640 = ((long long)640) << fixed_precision;
        long long F480 = ((long long)480) << fixed_precision;
        if (screen_X + screen_Radius < 0) break;
        if (screen_X - screen_Radius >= F640) break;
        if (screen_Y + screen_Radius < 0) break;
        if (screen_Y - screen_Radius >= F480) break;

        drawBall3D(vga, screen_X>>fixed_precision, screen_Y >> fixed_precision, screen_Radius >> fixed_precision, i % 7);
    }
#ifdef USE_OPEN_GL
    glFlush();
#endif
#endif
    
}
  1. #defines中的\是錯誤的

    \告訴您的編譯器定義在下一行繼續,但您的定義是單行的,這樣您的代碼就會出錯,甚至無法編譯。 我敢打賭,您是在基於 GCC 的編譯器上的某些 MCU 或 DSP 上執行此操作,通常錯誤不會生成新的二進制文件,因此您很可能使用過去最后可編譯的舊版本進行編程(有時很難將其發現為消息 build has stop 與編譯控制台中的成功操作區別不大)。

  2. 你得到了38.26 ,它增加了 64 位並使用long long

    問題是 long long 可以是任何東西,所以它在您的環境中有多少位? 如果只是 64 位,那么您會遇到問題,因為:

     A<<fixed_precision

    將需要64+fixed_precision位來存儲結果,所以如果你得到的結果更少,那么你扔掉 A 的 MSB 位會使你的結果無效(除法)

    這可以在 64 位上完成而不會丟失精度。 這里是我通常實現的定點數學:

     m = 1<<n Integer(a) = Real(x*m) Real(x) = Integer(a)/mx*m + y*m = (x+y)*m -> (a+b) x*m - y*m = (xy)*m -> (ab) x*m * y*m = (x*y)*m*m -> (a*b)>>nx*m / y*m = (x/y) + (x%y)/y -> (a/b)<<n + ((a%b)<<n)/b

    但是請注意,乘法a*b仍然需要 128 位,因此如果您無法像乘法一樣訪問 ALU 或將其移植到 64位,請使用天真的O(n^2)或 Karatsuba 乘法。 另一種選擇是將a*b>>n分解為除法( >>n )和模( &(n-1) )部分,就像我對除法所做的那樣。 但是因為我可以在我編寫代碼的所有平台上訪問64b*64b=(64+64)b操作,所以我懶得將其等同起來。

  3. 2'os 補碼上的有符號算術右移需要 SAR

    如果您的號碼已簽名,則不可能天真地使用a>>n ,因為它很可能會破壞您的號碼符號。 要在 2'os 補碼上除以 2 的冪,您需要 SAR(算術右移),因此您需要復制 MSB 位,因此對於 1 位移位,請執行以下操作:

     (a>>1)|(a&0x8000000000000000);

    對於更多位移,您需要添加分支...例如 4,8,16 位:

     (a>> 4)|((a&0x8000000000000000)?0xF000000000000000:0); (a>> 8)|((a&0x8000000000000000)?0xFF00000000000000:0); (a>>16)|((a&0x8000000000000000)?0xFFF0000000000000:0);

    掩碼可以存儲在 LUT 中,或者您可以在循環中使用 1 位移位,但這會更慢。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM