除了stdlib.h,我正在尝试显示一个不带printf或所有其他库的double,用于malloc。我知道双人间是如何储存的,我正经历着与计算有关的问题。
我知道双人有64位:
我使用了一些转换来获得所有这些值,而我在获得1分数(源代码:格式化)方面失败了,我得到了mantisma,但是我不知道如何正确地添加这个1。这里有一些代码:
double d;
unsigned long long *double_as_int;
unsigned long long value;
d = 0.5;
double_as_int = (unsigned long long *)&d;
value = *double_as_int & 0x001FFFFFFFFFFFFFULL;
printf("value = %llu\n", value); /* <- just for verification */我已经知道,要得到mantisma,我只需要做0x000FFFFFFFFFFFFULL,但是我尝试在1部分中添加一个。你们知道怎么解决这个问题吗?
发布于 2021-03-12 09:39:23
我知道双人有64位
不一定。一个"IEEE 754双精度二进制浮点“数字存储在64位。一个“双”可能是任何东西,它可能不是,它可能遵循IEEE 745标准。在假设__STDC_IEC_559__宏是C11附件F之前,您应该检查它。
如果您想要操作浮点数,您应该使用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表示一个小数,如基-2中的1.01010111..。逗号后面的数字只是浮点数的分数部分中的位,按顺序排列。下面的程序(其中有许多bug)用于以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..作为一个基2数(因此我在末尾添加了(2) )到一些“二进制到十进制转换器”中,比如速记,而基-2中的0.1是基-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()、fp()、物质。
https://stackoverflow.com/questions/66596031
复制相似问题