我知道 double 存储在 64 位中
不一定。 “IEEE 754 双精度二进制浮点”数以 64 位存储。 “双重”可能是任何东西,也可能不是,它可能遵循 IEEE 745 标准。在假设它是C11 Annex F 之前,您应该检查__STDC_IEC_559__ 宏。
如果你想操作浮点数,你应该使用frexp和其他专门用于抽象操作浮点数表示的函数,而不需要任何*(super unsafe casts*):
double d = DBL_MIN / 2;
int exponent;
double fraction = frexp(d, &exponent);
if (fraction == 0 && exponent == 0) abort(); /*handle error*/
printf("%g = %d * 2^%d * %f\n", d, d<0?-1:1, exponent, fraction);
如何解决这部分?
1.fraction 表示像 1.01010111.. 这样的小数 in base-2。逗号后面的 digits 只是浮点数小数部分中的位,按顺序排列。以下程序(其中有许多错误)旨在以sign * 2^(exp) * [0/1].fraction(2) 的形式输出表示形式的浮点值,其中分数以基数 2 表示:
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <math.h>
#include <stdbool.h>
#include <limits.h>
#include <float.h>
#if !__STDC_IEC_559__
#error
#endif
int main() {
double d = DBL_MIN / 2;
typedef union {
unsigned long long sign : 1;
unsigned long long exp : 11;
unsigned long long fract : 52;
} double64u;
double64u di;
static_assert(sizeof(double) == sizeof(double64u), "");
memcpy(&di, &d, sizeof(double));
// extract **binary** digits from value into buffer
char buffer[53] = {0};
char *p = buffer + 52;
unsigned long long tmp = di.fract;
for (int i = 0; i < 52; ++i) {
*(--p) = (tmp & 0x1) + '0';
tmp >>= 1;
}
char sign = di.sign < 0 ? -1 : 1;
bool normal = di.exp != 0;
printf("%g = \n", d);
if (normal) {
printf("%d * 2^(%d - 1023) * 1.%s(2)\n",
sign, di.exp, buffer);
} else {
printf("%d * 2^(1 - 1023) * 0.%s(2)\n",
sign, buffer);
}
}
在我的 x86-64 上,这个程序输出:
1.11254e-308
1 * 2^(1 - 1023) * 0.1000000000000000000000000000000000000000000000000000(2)
然后您可以将0.10.. 这是一个base 2 数字(所以我在末尾添加了(2))到一些“二进制到十进制转换器”,例如rapidtables,并且 base-2 中的 0.1 是 base-10 中的 0.5 (好吧,这个例子很简单)。所以数字是:
1 * 2^(1 - 1023) * 0.5
然后你可以使用bc这样的无限计算器并输入数字来计算实际结果:
$ bc
scale=400
1 * 2^(1 - 1023) * 0.5
.0000000000000000000000000000000000000000000000000000000000000000000\
00000000000000000000000000000000000000000000000000000000000000000000\
00000000000000000000000000000000000000000000000000000000000000000000\
00000000000000000000000000000000000000000000000000000000000000000000\
00000000000000000000000000000000000011125369292536006915451163586662\
0203210960799023116591527666370844360221740695909792714157950
与1.11254e-308相同的号码。
自己打印浮点数是一项非常艰巨的工作。我可以推荐https://www.ryanjuckett.com/printing-floating-point-numbers/ 以及介绍 Grisu3 和 Ryu 以及 Errol1 算法的论文。如需灵感,请阅读现有实现的代码:newlib vfprintf.c cvt()、musl vfprintf.c fmt_fp()、glibc printf_fp_ stuff。