转换位大小可变的有符号整数

时间:2019-03-21 15:47:12

标签: c embedded c99 twos-complement

我在无符号整数(uint32_t)中有许多位数(位数可以更改)。例如(示例中为12位):

uint32_t a = 0xF9C;

这些位表示该长度的有符号整数。 在这种情况下,十进制数应为-100。 我想将变量存储在带符号的变量中,并且获取的是实际值。 如果我只是使用:

int32_t b = (int32_t)a;

它只是值3996,因为它被强制转换为(0x00000F9C),但实际上必须为(0xFFFFFF9C)

我知道一种方法:

union test
{
    signed temp :12;
}; 
union test x;
x.temp = a;
int32_t result = (int32_t) x.temp;

现在我得到正确的值-100

但是有更好的方法吗? 我的解决方案不是很灵活,因为我提到位数可以变化(1-64位之间的任意值)。

5 个答案:

答案 0 :(得分:3)

  

但是有更好的方法吗?

嗯,取决于您所说的“更好”是什么意思。下面的示例显示了一种更灵活的方法,因为位字段的大小不固定。如果您的用例需要不同的位大小,则可以考虑采用“更好”的方式。

unsigned sign_extend(unsigned x, unsigned num_bits)
{
    unsigned f = ~((1 << (num_bits-1)) - 1);
    if (x & f)  x = x | f;
    return x;
}


int main(void)
{
    int x = sign_extend(0xf9c, 12);
    printf("%d\n", x);

    int y = sign_extend(0x79c, 12);
    printf("%d\n", y);
}

输出:

-100
1948

答案 1 :(得分:1)

无分支签名的扩展位域的方法(Henry S. Warren Jr.,CACM v20 n6 1977年6月)是这样的:

View

@model WebApplication2.Models.UploadFiles

@{
    ViewBag.Title = "Paygroup Edit";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Update Paygroup</h2>

@using (Html.BeginForm("Edit", "UpdateFiles", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
    @Html.AntiForgeryToken()
    <div class="form-group">
        @Html.LabelFor(m => m.PayGroup, new { @class = "control-label" })
        @Html.EditorFor(m => m.PayGroup, new { htmlAttributes = new { @class = "form-control", placeholder = Html.DisplayNameFor(m => m.PayGroup) } })
        @Html.ValidationMessageFor(m => m.PayGroup, "", new { @class = "text-danger" })
        <input type="submit" value="Add" class="btn btn-default" />
        <input type="submit" value="Delete" class="btn btn-default" />
    </div>
}
<table class="table table-striped">
    <tr>
        <th>Paygroups</th>
        <th>Date added</th>
    </tr>
    @foreach (var pg in Model.Paygroups)
    {
        <tr>
            <td>@pg</td>
        </tr>

    }
    @foreach (var date in Model.DateAdded)
    {
        <tr>
            <td>@date</td>
        </tr>
    }
</table>

根据@Lundin的评论进行更新

这是经过测试的代码(打印-100):

public class UpdateFilesController : Controller
    {
        // GET: Default
        public ActionResult Edit()
        {
            var fullpath = Path.Combine(Server.MapPath("~/sourcefiles"), "paygroup.txt");
            List<string> paygroupsList = new List<string>();
            List<string> DateList = new List<string>();
            string line;
            using (var sr = new StreamReader(fullpath))
            {
                while ((line = sr.ReadLine()) != null)
                {
                    string[] strArray = line.Split('|');
                    paygroupsList.Add(strArray[0]);
                    DateList.Add(strArray[1]);
                }
                UploadFiles model = new UploadFiles()
                {
                    Paygroups = paygroupsList,
                    DateAdded = DateList
                };
                return View(model);
            }
        }

答案 2 :(得分:0)

当右移带符号负整数时,这取决于符号扩展的实现定义行为。首先,将无符号整数一直向左移动,直到符号位变为MSB,然后将其强制转换为有符号整数,然后移回:

#include <stdio.h>
#include <stdint.h>

#define NUMBER_OF_BITS 12

int main(void) {
    uint32_t x = 0xF9C;
    int32_t y = (int32_t)(x << (32-NUMBER_OF_BITS)) >> (32-NUMBER_OF_BITS);

    printf("%d\n", y);

    return 0;
}

答案 3 :(得分:0)

这是您的问题的 a 解决方案:

int32_t sign_extend(uint32_t x, uint32_t bit_size)
{
    // The expression (0xffffffff << bit_size) will fill the upper bits to sign extend the number.
    // The expression (-(x >> (bit_size-1))) is a mask that will zero the previous expression in case the number was positive (to avoid having an if statemet).
    return (0xffffffff << bit_size) & (-(x >> (bit_size-1))) | x;
}
int main()
{

    printf("%d\n", sign_extend(0xf9c, 12)); // -100
    printf("%d\n", sign_extend(0x7ff, 12)); // 2047

    return 0;
}

答案 4 :(得分:-1)

执行此操作的理智,可移植且有效的方法只是掩盖数据部分,然后用0xFF ...填充其他所有内容以获取正确的2的补码表示形式。您需要知道多少位是数据部分。

  • 我们可以使用(1u << data_length) - 1屏蔽数据。
  • 在这种情况下,对于data_length = 8,数据掩码变为0xFF。让我们称之为data_mask
  • 因此,数字的数据部分为a & data_mask
  • 其余数字需要用零填充。也就是说,所有内容都不是数据掩码的一部分。只需执行~data_mask即可实现。
  • C代码:a = (a & data_mask) | ~data_mask。现在a是正确的32位2的补码。

示例:

#include <stdio.h>
#include <inttypes.h>

int main(void) 
{
  const uint32_t data_length = 8;
  const uint32_t data_mask = (1u << data_length) - 1;

  uint32_t a = 0xF9C;
  a = (a & data_mask) | ~data_mask;

  printf("%"PRIX32 "\t%"PRIi32, a, (int32_t)a);
}

输出:

FFFFFF9C        -100

这取决于int是32位2的补码,但可以完全移植。