Buffer 和 TypedArray 转换导致数据突变?

Buffer and TypedArray conversion causes data mutation?

我正在通过 XHR 加载远程资产并以 arrayBuffers 的形式接收它们。基础数据是 float32 依赖的。我需要对这些数组缓冲区做一些进一步的操作,比如连接和合并。对于这些操作,我正在试验 buffer module.

我发现使用 Buffer 模块和底层 Uint8Array 正在改变预期的 float32 输出

new Float32Array构造函数实例化TypedArray时,数据是正确的。

new Float32Array(action.payload.arrayBuffer)
Float32Array(9072) [-5, 35, -5, 255, 127, 128, 128, 1, 0.2666666805744171, 0.2666666805744171, 0.2666666805744171, 1, -5, 34, -5, 255, 127, 128, 128, 1, 0.2666666805744171, 0.2666666805744171, 0.2666666805744171, 1, -5, 35, -2, 255, 127, 128, 128, 1, 0.2666666805744171, 0.2666666805744171, 0.2666666805744171, 1, -5, 34, -2, 255, 127, 128, 128, 1, 0.2666666805744171, 0.2666666805744171, 0.2666666805744171, 1, -5, 35, -2, 255, 127, 128, 128, 1, 0.2666666805744171, 0.2666666805744171, 0.2666666805744171, 1, -5, 34, -5, 255, 127, 128, 128, 1, 0.2666666805744171, 0.2666666805744171, 0.2666666805744171, 1, -5, 38, -2, 255, 127, 128, 128, 2, 0.9411764740943909, 0.3686274588108063, 0.10980392247438431, 1, -5, 34, -2, 255, 127, 128, 128, 2, 0.9411764740943909, 0.3686274588108063, 0.10980392247438431, 1, -5, 38, 1, 255, …]

当使用 from 方法初始化 TypedArray 时,数据为空。这是预期的,因为 from 方法需要一个可迭代的数组,而 arrayBuffer 不是。

Float32Array.from(action.payload.arrayBuffer)
Float32Array []

从使用底层 Uint8ArrayUint8ArrayBuffer 转换时,浮点数据会发生变化。

Float32Array.from(new Uint8Array(action.payload.arrayBuffer))
Float32Array(36288) [0, 0, 160, 192, 0, 0, 12, 66, 0, 0, 160, 192, 0, 0, 127, 67, 0, 0, 254, 66, 0, 0, 0, 67, 0, 0, 0, 67, 0, 0, 128, 63, 137, 136, 136, 62, 137, 136, 136, 62, 137, 136, 136, 62, 0, 0, 128, 63, 0, 0, 160, 192, 0, 0, 8, 66, 0, 0, 160, 192, 0, 0, 127, 67, 0, 0, 254, 66, 0, 0, 0, 67, 0, 0, 0, 67, 0, 0, 128, 63, 137, 136, 136, 62, 137, 136, 136, 62, 137, 136, 136, 62, 0, 0, 128, 63, 0, 0, 160, 192, …]

Float32Array.from(Buffer.from(action.payload.arrayBuffer))
    Float32Array(36288) [0, 0, 160, 192, 0, 0, 12, 66, 0, 0, 160, 192, 0, 0, 127, 67, 0, 0, 254, 66, 0, 0, 0, 67, 0, 0, 0, 67, 0, 0, 128, 63, 137, 136, 136, 62, 137, 136, 136, 62, 137, 136, 136, 62, 0, 0, 128, 63, 0, 0, 160, 192, 0, 0, 8, 66, 0, 0, 160, 192, 0, 0, 127, 67, 0, 0, 254, 66, 0, 0, 0, 67, 0, 0, 0, 67, 0, 0, 128, 63, 137, 136, 136, 62, 137, 136, 136, 62, 137, 136, 136, 62, 0, 0, 128, 63, 0, 0, 160, 192, …]

最终输出的这种差异完全破坏了我期望 new Float32Array 输出

的工作管道

我正在寻找关于为什么会发生这种情况的解释和教育回应。

这是因为 Uint8 数组(视图)保存的每个值都基于视图转换为完整的 Float32 值,而不是底层 ArrayBuffer。

如果您有一个包含例如 4 个条目的 Uint8Array 视图:

Uint8 -> [1,2,3,4]

在 ArrayBuffer 中它看起来像这样(ArrayBuffer 始终是一个字节数组):

0x01020304

然后发生的事情是,当您将 Uint8 视图转换为 Float32 视图时,使用 view 表示中的值(即 [1,2,3,4]) ,而不是底层的 ArrayBuffer,因此这些条目只是简单地转换为新条目,但在不同的 type 中仍然表示与原始视图表示相同的数字 [1,2,3,4] :

Float32 -> [1,2,3,4]   (each entry stored as 32-bit value)

新的 ArrayBuffer 看起来像(手动转换,但你会明白)由 Float32 表示的 4 个字节(32 位)x 4 个值组成 - 还要注意浮点值编码为 IEEE754 Float32 类型表示的每个数字使用 4 个字节的格式:

0x00003F80 00000040 00004040 00004080 (=1,2,3,4 as IEEE754 encoded floating point values)

你也可以从新的底层 ArrayBuffer 的字节大小看出这一点(从问题中的数字):

9,072 x 4 = 36,288 bytes

为了获得您期望的结果,您必须直接通过其 buffer 属性:

使用缓冲区
new Float32Array(uint8array.buffer);  // notice .buffer -> actual ArrayBuffer

顺便说一下,这与您在问题第一行中所做的相同:

new Float32Array(action.payload.arrayBuffer);

字节顺序是一方面,但由于您的数字直接从 XHR 使用 Float32 视图时是正确的,在这种情况下这可能不是问题。

简而言之:没有发生突变,但不同类型(通过视图)需要不同数量的字节,如本例所示,Uint8 是来自底层 ArrayBuffer 的单个字节,而 Float32 是来自底层 ArrayBuffer 的 4 个字节,这意味着它们将转换为通过视图显示的不同值。