2012-03-07 27 views
1

我對C非常陌生,我正在編寫Ruby C擴展。其中一個函數應該計算兩種顏色之間的平淡。 Color結構使用char來存儲RGB值。混合比率爲0.034和1.0之間的double混合char和double的算術運算的潛在問題?

在我這裏完成的操作中混入chardouble是不好的做法嗎?會有潛在的問題嗎?

我猜測,如果體重是不是0.0和1.0之間的可能的問題,因爲它可能導致比0小於或大於255

我應該明確地鑄造類型大的價值嗎?

typedef struct Color Color; 

static struct Color { 
    unsigned char red, green, blue, alpha; 
}; 


static Color 
color_blend( Color color1, Color color2, double weight) 
{ 
    Color color3 = { 0, 0, 0, 0 }; 

    color3.red = (1 - weight) * color1.red + (weight * color2.red); 
    color3.green = (1 - weight) * color1.green + (weight * color2.green); 
    color3.blue = (1 - weight) * color1.blue + (weight * color2.blue); 
    color3.alpha = (1 - weight) * color1.alpha + (weight * color2.alpha); 

    return color3; 
} 
+0

右側表達式被「提升」爲「double」,然後在分配時轉換回「byte」。要回答的問題是:這可能會丟失信息嗎? – vulkanino 2012-03-07 12:01:19

回答

3

你並不需要進行明確的鑄造;編譯器的隱式轉換應該做同樣的事情。但是,有兩個很好的理由可以考慮顯式強制轉換:

  1. 在更高的警告級別上,您可能會收到有關隱式轉換精度等丟失的警告消息。
  2. 顯式強制轉換可幫助將您的意圖記錄到代碼的讀者。

我不認爲你的代碼會有任何範圍問題。但是,您可能會考慮添加從最近到最近的行爲,例如當前計算出的值等。 99.999將截斷爲99.

-1

如果不在正常算術中使用顯式類型轉換,則更好。編譯器知道如何進行優化,如果你開始告訴它要使用什麼類型,你可能會在不知不覺中排除某些選項並放慢你的代碼。

要小心的是,如果你有這樣的事情:

double_var = int_var1/int_var2; 

它會做整數師和然後結果轉換爲加倍,所以你會得到舍入誤差。只要其中一個變量是浮點類型(就像你所擁有的那樣),它應該工作得很好。

常數相同:使用0.0,而不是普通的0,如果你想要浮點數學。

+0

你有第一段引用嗎? – 2012-03-07 12:10:16

+0

不,沒有脫節,但我很樂意去說服海灣合作委員會去優化ARM在ARM上的擴展,如果這些類型被人爲地擴大或縮小了,它就無法工作。 – ams 2012-03-07 12:22:29

0

爲了使之安全,我認爲你應該做的下一步

unsigned char source_color = 230; 
double coef = 0.7; 
double res = source_color * coef; //is OK as char will be converted 
//to double before operation 

if(res < 0) 
    res = 0; 
if(res > 255) 
    res = 255; 
//this is needet to prevent from such bug. 
//If you wan't to convert for example double(256) to char you will have char(1) 
//as it will be counted 256 % 255 as 255 is max char can fit 

unsigned char result_color = (unsigned char)res; //This will trucate fraction 
//part of the number for example 1.2 => 1 or 1.999 => 1. 
//If you want to have 1.999 => 2 you should round you double number. 

該解決方案是安全的。

+4

'無符號字符(res)'在C中無效,C沒有構造函數。即使在C++中,使用臨時而不是演員陣容的構建也可能會被忽略。 C中的「正確」方式是「(unsigned char)res'和C++'static_cast (res)'。 – 2012-03-07 12:19:02

1

在您的特定示例中,只要您未將表達式更改,它就會正常工作。在你的代碼中有很多隱式的促銷活動,所以在某個地方很容易發現bug。

讓我們仔細研究該行:

color3.red = (1 - weight) * color1.red + (weight * color2.red); 

如果我們只是看看使用的類型,這種表達就相當於:

unsigned char = (signed int - double) * unsigned char + (double * unsigned char); 

讓我們假設編譯器使用左到右評估順序。然後,此表達式將被評估的以下列方式計算:

unsigned char = (signed int - double) * unsigned char + (double * unsigned char); //balance -> 
unsigned char = (double - double) * unsigned char + (double * unsigned char); //calculate-> 
unsigned char = double * unsigned char + (double * unsigned char); //balance-> 
unsigned char = double * double + (double * unsigned char); //calculate-> 
unsigned char = double + (double * unsigned char); //balance-> 
unsigned char = double + (double * double); //calculate-> 
unsigned char = double + double; // calculate-> 
unsigned char = double; // truncate-> 
unsigned char = unsigned char; 

如果任何子表達不含有雙型,很可能會發生錯誤。如果您不知道所有隱式類型轉換,修改此表達式將會非常危險。如果您不確定它們,請使用明確的類型轉換。

像MISRA-C這樣的編碼標準完全禁止隱式轉換,因爲這樣的轉換有時是危險且不可預知的。你的代碼的MISRA-C標準的版本會是什麼樣子:

color3.red = (double) ((1.0-weight) * color1.red) + 
       (double) (weight * color2.red); 

(你都可以從大量的問題了,因爲你正在使用unsigned char,而不是簡單的char假如你使用char,那麼所有的賭注會off)。

+0

那麼知道如果我沒有任何浮點類型,它們都是整數算術,結果是錯誤的。但是我在想,如果我把代碼放到了所有的地方,那代碼就不會閱讀,而且你會得到如此長的代碼。但我不確定是否因爲我在C方面缺乏經驗而出現任何其他無法預料的問題。這種細分非常有幫助。 – thomthom 2012-03-07 16:50:55

+0

問題是,對於我正在做的事情,有更好的做法嗎?這個函數是我需要將'double'和'char'混合在一起的唯一函數 - 並且我沒有看到使用'int'作爲'Color'結構的任何一點。雖然我的功能可能是好的 - 正如人們所說,是否有更好的設計? – thomthom 2012-03-07 16:55:34

+1

@thomthom重要的部分是看每個子表達式並理解那裏發生的事情。 C中的隱式升級是複雜而危險的。 [關於CERT的良好閱讀](https://www.securecoding.cert.org/confluence/display/seccode/INT02-C.+Understand+integer+conversion+rules)。 (CERT C是一種編碼標準,基本上是MISRA-C的輕量級版本,更關注臺式計算機而不是關鍵系統。) – Lundin 2012-03-07 19:05:38