簡體   English   中英

無 printf 打印雙

[英]Print double without printf

我正在嘗試顯示一個沒有 printf 或除 stdlib.h 之外的所有其他庫的雙精度庫,用於 malloc。 我知道雙份是如何儲存的,我在計算時遇到了問題。

我知道 double 以 64 位存儲:

  • 1 為標志;
  • 11 為指數;
  • 52 的價值;

我使用了一些轉換來獲得所有這些值,但我未能獲得 1.fraction(來源: https://en.wikipedia.org/wiki/Double-precision_floating-point_format ),我得到了 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.fraction 部分中添加一個。 你們知道如何解決這部分嗎?

我知道 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表示一個小數,例如base-2 中的1.01010111.. 逗號后面的數字只是浮點數小數部分中的位,按順序排列。 以下程序(其中包含許多錯誤)旨在 output 以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) )到一些“二進制到十進制轉換器”,例如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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM