如何打印(二进制)文件中的半精度/bfloat16 值?

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

这是以下变体:

如何在shell中从二进制文件打印浮点值?

在这个问题中,我们想要从二进制文件打印 IEEE 754 单精度(即 32 位)浮点值。

现在假设我想打印半精度(即16位)浮点数。

od
似乎不喜欢这样做:

$ od -t f2 c.bin
od: invalid type string ‘f2’;
this system doesn't provide a 2-byte floating point type

perl 的包 也没有...

如果您的答案还涵盖具有 bfloat16(也是 16 位)值的二进制文件,则会加分。

command-line floating-point binary-data half-precision-float
2个回答
1
投票

以下是 SO 的“Ask with AI”为 IEEE 754 半精度生成的代码(我问了 2 个不同的问题,合并了代码,并添加了缺失的导入):

#!/usr/bin/env python3

import struct
import sys

def half_to_double(half):
    # Pack the half-precision number into bytes
    packed_half = struct.pack('H', half)

    # Unpack the bytes into the corresponding format
    unpacked_half = struct.unpack('e', packed_half)[0]

    # Convert the unpacked value to double-precision
    double = float(unpacked_half)

    return double

# Read 2 bytes from stdin
data = sys.stdin.buffer.read(2)

# Unpack the bytes into a 2-byte integer
half_number = struct.unpack('<h', data)[0]

double_number = half_to_double(half_number)
print(double_number)

在 bash 或 zsh 下测试(用于

printf
\x
支持),为小端机器订购 2 个字节:

vlefevre@cventin:~$ printf "\x00\x00" | ./tst.py
0.0
vlefevre@cventin:~$ printf "\x00\x3c" | ./tst.py
1.0
vlefevre@cventin:~$ printf "\x01\x3c" | ./tst.py
1.0009765625
vlefevre@cventin:~$ printf "\xff\x7b" | ./tst.py
65504.0

这可以通过维基百科上半精度浮点格式给出的示例进行检查。

这也是 Perl 的版本,但仅适用于普通数字。该代码也是由 SO 的“Ask with AI”生成的,但它是错误的(它混合了 32 位和 64 位数字),所以我必须修复它(并适应问题的开头)。

#!/usr/bin/env perl

use strict;

# Code from SO's "Ask with AI" with various fixes.
# For normal numbers only!

# Read the binary16 number from stdin
my $binary16;
read(STDIN, $binary16, 2) == 2 or die;

# Convert the binary16 number to an unsigned short integer
my $unsigned_short = unpack 'S', $binary16;
printf "%04X\n", $unsigned_short;

# Extract the sign bit, exponent, and significand from the unsigned short
my $sign = ($unsigned_short & 0x8000) >> 15;
my $exponent = ($unsigned_short & 0x7C00) >> 10;
my $significand = $unsigned_short & 0x03FF;

# Convert the binary16 components to binary32 components
my $exponent_bias = 15;  # Bias for the binary16 exponent
my $exponent_offset = 127;  # Offset for the binary32 exponent

my $binary32_sign = $sign;
my $binary32_exponent = ($exponent - $exponent_bias) + $exponent_offset;
my $binary32_significand = $significand << 13;

print "$sign $exponent $significand\n";

# Combine the binary32 components into a binary string
my $binary32_string = pack 'N',
  $binary32_sign << 31 | $binary32_exponent << 23 | $binary32_significand;

# Unpack the binary string as a float
my $converted_number = unpack 'f>', $binary32_string;

print "$converted_number\n";

对于bfloat16,这更简单,因为它可以被视为binary32二进制字符串的截断。因此只需将 2 字节整数向左移动 16 位(即在右侧插入 16 个零)。这是代码:

#!/usr/bin/env perl

use strict;

# Read the bfloat16 number from stdin
my $bfloat16;
read(STDIN, $bfloat16, 2) == 2 or die;

# Convert the bfloat16 number to an unsigned short integer
my $unsigned_short = unpack 'S', $bfloat16;
printf "%04X\n", $unsigned_short;

my $binary32_string = pack 'N', $unsigned_short << 16;

# Unpack the binary32 string as a float
my $converted_number = unpack 'f>', $binary32_string;

print "$converted_number\n";

这可以在维基百科上的bfloat16浮点格式的示例中进行检查。


0
投票

GNU coreutils 将在 9.5 版本中添加对此的支持,分别使用 -tfH 和 -tfB 类型:

$ printf '\x3F\x80\x00\x00' | od -An --endian=big -tfH -tf2 -tfB -tfF
           1.875               0
           1.875               0
               1               0
                               1
© www.soinside.com 2019 - 2024. All rights reserved.