可能沒有簡單的方法。這是一個相當棘手的問題。
你的代碼是不是解決它的權利有以下幾個原因:
- 浮點運算是不是小數的大多數實際實現,它們是二進制。因此,當您將浮點數乘以10或將其除以10時,可能會失去精度(這取決於數字)。
- 即使標準
64-bit IEEE-754
浮點格式儲量的尾數,這相當於floor(log10(2^53))
= 15
十進制數字53
位,在該格式的有效數量可能需要多達一些1080
十進制數字的小數部分打印完全正確,這是你似乎在問的問題。
解決的方法之一是使用snprintf()
的%a
格式類型說明符,這是會使用尾數和1999年的保證,這將打印所有的C標準的十六進制數字打印的浮點值如果浮點格式爲基數2(AKA base-2或簡單二進制),則爲有效數字。所以,用這個你可以獲得數字尾數的所有二進制數字。從這裏你可以知道小數部分有多少個小數位。
現在,請注意:
1。00000 = 2 = 1.00000(二進制)
0.50000 = 2 -1 = 0.10000
0.25000 = 2 -2 = 0.01000
0.12500 = 2 -3 = 0.00100
0.06250 = 2 -4 = 0.00010
0.03125 = 2 -5 = 0.00001
等。
你可以清楚地看到這裏,在i
個位置中的二進制表示的點右邊二進制數字產生的最後一個非零十進制數字也i
個位置的右側十進制表示中的點。因此,如果您知道二進制浮點數中最低有效非零位的位置,則可以計算出精確打印數字的小數部分所需的小數位數。
這就是我的程序正在做的事情。
代碼:
// file: PrintFullFraction.c
//
// compile with gcc 4.6.2 or better:
// gcc -Wall -Wextra -std=c99 -O2 PrintFullFraction.c -o PrintFullFraction.exe
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <float.h>
#include <assert.h>
#if FLT_RADIX != 2
#error currently supported only FLT_RADIX = 2
#endif
int FractionalDigits(double d)
{
char buf[
1 + // sign, '-' or '+'
(sizeof(d) * CHAR_BIT + 3)/4 + // mantissa hex digits max
1 + // decimal point, '.'
1 + // mantissa-exponent separator, 'p'
1 + // mantissa sign, '-' or '+'
(sizeof(d) * CHAR_BIT + 2)/3 + // exponent decimal digits max
1 // string terminator, '\0'
];
int n;
char *pp, *p;
int e, lsbFound, lsbPos;
// convert d into "+/- 0x h.hhhh p +/- ddd" representation and check for errors
if ((n = snprintf(buf, sizeof(buf), "%+a", d)) < 0 ||
(unsigned)n >= sizeof(buf))
return -1;
//printf("{%s}", buf);
// make sure the conversion didn't produce something like "nan" or "inf"
// instead of "+/- 0x h.hhhh p +/- ddd"
if (strstr(buf, "0x") != buf + 1 ||
(pp = strchr(buf, 'p')) == NULL)
return 0;
// extract the base-2 exponent manually, checking for overflows
e = 0;
p = pp + 1 + (pp[1] == '-' || pp[1] == '+'); // skip the exponent sign at first
for (; *p != '\0'; p++)
{
if (e > INT_MAX/10)
return -2;
e *= 10;
if (e > INT_MAX - (*p - '0'))
return -2;
e += *p - '0';
}
if (pp[1] == '-') // apply the sign to the exponent
e = -e;
//printf("[%s|%d]", buf, e);
// find the position of the least significant non-zero bit
lsbFound = lsbPos = 0;
for (p = pp - 1; *p != 'x'; p--)
{
if (*p == '.')
continue;
if (!lsbFound)
{
int hdigit = (*p >= 'a') ? (*p - 'a' + 10) : (*p - '0'); // assuming ASCII chars
if (hdigit)
{
static const int lsbPosInNibble[16] = { 0,4,3,4, 2,4,3,4, 1,4,3,4, 2,4,3,4 };
lsbFound = 1;
lsbPos = -lsbPosInNibble[hdigit];
}
}
else
{
lsbPos -= 4;
}
}
lsbPos += 4;
if (!lsbFound)
return 0; // d is 0 (integer)
// adjust the least significant non-zero bit position
// by the base-2 exponent (just add them), checking
// for overflows
if (lsbPos >= 0 && e >= 0)
return 0; // lsbPos + e >= 0, d is integer
if (lsbPos < 0 && e < 0)
if (lsbPos < INT_MIN - e)
return -2; // d isn't integer and needs too many fractional digits
if ((lsbPos += e) >= 0)
return 0; // d is integer
if (lsbPos == INT_MIN && -INT_MAX != INT_MIN)
return -2; // d isn't integer and needs too many fractional digits
return -lsbPos;
}
const double testData[] =
{
0,
1, // 2^0
0.5, // 2^-1
0.25, // 2^-2
0.125,
0.0625, // ...
0.03125,
0.015625,
0.0078125, // 2^-7
1.0/256, // 2^-8
1.0/256/256, // 2^-16
1.0/256/256/256, // 2^-24
1.0/256/256/256/256, // 2^-32
1.0/256/256/256/256/256/256/256/256, // 2^-64
3.14159265358979323846264338327950288419716939937510582097494459,
0.1,
INFINITY,
#ifdef NAN
NAN,
#endif
DBL_MIN
};
int main(void)
{
unsigned i;
for (i = 0; i < sizeof(testData)/sizeof(testData[0]); i++)
{
int digits = FractionalDigits(testData[i]);
assert(digits >= 0);
printf("%f %e %.*f\n", testData[i], testData[i], digits, testData[i]);
}
return 0;
}
輸出(ideone):
0.000000 0.000000e+00 0
1.000000 1.000000e+00 1
0.500000 5.000000e-01 0.5
0.250000 2.500000e-01 0.25
0.125000 1.250000e-01 0.125
0.062500 6.250000e-02 0.0625
0.031250 3.125000e-02 0.03125
0.015625 1.562500e-02 0.015625
0.007812 7.812500e-03 0.0078125
0.003906 3.906250e-03 0.00390625
0.000015 1.525879e-05 0.0000152587890625
0.000000 5.960464e-08 0.000000059604644775390625
0.000000 2.328306e-10 0.00000000023283064365386962890625
0.000000 5.421011e-20 0.0000000000000000000542101086242752217003726400434970855712890625
3.141593 3.141593e+00 3.141592653589793115997963468544185161590576171875
0.100000 1.000000e-01 0.1000000000000000055511151231257827021181583404541015625
inf inf inf
nan nan nan
0.000000 2.225074e-308 0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002225073858507201383090232717332404064219215980462331830553327416887204434813918195854283159012511020564067339731035811005152434161553460108856
你可以看到,π
和0.1
是唯一真正達到15
十進制數字,其餘的這些數字顯示了這些數字真正起了什麼作用因爲這些數字不能完全用二進制浮點格式表示。
你也可以看到,DBL_MIN
,最小正正規化double
值,在小數部分1022
數字和那些有715
顯著數字。
這種解決方案可能出現的問題:
- 編譯器的
printf()
功能不支持%a
或不正確打印的精度要求的所有數字(這是很可能的)。
- 您的計算機使用非二進制浮點格式(這是非常罕見的)。
您可以使用'sprintf(line,「%。* f」,pl,d);'而不是'switch'。 – 2013-03-04 05:18:55