如何将具有两个无符号短整型的结构视为无符号整型? (在C中)

发布于 2024-09-28 18:14:18 字数 538 浏览 6 评论 0原文

我创建了一个结构来表示定点正数。我希望小数点两边的数字都由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 xFixed y。为此,我将它们视为整数并相加。

(Fixed) ( (int)x + (int)y );

但正如我的 Visual Studio 2010 编译器所说,我无法在 Fixedint 之间进行转换。

这样做的正确方法是什么?

编辑:我不致力于固定的 {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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

痴者 2024-10-05 18:14:18

您可以尝试进行令人讨厌的黑客攻击,但这里存在字节序问题。无论您做什么转换,编译器如何知道您希望 floor 成为结果中最重要的部分,而 fraction 成为不太重要的部分?任何依赖于重新解释内存的解决方案都适用于一种字节序,但不适用于另一种字节序。

您应该:

(1) 显式定义转换。假设 short 是 16 位:

unsigned int val = (x.floor << 16) + x.fraction;

(2) 更改 Fixed 使其具有 int 成员而不是两个 Shorts,然后分解需要时,而不是需要时组合

如果你想让加法更快,那么(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));

这将在大端系统上工作,其中固定没有填充(并且整数类型没有填充位)。在小端系统上,您需要固定的成员处于其他顺序,这就是它令人讨厌的原因。有时通过 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, and fraction 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:

unsigned int val = (x.floor << 16) + x.fraction;

(2) change Fixed so that it has an int 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:

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));

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.

爱给你人给你 2024-10-05 18:14:18

如果有必要,您可以使用联合,但要注意字节序问题。您可能会发现算术不起作用,而且当然不可移植。

typedef struct Fixed_t {
   union { 
        struct { unsigned short floor; unsigned short fraction }; 
        unsigned int whole;
         };
} Fixed;

(我认为)这更有可能以大端方式工作(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.

typedef struct Fixed_t {
   union { 
        struct { unsigned short floor; unsigned short fraction }; 
        unsigned int whole;
         };
} Fixed;

which is more likely (I think) to work big-endian (which Windows/Intel isn't).

心房的律动 2024-10-05 18:14:18

一些魔法:

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]

要点:

  • 我使用固定大小的整数类型,因此您不必依赖于 16 位的 short 和 32 位的 int
  • FloorFraction 的宏(大写以避免与 floor() 函数冲突)以独立于字节序的方式访问这两个部分,如下所示foo.Floorfoo.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(假)。在大端系统上,情况正好相反。

因此,在小端系统上,Floorw[1]Fractionw[0] 。在大端系统上,Floorw[0]Fractionw[1]。无论哪种方式,您最终都会存储/访问平台字节序的 32 位值的正确一半。

理论上,假设的系统可以对 16 位和 32 位值使用完全不同的表示形式(例如交错两半的位),从而破坏这些宏。实际上,这不会发生。 :-)

Some magic:

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]

Key points:

  • I use fixed-size integer types so you're not depending on short being 16-bit and int being 32-bit.
  • The macros for Floor and Fraction (capitalized to avoid clashing with floor() function) access the two parts in an endian-independent way, as foo.Floor and foo.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]; overlapping uint32_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 is w[1] and Fraction is w[0]. On a big-endian system, Floor is w[0] and Fraction is w[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. :-)

迷乱花海 2024-10-05 18:14:18

这是不可能移植的,因为编译器不保证 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 an int. The right way is to define a function Fixed add(Fixed a, Fixed b).

浅忆流年 2024-10-05 18:14:18

只需单独添加各个部分即可。您需要知道表示“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;
}

Just add the pieces separately. You need to know the value of the fraction that means "1" - here I'm calling that 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;
 }

Alternatively, if you're just setting the fixed point as being at the 2 byte boundary you can do something like:

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;
}
小清晰的声音 2024-10-05 18:14:18

试试这个:

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;
}

Try this:

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;
}
三生殊途 2024-10-05 18:14:18

如果你的编译器将这两个短放在 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?

迷荒 2024-10-05 18:14:18
// add two Fixed 
Fixed operator+( Fixed a, Fixed b ) 
{   
...
}

//add Fixed and int
Fixed operator+( Fixed a, int b ) 
{   
...
}
// add two Fixed 
Fixed operator+( Fixed a, Fixed b ) 
{   
...
}

//add Fixed and int
Fixed operator+( Fixed a, int b ) 
{   
...
}
浮云落日 2024-10-05 18:14:18

您可以使用以下方法将任何可寻址类型转换为另一种类型:

*(newtype *)&var

You may cast any addressable type to another one by using:

*(newtype *)&var
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文