2012-03-07 112 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