如何在 Metal Shader Language 中将浮点指针转换为 uint8_t 指针?

问题描述

我正在尝试在 kernel void 函数内用 Metal Shading Language 编写以下 C 代码

float f = 2.1;
uint8_t byteArray[sizeof(f)];
for (int a = 0; a < sizeof(f); a++) {
    byteArray[a] = ((uint8_t*)&f)[a];
}

代码应该获取浮点值的字节数组。当我尝试用 Metal Shader Language 编写相同的代码时,出现以下构建时错误

指针类型必须有明确的地址空间限定符

我知道 Metal 限制了指针的使用,并要求内核函数的参数提供显式地址空间属性,例如 deviceconstant 等。我将如何执行类型-用金属着色语言投射?

解决方法

这不是 C++,所以你必须用另一种方式来做。

您不能在 MSL 中正确使用联合或 infoLoading

相反,有一个类型为 4 reinterpret_cast 的向量,它是 uint8_t

为了做你想做的事,你会写成这样。

uchar4

请参阅 MSL spec,第 2.19 节类型转换和重新解释数据。

至于地址空间限定符:

Metal Shading Language 中的每个指针都有一个地址限定符。它可以是 float f = 2.1; uchar4 ff = as_type<uchar4>(f); deviceconstantthread 等。请参阅规范中的第 4 章。

这些地址限定符来自内存空间不同的事实。您可以在上面的文档中阅读有关它们的更多信息。

由于 threadgroup 是局部变量,它位于 f 空间中,因此您的 thread 指针将具有 uint8_t 的类型,而不仅仅是 thread uint8_t*

所以你可能会这样做:

uint8_t

但我认为 float f = 2.1; thread uint8_t* ff = (thread uint8_t*)&f; 方法要清晰得多。

,

不知道具体的 metal,但在普通 C 中,您想将 fbyteArray 放在 union

这是一些示例代码:

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

union float_byte {
    float f;
    uint8_t byteArray[sizeof(float)];
};

union float_byte u;

void
dotest(float f)
{

    u.f = f;

    printf("%.6f",u.f);
    for (int idx = 0;  idx < sizeof(u.byteArray);  ++idx)
        printf(" %2.2X",u.byteArray[idx]);
    printf("\n");
}

int
main(void)
{

    dotest(2.1);
    dotest(7.6328);

    return 0;
}

这是程序输出:

2.100000 66 66 06 40
7.632800 E6 3F F4 40

更新:

即使在今天,阅读一个不是最后一个写信的工会成员在技术上仍然是 UB 吗?虽然,这听起来现在已经通过实现定义的行为得到了广泛的支持。许多相关问题之一:stackoverflow.com/questions/2310483/... – yano

不,出于多种原因,它不是UB。

可能是“实现定义的”行为,但只是因为 CPU/处理器字节序重新。内存中 [32 位] float 的格式,如果我们希望解释 byteArray

中的字节

但是,AFAICT,这并不影响 OP 的问题,因为重点只是获得一个字节缓冲区 [用于数据的二进制/序列化?]。

如果想要解释数据(例如设计 DIY F.P. S/W 实现),则必须知道 float 的格式和字节序。这 [可能] 是 IEEE 784 格式和处理器字节序。

但是,使用 union 只是得到一个字节指针,没有问题。它甚至不是“实现定义”的行为。

这与以下内容大致相同:

float my_f = f;
uint8_t *byteArray = (uint8_t *) &my_f;

而且,它之所以有效,是因为它必须起作用。

此外,union [此处使用的] 是一个常见的习语,可以追溯到 1970 年代,因此必须得到支持。

此外,它只是工作[因为它必须按照设计]。

如果我们有:

void
funcA(float f)
{

    u.f = f;
}

void
funcB(void)
{
    for (int idx = 0;  idx < sizeof(u.byteArray);  ++idx)
        printf(" %2.2X",u.byteArray[idx]);
    printf("\n");
}

为了好玩,假设 funcAfuncB 位于单独的 .c 文件中。当调用 funcA 时,它会[以可预测的方式]更改 u 的内存。

中间的任何内容都不会改变 u 的布局。

然后,我们调用 funcBbyteArray 中的字节布局将是相同/可预测的数据。

这与将 float 作为 二进制 数据写入文件的方式相似:

#include <unistd.h>

int fd;

void
writefloat(float f)
{
    float my_f = f;

    write(fd,&my_f,sizeof(float));
}

void
writefloat2(float f)
{

    write(fd,&f,sizeof(float));
}

void
writefloat3(float f)
{

    write(fd,sizeof(f));
}

如果我们使用 uint32_t 而不是 float,也许这会更容易看出。我们可以做一个字节序测试。 [注意:这是粗略的,并没有考虑像 pdp11/vax 这样的奇怪的字节序]:

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

union uint_byte {
    uint32_t i;
    uint8_t b[sizeof(uint32_t)];
};

union uint_byte u;

int
main(void)
{

    u.i = 0x01020304;
    if (u.b[0] == 0x04)
        printf("cpu is little-endian\n");
    else
        printf("cpu is big-endian\n");

    return 0;
}
,

在 C++ (20) 中,您有 bit_cast。 在“注释”部分,您将看到,它只是作为对 memcpy 的调用实现的。

因此,为了避免“未定义行为”并确保安全(除了之前的所有方法,它也是最快的),只需执行以下操作:

memcpy(byteArray,sizeof f); //size of byteArray must be at least: sizeof f

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...