它应该做什么: 输入示例 - 101.101 输出 - 5.625
这是我写一个浮点二进制 - 十进制转换器的方式,但有 一个小错误 - 问题是输出不准确到正确的小数点。
我的代码做了什么: 输入 - 101.101 输出 - 5.624985
当我将计数从-16更改为-32时,我的代码执行了什么操作:
输入 - 101.101输出 - 5.625000这是正确的。
输入 - 101.111输出 - 5.875061这仍然是关闭它应该是5.875
#include <stdio.h>
double decimal(double decpart);
long int integer(long int intpart);
int main(int argc, const char * argv[])
{
double x;
scanf("%lf", &x);
long int intpart = (long int)x;
double decpart = x-intpart;
double finint = integer(intpart);
double findec = decimal(decpart);
double finnum = findec + finint;
printf("%lf\n",finnum);
return 0;
}
long int integer(long int intpart)
{
double sum = 0;
long int a, b, p= 0;
while(intpart>0)
{
a = intpart % 10;
b = a*(pow(2, p));
sum = sum + b;
p++;
intpart = intpart / 10;
}
return sum;
}
double decimal(double decpart)
{
double sum = 0;
int count = 0;
while (decpart > 0 && count > -32)
{
count--;
decpart = decpart*10;
if (decpart >= 1)
{
decpart = decpart - 1;
sum = sum + pow(2, count);
}
}
return sum;
}
答案 0 :(得分:2)
不准确性是从pow
函数构建的舍入错误,即使对于整数参数,它几乎总是有一个小错误。这是因为pow(x, y)
通常基于数学身份exp(log(x) * y)
实施,其中log
和exp
使用自然基础2.718281828...
。因此,即使在例如基数为2,log(2)
是近似值,因此exp(log(2))
将更接近近似值。
在您的情况下,您可以拥有一个count
字段,而不是pow
和double value
,而0.5
字段从0.5
开始,并乘以double decimal(double decpart)
{
double sum = 0;
double value = 0.5;
while (decpart > 0 && value > 1.0e-5) // approx. 2 ^ -16
{
decpart = decpart*10;
printf("%lf\n",decpart);
if (decpart > 1)
{
decpart = decpart - 1;
sum = sum + value;
}
value *= 0.5;
}
return sum;
}
每次迭代后:
pow
一般来说,这比value
替代方案更准确。在符合IEEE-754标准的系统(大多数现代系统)上,scanf
应该始终是您想要的确切值。
此外,正如我/其他人所提到的,使用double
将输入读作char
而不是字符串也导致不准确,如0.1之类的数字往往无法准确存储。相反,您应该输入{{1}}数组,然后解析字符串。
答案 1 :(得分:2)
问题是-16
,这只是65,536或(0.0000153 ......)中的一部分。你得到的答案和愿望都在这个范围内。相反,需要一个更负的值,如-32或-53。 (或约ln2(DBL_EPSILON)
) -
[Edit2] -17,-18等值也有其他问题。见下文。
另外if (decpart > 1)
- &gt; if (decpart >= 1)
。
[编辑]
根据大多数 DBL_MIN_10_EXP
的{{1}}的C规范和典型的二进制浮点数,合理的-37
将提供完全 pow(2, count)
的答案范围为count
到-80
。
一旦输入N位有效数字(“101.101”为6),您读取十进制数字并将其视为二进制FP编号的方法可能会中断。预计+80
应为N
或至少8或9位数字。要超出该限制,建议@Drew McGowen建议,并将您的输入作为字符串进行读取和处理。
[EDIT2]
鉴于典型1/DBL_EPSILON
,double
有效数字的限制大约为16或17.这不仅限制了输入,而且还限制了迭代次数N
。比这更深入,FP转换为“101.111”(更像while (decpart > 0 && count > -16)
)的字符串会产生意外结果,如101.111000000000004...
(数学上正确101 + 1 * 1/2 + 1 * 1/4 + 1 * 1/8 + 1 * pow(2,-15)+ 1 * pow(2,-16)))... = 5.875061
所以......迭代101.111000000000001111111...
超过decimal()
或大约15,16次,开始产生废话。然而,迭代16次的代码仅提供65,536(0.000015 ...)中1个部分的小数精度。因此,要获得更好的答案,需要一种新的方法(如字母@Drew McGowen,灵感来自@BLUEPIXY)。
log10(1/DBL_EPSILON)
答案 2 :(得分:2)
无法准确表达,因为0.1
(10)等数字是二进制的无限小数。
所以我建议你将输入转换为字符串。
#include <stdio.h>
#include <string.h>
double bstrtod(const char *bstr){
double x = 0, one = 1.0;
char *p = strchr(bstr, '.');
if(p){
char *fp = p;
while(*++fp){
one /= 2.0;
if(*fp=='1')
x += one;
}
} else {
p = strchr(bstr, '\0');
}
one = 1.0;
do{
if(*--p == '1')
x += one;
one *= 2.0;
}while(p!=bstr);
return x;
}
int main(void){
double x = bstrtod("101.101");
printf("%f\n", x);//5.625000
return 0;
}
答案 3 :(得分:0)
这种不准确性来自四舍五入期间的错误。即使您输入8,它实际上存储了7.99999 ...这就是问题发生的原因。
答案 4 :(得分:0)
准确的浮点二进制到十进制转换器
此代码使用字符串将二进制输入转换为十进制输出
#include <stdio.h>
#include <string.h>
double blembo(const char *blem);
int main(void)
{
char input[50];
gets(input);
int t = 0, flag = 0;
while (input[t] != '\0') // This whole block of code invalidates "hello" or "921" or "1.1.0" inputs
{
if(input[t] == '0' || input[t] == '1' || input[t] == '.')
{
if(input[t] == '.')
{
if(flag != 1)
{
flag = 1;
t++;
continue;
}
else
{
printf("\nIncorrect input\n");
return 0;
}
}
else
{
t++;
continue;
}
}
else
{
printf("\nIncorrect input\n");
return 0;
}
}
double output = blembo(input);
printf("%lf\n", output);
return 0;
}
double blembo (const char *blem)
{
double x=0, one = 1.0;
char *p , *fp;
p = strchr(blem, '.');
if(p)
{
fp = p;
while(*++fp)
{
one = one / 2.0;
if(*fp == '1')
{
x = x + one;
}
}
}
else
{
p = strchr(blem, '\0');
}
one = 1.0;
do
{
if(*--p == '1')
{
x = x + one;
}
one = one * 2.0;
}
while(p!=blem);
return x;
}