【问题标题】:reading big-endian files in little-endian system在 little-endian 系统中读取 big-endian 文件
【发布时间】:2016-09-21 16:53:17
【问题描述】:

我有一个需要用 C 语言读取的数据文件。它由以二进制形式存储的交替 16 位整数组成,我只需要第一列(即从 0 开始的每隔一个条目)

我有一个简单的 python 脚本可以准确读取文件:

import numpy as np


fname = '[filename]'
columntypes = np.dtype([('curr_pA', '>i2'),('volts', '>i2')])
test = np.memmap(fname, dtype=columntypes,mode='r')['curr_pA']

我想将它移植到 C。因为我的机器本身是 little-endian,所以我需要手动执行字节交换。这是我所做的:

void swapByteOrder_int16(double *current, int16_t *rawsignal, int64_t length)
{
    int64_t i;
    for (i=0; i<length; i++)
    {
        current[i] = ((rawsignal[2*i] << 8) | ((rawsignal[2*i] >> 8) & 0xFF));
    }
}


int64_t read_current_int16(FILE *input, double *current, int16_t *rawsignal, int64_t position, int64_t length)
{
    int64_t test;

    int64_t read = 0;

    if (fseeko64(input,(off64_t) position*2*sizeof(int16_t),SEEK_SET))
    {
        return 0;
    }
    test = fread(rawsignal, sizeof(int16_t), 2*length, input);
    read = test/2;
    if (test != 2*length)
    {
        perror("End of file reached");
    }
    swapByteOrder_int16(current, rawsignal, length);
    return read;
}

read_current_int16 函数中,我使用fread 将大量数据(两列)读入rawsignal 数组。然后我调用swapByteOrder_int16 来挑选所有其他值,并交换它的字节。然后我将结果转换为double 并将其存储在current

它不起作用。我得到垃圾作为 C 代码中的输出。我想我已经开始太久了,再也看不到自己的错误了。谁能发现明显的错误?

【问题讨论】:

  • 为什么代码在swapByteOrder_int16(double *current, int16_t *rawsignal, int64_t length) 中使用double 而不是int16_t
  • 其余代码使用double处理代码I/O部分的输出。由于我从int16_t 转换为double,因此应该不会损失精度。对current[i] 的赋值中有一个隐式转换。
  • rawsignal[2*i] 表示输入是每整数 32 位。现在,如果它们也被交换了,你应该使用rawsignal[2*(i+1)-1] 使用调试器来检查发生了什么。
  • rawsignal 应该是未签名的。否则&gt;&gt; 8 可能会移动 1 位(符号扩展)。
  • 您可能不需要将此移植到 C。请参阅 numpy 文档中的 Byte-swapping Introduction to byte ordering and ndarrays

标签: c endianness


【解决方案1】:

将字节序交换作为无符号数学执行,然后分配给double

void swapByteOrder_int16(double *current, const int16_t *rawsignal, size_t length) {
    for (size_t i = 0; i < length; i++) {
      int16_t x = rawsignal[2*i];
      x = (x*1u << 8) | (x*1u >> 8);
      current[i] = x;
    }
}

【讨论】:

  • 这可以解决问题。我的想法略有不同,但我认为我们的方法是等效的。
  • 查看我发布的答案以了解我实现它的方式,但这篇文章给了我答案,所以我接受了。
【解决方案2】:

我更喜欢这个面具和班次组合:

current[i] = ((rawsignal[2*i] & 0x00ff) << 8) | (rawsignal[2*i] >> 8)

【讨论】:

    【解决方案3】:

    正如几个人所建议的那样,以无符号的方式进行轮班就可以了。为了完整起见,我用我的实现来回答这个问题,因为我从接受的答案中对其进行了一些调整:

    void swapByteOrder_int16(double *current, uint16_t *rawsignal, int64_t length)
    {
        union int16bits bitval;
    
        int64_t i;
        for (i=0; i<length; i++)
        {
            bitval.bits = rawsignal[2*i];
            bitval.bits = (bitval.bits << 8) | (bitval.bits >> 8);
            current[i] = (double) bitval.currentval;
        }
    }
    
    union int16bits
    {
        uint16_t bits;
        int16_t currentval;
    };
    

    【讨论】:

      【解决方案4】:

      用无符号类型交换位会让事情变得更容易:

      void swapByteOrder_int16(double *current, void const *rawsignal_, size_t length)
      {
          uint16_t const *rawsignal = rawsignal_;
          size_t i;
          for (i=0; i<length; i++)
          {
             uint16_t tmp = rawsignal[2*i];
             tmp = ((tmp >> 8) & 0xffu) | ((tmp << 8) & 0xff00u);
             current[i] = (int16_t)(tmp);
          }
      }
      

      注意:当rawsignal未对齐时,您必须memcpy()它。

      【讨论】:

      • 我相信这相当于我接受的答案,但你的速度稍慢^_^。出于兴趣,我将我的解决方案发布为另一个答案。
      • 第一个&amp; 0xffu 不需要。第二个&amp; 0xffu 是错误的,可能是 0xFF00(也不需要)。
      • 请修复掩码,因为它不正确 - 此代码不起作用。
      • chux:是的,不需要第一个&amp;。但它使事情更加对称(例如,在转换 uint32_t 时)。它也增加了可读性,几乎每个编译器都会对其进行优化。
      猜你喜欢
      • 2020-12-01
      • 2019-06-30
      • 2011-10-06
      • 2018-05-15
      • 2022-06-10
      • 2012-10-09
      • 1970-01-01
      • 2010-10-16
      • 1970-01-01
      相关资源
      最近更新 更多