[英]Print double without printf
我正在嘗試顯示一個沒有 printf 或除 stdlib.h 之外的所有其他庫的雙精度庫,用於 malloc。 我知道雙份是如何儲存的,我在計算時遇到了問題。
我知道 double 以 64 位存儲:
我使用了一些轉換來獲得所有這些值,但我未能獲得 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.