我创建了一个表示定点正数的结构。我希望小数点两边的数字包含2个字节。
typedef struct Fixed_t {
unsigned short floor; //left side of the decimal point
unsigned short fraction; //right side of the decimal point
} Fixed;
现在我想添加两个固定点数,Fixed x
和Fixed y
。为此,我将它们视为整数并添加。
(Fixed) ( (int)x + (int)y );
但正如我的visual studio 2010编译器所说,我无法在Fixed
和int
之间进行转换。
这样做的正确方法是什么?
编辑:我没有致力于{short floor, short fraction}
实施Fixed。
答案 0 :(得分:6)
你可能会尝试一个令人讨厌的黑客攻击,但这里有一个带有endian-ness的问题。无论你做什么转换,编译器应该如何知道你希望floor
成为结果中最重要的部分,fraction
不那么重要的部分?任何依赖于重新解释内存的解决方案都适用于一个字节序而不是另一个字节序。
你应该:
(1)明确定义转换。假设short
是16位:
unsigned int val = (x.floor << 16) + x.fraction;
(2)更改Fixed
以便它有一个int
成员而不是两个短片,然后在需要时分解,而不是撰写必要时。
如果你想要加速,那么(2)就是要做的事情。如果你有64位类型,那么你也可以在不分解的情况下进行乘法运算:unsigned int result = (((uint64_t)x) * y) >> 16
。
unsigned int val;
assert(sizeof(Fixed) == sizeof(unsigned int)) // could be a static test
assert(2 * sizeof(unsigned short) == sizeof(unsigned int)) // could be a static test
memcpy(&val, &x, sizeof(unsigned int));
这适用于大端系统,其中Fixed没有填充(并且整数类型没有填充位)。在一个小端系统上,你需要Fixed的成员处于另一个顺序,这就是为什么它很讨厌。有时候通过memcpy进行操作是正确的(在这种情况下,它是一个“技巧”而不是“讨厌的黑客”)。这不是其中之一。
答案 1 :(得分:5)
如果必须,您可以使用联盟,但要注意字节序问题。您可能会发现算术不起作用,当然也不可移植。
typedef struct Fixed_t {
union {
struct { unsigned short floor; unsigned short fraction };
unsigned int whole;
};
} Fixed;
更有可能(我认为)工作大端(Windows / Intel不是)。
答案 2 :(得分:3)
一些魔法:
typedef union Fixed {
uint16_t w[2];
uint32_t d;
} Fixed;
#define Floor w[((Fixed){1}).d==1]
#define Fraction w[((Fixed){1}).d!=1]
关键点:
short
为16位且int
为32位。Floor
和Fraction
(大写以避免与floor()
函数发生冲突)的宏以与endian无关的方式访问这两个部分,如foo.Floor
和{{ 1}}。编辑:根据OP的要求,解释宏:
联合是一种声明由多种不同重叠类型组成的对象的方法。这里我们foo.Fraction
重叠uint16_t w[2];
,可以将值作为2个16位单位或1个32位单位进行访问。
uint32_t d;
是复合文字,可以更详细地编写为(Fixed){1}
。它的第一个元素((Fixed){{1,0}}
)使用uint16_t w[2];
进行初始化。然后,表达式{1,0}
计算为32位整数,其第一个16位半部分为1,第二个16位半部分为0.在小端系统上,此值为1,因此{{1} }计算结果为1(true),((Fixed){1}).d
计算结果为0(false)。在大端系统上,它将是另一种方式。
因此,在小端系统上,((Fixed){1}).d==1
为((Fixed){1}).d!=1
,Floor
为w[1]
。在大端系统上,Fraction
为w[0]
,Floor
为w[0]
。无论哪种方式,您最终都会存储/访问32位值的正确一半以用于平台的字节序。
理论上,假设系统可以对16位和32位值使用完全不同的表示(例如交错两半的位),从而破坏这些宏。在实践中,这不会发生。 : - )
答案 3 :(得分:2)
这是不可能的,因为编译器不保证Fixed
将使用与int
相同的空间量。正确的方法是定义函数Fixed add(Fixed a, Fixed b)
。
答案 4 :(得分:1)
只需单独添加各个部分即可。你需要知道分数的值,意思是“1” - 这里我称之为FRAC_MAX
:
// c = a + b
void fixed_add( Fixed* a, Fixed* b, Fixed* c){
unsigned short carry = 0;
if((int)(a->floor) + (int)(b->floor) > FRAC_MAX){
carry = 1;
c->fraction = a->floor + b->floor - FRAC_MAX;
}
c->floor = a->floor + b->floor + carry;
}
或者,如果您只是将固定点设置为2字节边界,则可以执行以下操作:
void fixed_add( Fixed* a, Fixed *b, Fixed *c){
int ia = a->floor << 16 + a->fraction;
int ib = b->floor << 16 + b->fraction;
int ic = ia + ib;
c->floor = ic >> 16;
c->fraction = ic - c->floor;
}
答案 5 :(得分:0)
试试这个:
typedef union {
struct Fixed_t {
unsigned short floor; //left side of the decimal point
unsigned short fraction; //right side of the decimal point
} Fixed;
int Fixed_int;
}
答案 6 :(得分:0)
如果你的编译器将两个short放在4个字节上,那么你可以使用memcpy来复制你的struct中的int,但正如另一个答案中所说,这不是可移植的......并且非常难看。
您真的想要在单独的方法中单独添加每个字段吗? 出于性能原因,您想保留整数吗?
答案 7 :(得分:-1)
// add two Fixed
Fixed operator+( Fixed a, Fixed b )
{
...
}
//add Fixed and int
Fixed operator+( Fixed a, int b )
{
...
}
答案 8 :(得分:-1)
您可以使用以下方法将任何可寻址类型转换为另一种类型:
*(newtype *)&var