从具有指定字节顺序的二进制 unsigned char* 读取 C 中的 double

Posted

技术标签:

【中文标题】从具有指定字节顺序的二进制 unsigned char* 读取 C 中的 double【英文标题】:Reading double in C from binary unsigned char* with specified endianness 【发布时间】:2020-06-25 14:26:24 【问题描述】:

我用 C 语言编写了一个 MariaDB/mysql UDF,用于处理空间数据。 I 数据可作为无符号字符 * 用于该函数。数据的二进制编码从一个切换位开始,指示流是小端编码还是大端编码。由于是这种情况,我使用以下宏从流中读取无符号 32 位整数:

#define U32BIT_LE_DATA(ptr) (*(ptr)<<0) | (*(ptr + 1)<<8) | (*(ptr + 2)<<16) | (*(ptr + 3)<<24)
#define U32BIT_BE_DATA(ptr) (*(ptr + 3)<<0) | (*(ptr + 2)<<8) | (*(ptr + 1)<<16) | (*(ptr)<<24)
    
uint32_t var = U32BIT_LE_DATA(ptr); // Little endian encoding
uint32_t var = U32BIT_BE_DATA(ptr); // Big endian encoding

流还有我需要解析的双精度数据(使用 IEEE 754 双精度格式的 64 位(8 字节)双精度数据)。我知道我能做到:

double var;
memcpy(&var, ptr, sizeof(double));

但是这个代码在可移植性方面不是很安全。我知道如果我知道我的机器字节序,那么我可以在调用memcpy 之前简单地颠倒字节的顺序。尽管如此,是否有更可靠的方法可以使用指定的字节序将双精度数解码或编码为 64 位 IEEE 754 双精度浮点数,而无需知道运行代码的机器的字节序(和系统特定的双精度布局)?

【问题讨论】:

如果您知道您机器的double 也是IEEE 754 双精度,您可以先将字节读取为uint64_t 并进行字节序转换,然后将memcpy 读取为double或者通过 union 类型沉迷于一些不安全的别名。 大约是多少数据量(千兆字节或 PB)? @BasileStarynkevitch,很好!也许你会笑得很开心,二进制数据大约是几千字节到几兆字节。 【参考方案1】:
typedef union 

    double d;
    uint8_t b[sizeof(double)];
u64;

inline double toDoubleLE(const uint8_t *arr, int endianess)

    u64 u;
    if (endianess)
    
        for(size_t x = 0; x < sizeof(u); x++)
        
            u.b[sizeof(u) - x - 1] = arr[x];
        
    
    else
    
        for(size_t x = 0; x < sizeof(u); x++)
        
            u.b[x] = arr[x];
        
    
    return u.d;


double fooLE(uint8_t *arr)

    return toDoubleLE(arr, 0);


double foobE(uint8_t *arr)

    return toDoubleLE(arr, 1);


编译器是“智能”的,x86-64 会将其转换为 2 个机器码操作。

fooLE:
        movzx   eax, BYTE PTR [rdi]
        movq    xmm0, rax
        ret
foobE:
        mov     rax, QWORD PTR [rdi]
        bswap   rax
        movq    xmm0, rax
        ret

https://godbolt.org/z/ofpDGe

【讨论】:

你忘了在上面定义u64 @IanAbbott 确实:) 感谢有关编译器的信息。为了清楚起见,通过这种方法,我需要检查系统的字节顺序是否正确?我假设我可以根据流的字节序检查系统的字节序,如果它们相同,则使用 memcpy,如果它们不同,则使用此字节数组反转技巧(哪个方向并不重要)。对吗? 仅当您的代码将被编译以在许多硬件架构上执行时。如果是 PC,你可以假设小端【参考方案2】:

不过,有没有更可靠的方法来解码或编码为 64 位 IEEE 754 双精度浮点数

在 POSIX 或 Unix 系统上,考虑使用 XDR 或 ASN/1。

如果数据不是太大,请考虑JSON(例如Jansson)或YAML,并决定以文本形式表示。然后仔细阅读fscanf 和fprintf 的文档(例如与%a 格式说明符相关)。

另请参阅floating-point-gui.de

【讨论】:

感谢您的指点,我从来不知道 XDR 和 ASN/1。很整洁的东西。我在 JSON/YAML 方面做了很多工作,并在 fscanf/fprintf 方面做了一些工作。有趣的是,在我开始处理这些问题之前,我一直认为二进制流非常优越。可悲的是,就像生活中的许多事情一样,我无法控制生成的二进制数据。

以上是关于从具有指定字节顺序的二进制 unsigned char* 读取 C 中的 double的主要内容,如果未能解决你的问题,请参考以下文章

将 unsigned int + 字符串转换为 unsigned char 向量

如何从长指针中提取 little-endian unsigned short?

c语言signed char, char, unsinedchar区别

Java 读取二进制文件 ,读八个字节,然后转换成一个double,怎么写? 我知道怎么读四个字节转成int的。

数据类型unsigned char表示范围(存储值的范围)是多少,为啥(写出计算过程)

unsigned char 和 signed char