C 将浮点二进制转换为十进制时,结果稍微不准确
它应该做什么: 输入示例-101.101 产出-5.625 这是我写浮点二进制-十进制转换器的方式,但是有 一个小错误-问题是输出不精确到正确的小数点 我的代码的作用是: 输入-101.101 产出-5.624985 当我将计数从-16更改为-32时,我的代码会执行什么操作: 输入-101.101输出-5.625000这是正确的 输入-101.111输出-5.875061该值仍处于关闭状态,应为5.875C 将浮点二进制转换为十进制时,结果稍微不准确,c,binary,decimal,C,Binary,Decimal,它应该做什么: 输入示例-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(doub
#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;
}
#包括
双十进制(双十进制);
长整型(长整型);
int main(int argc,const char*argv[]
{
双x;
扫描频率(“%lf”、&x);
长整型整数部分=(长整型)x;
双decpart=x-intpart;
双有限元=整数(整数部分);
双精度findec=十进制(十进制部分);
双finnum=findec+finint;
printf(“%lf\n”,finnum);
返回0;
}
长整型整数(长整型整数部分)
{
双和=0;
长整数a,b,p=0;
而(intpart>0)
{
a=第%10部分;
b=a*(功率(2,p));
sum=sum+b;
p++;
intpart=intpart/10;
}
回报金额;
}
双十进制(双十进制部分)
{
双和=0;
整数计数=0;
而(depart>0&&count>-32)
{
计数--;
depart=depart*10;
如果(decpart>=1)
{
depart=depart-1;
总和=总和+功率(2,计数);
}
}
回报金额;
}
误差是由pow
函数产生的舍入误差,该函数几乎总是有一个小误差,即使对于整数参数也是如此。这是因为pow(x,y)
通常基于数学恒等式exp(log(x)*y)
实现,其中log
和exp
使用自然基2.718281828…
。因此,即使基数为2,log(2)
也是近似值,因此exp(log(2))
将更接近近似值
在您的情况下,不必使用count
和pow
,您可以有一个双值
字段,该字段从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
备选方案更准确。在符合IEEE-754标准的系统上(大多数现代系统都是),值应始终是您想要的精确值
此外,正如我/其他人所提到的,使用scanf
以double
而不是字符串的形式读取输入也会导致不准确,因为像0.1这样的数字通常无法准确存储。相反,您应该输入一个char
数组,然后解析字符串。问题是-16
,它只是65536或(0.0000153…)中的一部分。你得到的答案和愿望都在这个范围内。相反,需要一个更负的值,如-32或-53。(或关于ln2(DBL\u EPSILON)
)-
[Edit2]像-17、-18等值还有其他问题。见下文
还有if(depart>1)
-->if(depart>=1)
[编辑]
根据C规范(最多为-37
和典型的二进制浮点),合理的pow(2,count)
将为count
在-80
到+80
范围内的count
提供准确答案
一旦输入N个有效数字(“101.101”为6),您读取十进制数并将其视为二进制FP数的方法可能会崩溃。期望N
类似于1/DBL\u EPSILON
或至少8或9位数字。要想超越这个限制,建议@Drew McGowen提供建议,并将您的输入作为字符串阅读和处理
#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;
}
[编辑二]
给定一个典型的double
,N
有效位数的限制约为16或17。这不仅限制了输入,还限制了中的迭代次数,而(depart>0&&count>-16)
。更深入地说,“101.111”的字符串到FP的转换(更像是101.111000000000004…
)会产生意想不到的结果,其行为类似于101.11100000000000111111…
(数学上正确的101+1*1/2+1*1/4+1*1/8+1*pow(2,-15)+1*pow(2,-16))…=5.875061
所以。。。。。迭代decimal()
超过log10(1/DBL\u EPSILON)
或大约15,16次,开始生成垃圾。然而,代码迭代16次只能提供65536(0.000015…)中1部分的十进制精度。因此,要得到比这更好的答案,需要一种新的方法(比如@Drew McGowen,灵感来自@BLUEPIXY的字符串)
double BinaryFloatinPoint(const char *s) {
double sum = 0.0;
double power = 1.0;
char dp = '.'; // binary radix point
while (*s) {
if (*s == '0' || *s == '1') {
sum *= 2.0;
sum += *s - '0';
power *= 0.5;
} else if (*s == dp) {
dp = '0';
power = 1.0;
} else {
return 0.0; // Unexpected char, maybe return NAN instead
}
s++;
}
if (dp == '0') { // If dp found ...
sum *= power;
}
return sum;
}
无法准确表示,因为数字,例如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;
}
#包括
#包括
双BSRTOD(常量字符*bstr){
双x=0,一=1.0;
char*p=strchr(bstr,“.”);
如果(p){
char*fp=p;
而(*++fp){
一/=2.0;
如果(*fp=='1')
x+=1;
}
}否则{
p=strchr(bstr,'\0');
}
1=1.0;
做{
如果(*--p==“1”)
x+=1;
一个*=2.0;
}而(p!=bstr);
返回x;
}
内部主(空){
双x=bstrtod(“101.101”);
printf(“%f\n”,x);//5.625000
返回0;
}
这种不精确性来自四舍五入过程中的错误。即使您输入8,它实际上也存储7.99999。。。这就是问题出现的原因。精确浮点二进制到十进制转换器
此代码使用字符串将二进制输入转换为十进制输出
#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;
}
#包括
#包括
双blembo(const char*blem);
内部主(空)
{
字符输入[50];
获取(输入);
int t=0,flag=0;
while(input[t]!='\0')//这整段代码使“hello”或“921”或“1.1.0”输入无效
{
如果(