如何将具有两个无符号短整型的结构视为无符号整型? (在C中)
我创建了一个结构来表示定点正数。我希望小数点两边的数字都由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}
实现。
I created a structure to represent a fixed-point positive number. I want the numbers in both sides of the decimal point to consist 2 bytes.
typedef struct Fixed_t {
unsigned short floor; //left side of the decimal point
unsigned short fraction; //right side of the decimal point
} Fixed;
Now I want to add two fixed point numbers, Fixed x
and Fixed y
. To do so I treat them like integers and add.
(Fixed) ( (int)x + (int)y );
But as my visual studio 2010 compiler says, I cannot convert between Fixed
and int
.
What's the right way to do this?
EDIT: I'm not committed to the {short floor, short fraction}
implementation of Fixed.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
您可以尝试进行令人讨厌的黑客攻击,但这里存在字节序问题。无论您做什么转换,编译器如何知道您希望
floor
成为结果中最重要的部分,而fraction
成为不太重要的部分?任何依赖于重新解释内存的解决方案都适用于一种字节序,但不适用于另一种字节序。您应该:
(1) 显式定义转换。假设
short
是 16 位:(2) 更改
Fixed
使其具有int
成员而不是两个 Shorts,然后分解需要时,而不是需要时组合。如果你想让加法更快,那么(2)就是要做的事情。如果您有 64 位类型,那么您也可以进行乘法而不进行分解:unsigned int result = (((uint64_t)x) * y) >> 16. .
顺便说一句,令人讨厌的黑客是这样的:
这将在大端系统上工作,其中固定没有填充(并且整数类型没有填充位)。在小端系统上,您需要固定的成员处于其他顺序,这就是它令人讨厌的原因。有时通过 memcpy 进行转换是正确的做法(在这种情况下,这是一个“技巧”而不是“令人讨厌的黑客”)。这只是不是这样的一次。
You could attempt a nasty hack, but there's a problem here with endian-ness. Whatever you do to convert, how is the compiler supposed to know that you want
floor
to be the most significant part of the result, andfraction
the less significant part? Any solution that relies on re-interpreting memory is going to work for one endian-ness but not another.You should either:
(1) define the conversion explicitly. Assuming
short
is 16 bits:(2) change
Fixed
so that it has anint
member instead of two shorts, and then decompose when required, rather than composing when required.If you want addition to be fast, then (2) is the thing to do. If you have a 64 bit type, then you can also do multiplication without decomposing:
unsigned int result = (((uint64_t)x) * y) >> 16
.The nasty hack, by the way, would be this:
That would work on a big-endian system, where Fixed has no padding (and the integer types have no padding bits). On a little-endian system you'd need the members of Fixed to be in the other order, which is why it's nasty. Sometimes casting through memcpy is the right thing to do (in which case it's a "trick" rather than a "nasty hack"). This just isn't one of those times.
如果有必要,您可以使用联合,但要注意字节序问题。您可能会发现算术不起作用,而且当然不可移植。
(我认为)这更有可能以大端方式工作(Windows/Intel 则不然)。
If you have to you can use a union but beware of endian issues. You might find the arithmetic doesn't work and certainly is not portable.
which is more likely (I think) to work big-endian (which Windows/Intel isn't).
一些魔法:
要点:
short
和 32 位的int
。Floor
和Fraction
的宏(大写以避免与floor()
函数冲突)以独立于字节序的方式访问这两个部分,如下所示foo.Floor
和foo.Fraction
。编辑:根据OP的要求,对宏的解释:
联合是声明由几种不同的重叠类型组成的对象的一种方式。这里我们有
uint16_t w[2];
重叠uint32_t d;
,使得可以以 2 个 16 位单元或 1 个 32 位单元的形式访问该值。(Fixed){1}
是一个复合文字,可以更详细地写为(Fixed){{1,0}}
。它的第一个元素 (uint16_t w[2];
) 使用{1,0}
进行初始化。然后,表达式((Fixed){1}).d
的计算结果为 32 位整数,其前 16 位一半为 1,后 16 位一半为 0。系统中,该值为 1,因此((Fixed){1}).d==1
计算结果为 1 (true) 并且((Fixed){1}).d!= 1
的计算结果为 0(假)。在大端系统上,情况正好相反。因此,在小端系统上,
Floor
为w[1]
,Fraction
为w[0]
。在大端系统上,Floor
为w[0]
,Fraction
为w[1]
。无论哪种方式,您最终都会存储/访问平台字节序的 32 位值的正确一半。理论上,假设的系统可以对 16 位和 32 位值使用完全不同的表示形式(例如交错两半的位),从而破坏这些宏。实际上,这不会发生。 :-)
Some magic:
Key points:
short
being 16-bit andint
being 32-bit.Floor
andFraction
(capitalized to avoid clashing withfloor()
function) access the two parts in an endian-independent way, asfoo.Floor
andfoo.Fraction
.Edit: At OP's request, an explanation of the macros:
Unions are a way of declaring an object consisting of several different overlapping types. Here we have
uint16_t w[2];
overlappinguint32_t d;
, making it possible to access the value as 2 16-bit units or 1 32-bit unit.(Fixed){1}
is a compound literal, and could be written more verbosely as(Fixed){{1,0}}
. Its first element (uint16_t w[2];
) gets initialized with{1,0}
. The expression((Fixed){1}).d
then evaluates to the 32-bit integer whose first 16-bit half is 1 and whose second 16-bit half is 0. On a little-endian system, this value is 1, so((Fixed){1}).d==1
evaluates to 1 (true) and((Fixed){1}).d!=1
evaluates to 0 (false). On a big-endian system, it'll be the other way around.Thus, on a little-endian system,
Floor
isw[1]
andFraction
isw[0]
. On a big-endian system,Floor
isw[0]
andFraction
isw[1]
. Either way, you end up storing/accessing the correct half of the 32-bit value for the endian-ness of your platform.In theory, a hypothetical system could use a completely different representation for 16-bit and 32-bit values (for instance interleaving the bits of the two halves), breaking these macros. In practice, that's not going to happen. :-)
这是不可能移植的,因为编译器不保证
Fixed
将使用与int
相同的空间量。正确的方法是定义一个函数Fixed add(Fixed a, Fixed b)
。This is not possible portably, as the compiler does not guarantee a
Fixed
will use the same amount of space as anint
. The right way is to define a functionFixed add(Fixed a, Fixed b)
.只需单独添加各个部分即可。您需要知道表示“1”的分数的值 - 这里我称之为
FRAC_MAX
:或者,如果您只是将固定点设置为 2 字节边界,您可以做类似的事情:
Just add the pieces separately. You need to know the value of the fraction that means "1" - here I'm calling that
FRAC_MAX
:Alternatively, if you're just setting the fixed point as being at the 2 byte boundary you can do something like:
试试这个:
Try this:
如果你的编译器将这两个短放在 4 个字节上,那么你可以使用 memcpy 将你的 int 复制到你的结构中,但正如另一个答案中所说,这是不可移植的......而且非常丑陋。
您真的关心在单独的方法中单独添加每个字段吗?
出于性能原因您想保留整数吗?
If your compiler puts the two short on 4 bytes, then you can use memcpy to copy your int in your struct, but as said in another answer, this is not portable... and quite ugly.
Do you really care adding separately each field in a separate method?
Do you want to keep the integer for performance reason?
您可以使用以下方法将任何可寻址类型转换为另一种类型:
You may cast any addressable type to another one by using: