首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >不带打印的双打印

不带打印的双打印
EN

Stack Overflow用户
提问于 2021-03-12 07:43:12
回答 1查看 325关注 0票数 2

除了stdlib.h,我正在尝试显示一个不带printf或所有其他库的double,用于malloc。我知道双人间是如何储存的,我正经历着与计算有关的问题。

我知道双人有64位:

  • 1作标志;
  • 指数11;
  • 52按价值计算;

我使用了一些转换来获得所有这些值,而我在获得1分数(源代码:格式化)方面失败了,我得到了mantisma,但是我不知道如何正确地添加这个1。这里有一些代码:

代码语言:javascript
复制
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部分中添加一个。你们知道怎么解决这个问题吗?

EN

回答 1

Stack Overflow用户

发布于 2021-03-12 09:39:23

我知道双人有64位

不一定。一个"IEEE 754双精度二进制浮点“数字存储在64位。一个“双”可能是任何东西,它可能不是,它可能遵循IEEE 745标准。在假设__STDC_IEC_559__宏是C11附件F之前,您应该检查它。

如果您想要操作浮点数,您应该使用frexp和其他专门用来抽象地操作浮点数表示的函数,而不需要任何*(super unsafe casts*)

代码语言:javascript
复制
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中:

代码语言:javascript
复制
#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上,这个程序输出:

代码语言:javascript
复制
1.11254e-308
1 * 2^(1 - 1023) * 0.1000000000000000000000000000000000000000000000000000(2)

然后,您可以将0.10..作为一个基2数(因此我在末尾添加了(2) )到一些“二进制到十进制转换器”中,比如速记,而基-2中的0.1是基-10中的0.5 (好吧,这个例子无论如何都很简单)。数字是:

代码语言:javascript
复制
 1 * 2^(1 - 1023) * 0.5

然后,您可以使用一些像bc这样的无限计算器并输入数字来计算实际结果:

代码语言:javascript
复制
$ 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()物质

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/66596031

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档