如何从内存中的原始字节读取浮点值?

问题描述 投票:0回答:4

我正在尝试从原始字节数组中读取浮点值。我有一个指向原始字节数组的指针,我想读取与原始字节相关联的浮点值。我正在使用联合数据结构来读取浮点值,但是我无法读取正确的值。

// Floating point value: 0x3F800000 (floating point value 1.0)
char * c = "\u003F\u0080\u0000\u0000";
union char4_or_float {
    char element[4];
    float val;
} cf;
cf.element[0] = c[0];
cf.element[1] = c[1];
cf.element[2] = c[2];
cf.element[3] = c[3];
printf("%f", cf.val);

预期结果:1.0,返回输出:0.0

我想知道这段代码是否正确。如果没有,你能告诉我如何解决它吗?另外,如果有它不起作用的场景,你能提到它们吗?

谢谢。

c++ unions rawbytestring
4个回答
3
投票

你有两个问题:

  1. 使用 unicode 字符不一定以字符串中的预期字节结束,请尝试
    const char * c = "\x3F\x80\x00\x00";
    代替
  2. 你可能在一台小端机器上运行,你的字节是大端所以你需要在你复制时交换:
cf.element[0] = c[3];
cf.element[1] = c[2];
cf.element[2] = c[1];
cf.element[3] = c[0];

虽然以上所有都依赖于未定义的行为,但 memcpy 会更简单和合法:

#include <cstdio>
#include <cstring>

int main()
{
    const char * c = "\x00\x00\x80\x3f";
    float f;
    std::memcpy(&f, c, sizeof(f));
    printf("%f", f);
}

0
投票

几个问题:

  1. 对于文字,您应该将 char 定义为 const char*

  2. Unicode 文字应以“u”开头并分配给

    const char16_t*
    而不是
    const char*

  3. 您可以使用带有“x”前缀的文字

    const char*

  4. 总是使用 memcpy 来避免别名。按照标准,执行“联合方式”通常是未定义的行为,尽管它过去工作正常。

  5. PC 是小端字节序所以字节的顺序是颠倒的

这是我的看法:

#include <cstdio>
#include <cstring>
#include <cstdint>

int main() 
{
    const char * c = "\x00\x00\x80\x3f";
    const char16_t * d = u"\u0000\u3f80";
    float val;
    memcpy( &val, c, sizeof(val));
    printf("%f\n", val);
    memcpy( &val, d, sizeof(val));
    printf("%f\n", val);

    uint32_t ival;
    memcpy(&ival,c,sizeof(ival));
    printf( "%08x\n", ival );
    memcpy(&ival,c,sizeof(ival));
    printf( "%08x\n", ival );
}

这打印

1.000000
1.000000
3f800000
3f800000

Godbolt 链接:https://godbolt.org/z/nsrGbaYn1


0
投票

您不能以可移植的方式使用联合进行类型双关,标准 C++ 不允许这样做。此外,您需要注意字节顺序。

下面我从一个

float
开始。这可以被视为一个字节数组。我将它复制到第二个
char
数组。我这样做是为了获得字节 -> 浮点部分的正确输入,然后通过将字节 memcopying 到
float
:

#include <iostream>
#include <cstring>

int main() {
     
    // prepare the right input with right endianess
    float x = 1.0;
    char* ptr = reinterpret_cast<char*>(&x);
    char* ptr_copy = new char[sizeof(float)];
    for (unsigned i=0;i<sizeof(float);++i) {
        std::cout << static_cast<unsigned>(ptr[i]) << " ";
        ptr_copy[i] = ptr[i];
    }

    // now ptr_copy is the array of bytes that can be 
    // transformed to a float via memcpy        
    float y;
    std::memcpy(&y,ptr_copy,sizeof(float));
    std::cout << "\n" << y;
}

现场演示


0
投票

您遇到的一个问题是您尝试创建的字符串不是您实际创建的字符串。您正在使用 \u,它被解析为 unicode 字符(在您的情况下无论如何都是无效的)。如果你试图在内存中为 0x3F800000 创建原始字节,你应该像这样转义它们:

"\x3f\x80\x00\x00"

但这会出现第二个问题,您正在处理哪种字节序(可能是小字节序),因为您将原始字节指定为内存中的连续字节,您必须意识到这一点。

"\x3f\x80\x00\x00" 将在大端生成 0x3f800000

"\x00\x00\x80\x3f" 将在小端中产生 0x3f800000

因此更改该行将使您的代码正常工作(如果您使用的是小端平台)

// char * c = "\u003F\u0080\u0000\u0000";
char * c = "\x00\x00\x80\x3f"; // little endian for float 1

当你将这个问题标记为 C++ 时,我会提到你将原始字节读入浮点数的方式应该是这样的:

char *rawbytes="...";

float f=*reinterpret_cast<float*>(rawbytes);


如果原始字节的字节顺序与您的系统不同,则必须交换字节。直到 C++23 才为它内置了一个,所以你可能应该使用这样的东西:

template<typename T, typename std::enable_if<std::is_integral_v<T> && sizeof(T)==4,int>::type=0>
constexpr inline void binarySwap(T &value) {
    std::uint32_t tmp = ((value << 8) & 0xFF00FF00) | ((value >> 8) & 0xFF00FF);
    value = (tmp << 16) | (tmp >> 16);
}

当然,字节交换功能将取决于您正在处理的浮点类型的大小。根据你的问题,我在这里展示了 32 位版本。

© www.soinside.com 2019 - 2024. All rights reserved.