不幸的是,您无法从printf 中移植相同 格式0xa.1fp10。 C 标准规定%a 的输出是这样的:对于非零的normal 双精度,在@ 之前一个 非零数字之前 987654326@ 以及在. 之后 精确表示值所需的位数。实现可以选择有多少第一位进入第一位!
但是 C11 标准有脚注 278 说明
二进制实现可以选择小数点字符左侧的十六进制数字,以便后续数字与半字节(4 位)边界对齐。
这就是问题所在。由于 IEEE 754 doubles 有 53 位尾数; 正常数字的第一位是 1;其余 52 位可被 4 整除,该脚注之后的实现(我的机器上的 Glibc 似乎是一个),将总是输出任何有限的非零浮点数,以便它开始0x1.!
试试这个最小的程序:
#include <stdio.h>
int main(void) {
for (double i = 1; i < 1024 * 1024; i *= 2) {
printf("%a %a %a\n", 1.0 * i, 0.7 * i, 0.67 * i);
}
}
我电脑上的输出是
0x1p+0 0x1.6666666666666p-1 0x1.570a3d70a3d71p-1
0x1p+1 0x1.6666666666666p+0 0x1.570a3d70a3d71p+0
0x1p+2 0x1.6666666666666p+1 0x1.570a3d70a3d71p+1
0x1p+3 0x1.6666666666666p+2 0x1.570a3d70a3d71p+2
0x1p+4 0x1.6666666666666p+3 0x1.570a3d70a3d71p+3
0x1p+5 0x1.6666666666666p+4 0x1.570a3d70a3d71p+4
0x1p+6 0x1.6666666666666p+5 0x1.570a3d70a3d71p+5
0x1p+7 0x1.6666666666666p+6 0x1.570a3d70a3d71p+6
0x1p+8 0x1.6666666666666p+7 0x1.570a3d70a3d71p+7
0x1p+9 0x1.6666666666666p+8 0x1.570a3d70a3d71p+8
0x1p+10 0x1.6666666666666p+9 0x1.570a3d70a3d71p+9
0x1p+11 0x1.6666666666666p+10 0x1.570a3d70a3d71p+10
0x1p+12 0x1.6666666666666p+11 0x1.570a3d70a3d71p+11
0x1p+13 0x1.6666666666666p+12 0x1.570a3d70a3d71p+12
0x1p+14 0x1.6666666666666p+13 0x1.570a3d70a3d71p+13
0x1p+15 0x1.6666666666666p+14 0x1.570a3d70a3d71p+14
0x1p+16 0x1.6666666666666p+15 0x1.570a3d70a3d71p+15
0x1p+17 0x1.6666666666666p+16 0x1.570a3d70a3d71p+16
0x1p+18 0x1.6666666666666p+17 0x1.570a3d70a3d71p+17
0x1p+19 0x1.6666666666666p+18 0x1.570a3d70a3d71p+18
此输出是高效 - 对于每个 正常 数字,代码只需输出 0x1. 后跟尾数的所有实际半字节转换为十六进制,strip尾随 0 字符并附加 p+ 后跟指数。
对于长双精度,x86 格式具有 64 位尾数。由于 64 位可以精确地分割成半字节,一个合理的实现将在 normal 数字的 . 之前有一个 full 半字节,其值从 0x8 到 @987654337 不等@(第一位始终为 1),该点后面最多 15 个半字节。
尝试你的实现
#include <stdio.h>
int main(void) {
for (long double i = 1; i < 32; i ++) {
printf("%La\n", i);
}
}
看看是否符合这个预期……
在正正数和零之间可能存在次正数 - 我的 Glibc 用 0x0. 表示这些双精度值,后跟尾数的实际半字节,去除尾随零,以及固定指数 -1022 - 再次,表示是最容易实现且计算速度最快的表示。