我正在尝试将自己的字符串写入浮点转换函数。它基本上是strtof()
的廉价ripoff,但我不能让它完全模仿strtof()
。我不希望我的函数完全模仿strtof()
,但我想知道它为什么会有所不同。我已经测试了几个不同的字符串,并且我发现以下字符串在赋予我的函数时被赋予strtof()
以及使用printf("%.38f"))
打印时具有不同的值。
为什么会这样? (也可以随意指出任何其他错误,或告诉我任何其他字符串也有不同的值(我无法找到所有这些)。)
#include <stdlib.h>
#include <stdio.h>
#include <float.h>
#include <math.h>
int dec_to_f(char *dec, float *f)
{
int i = 0;
float tmp_f = 0;
if(dec == NULL) return 1;
if(f == NULL) return 2;
if(dec[i] == '\000') return 3;
if(dec[i] == '-')
{
i++;
if(dec[i] == '\000') return 3;
for(; dec[i] != '\000'; i++)
{
if(dec[i] == '.')
{
float dec_place = 10;
int power_of_ten = 1;
for(i++; dec[i] != '\000'; i++, power_of_ten++, dec_place *= 10)
{
if(dec[i] >= '0' && dec[i] <= '9')
{
if(power_of_ten > FLT_MAX_10_EXP) return 4;
else tmp_f -= (dec[i] - '0') / dec_place;
}
else return 5;
}
break;
}
if(dec[i] >= '0' && dec[i] <= '9')
{
tmp_f = tmp_f * 10 - (dec[i] - '0');
if(!isfinite(tmp_f)) return 6;
}
else return 5;
}
}
else
{
if(dec[i] == '+')
{
if(dec[i+1] == '\000') return 3;
else i++;
}
for(; dec[i] != '\000'; i++)
{
if(dec[i] == '.')
{
float dec_place = 10;
int power_of_ten = 1;
for(i++; dec[i] != '\000'; i++, power_of_ten++, dec_place *= 10)
{
if(dec[i] >= '0' && dec[i] <= '9')
{
if(power_of_ten > FLT_MAX_10_EXP) return 7;
else tmp_f += (dec[i] - '0') / dec_place;
}
else return 5;
}
break;
}
if(dec[i] >= '0' && dec[i] <= '9')
{
tmp_f = tmp_f * 10 + (dec[i] - '0');
if(!isfinite(tmp_f)) return 8;
}
else return 5;
}
}
*f = tmp_f;
return 0;
}
int main()
{
printf("FLT_MIN = %.38f\n", FLT_MIN);
printf("FLT_MAX = %f\n", FLT_MAX);
float f = 0;
int return_value = 0;
char str[256];
printf("INPUT = ");
scanf("%s", str);
return_value = dec_to_f(str, &f);
printf("return_value = %i\nstr = \"%s\"\nf = %.38f\nstrtof = %.38f\n", return_value, str, f, strtof(str, NULL));
}
答案 0 :(得分:3)
转换困难的原因有很多。其中两个是:
(事实上,考虑1.30000001192092895507812500000000 ... x 。如果 x 是该数字中任意个零后的非零数字,那么转换应该向上舍入。如果没有非零数字,那么转换应该向下舍入。这意味着无限制你需要检查多少位数才能确定正确的舍入幸运的是,除了扫描数字之外,你必须做的算术量有限,如文中所示。)
答案 1 :(得分:2)
在查看strtof / strtod的来源后,它使用double然后强制转换为浮动。
用double替换float会产生与strtof相同的结果:
#include <stdlib.h>
#include <stdio.h>
#include <float.h>
#include <math.h>
int dec_to_f(char *dec, float *f)
{
int i = 0;
double tmp_f = 0;
if(dec == NULL) return 1;
if(f == NULL) return 2;
if(dec[i] == '\000') return 3;
if(dec[i] == '-')
{
i++;
if(dec[i] == '\000') return 3;
for(; dec[i] != '\000'; i++)
{
if(dec[i] == '.')
{
double dec_place = 10;
int power_of_ten = 1;
for(i++; dec[i] != '\000'; i++, power_of_ten++, dec_place *= 10)
{
if(dec[i] >= '0' && dec[i] <= '9')
{
if(power_of_ten > FLT_MAX_10_EXP) return 4;
else tmp_f -= (dec[i] - '0') / dec_place;
}
else return 5;
}
break;
}
if(dec[i] >= '0' && dec[i] <= '9')
{
tmp_f = tmp_f * 10 - (dec[i] - '0');
if(!isfinite(tmp_f)) return 6;
}
else return 5;
}
}
else
{
if(dec[i] == '+')
{
if(dec[i+1] == '\000') return 3;
else i++;
}
for(; dec[i] != '\000'; i++)
{
if(dec[i] == '.')
{
double dec_place = 10;
int power_of_ten = 1;
for(i++; dec[i] != '\000'; i++, power_of_ten++, dec_place *= 10)
{
if(dec[i] >= '0' && dec[i] <= '9')
{
if(power_of_ten > FLT_MAX_10_EXP) return 7;
else tmp_f += (dec[i] - '0') / dec_place;
}
else return 5;
}
break;
}
if(dec[i] >= '0' && dec[i] <= '9')
{
tmp_f = tmp_f * 10 + (dec[i] - '0');
if(!isfinite(tmp_f)) return 8;
}
else return 5;
}
}
*f = (float)tmp_f;
return 0;
}
int main()
{
printf("FLT_MIN = %.38f\n", FLT_MIN);
printf("FLT_MAX = %f\n", FLT_MAX);
float f = 0;
int return_value = 0;
char str[256];
printf("INPUT = ");
scanf("%s", str);
return_value = dec_to_f(str, &f);
printf("return_value = %i\nstr = \"%s\"\nf = %.38f\nstrtof = %.38f\n", return_value, str, f, strtof(str, NULL));
}
答案 2 :(得分:2)
简短的回答是:您不能使用浮点数或双精度数转换为浮点数或双精度数。你需要更高精度的算术,“大浮点数”或“大整数”。
答案较长的是David Gay's paper(在其他答案中引用)和David Gay's implementation of that paper。
更长的答案在我的网站上,其中I explain David Gay's code在一系列详细的文章中。
如果您不关心如何正确转换,并且只是想了解您的错误原因,请阅读我的文章Quick and Dirty Decimal to Floating-Point Conversion。它显示了一个像你这样的小程序,看起来应该可行,但却没有。然后,请参阅我的文章Decimal to Floating-Point Needs Arbitrary Precision以了解原因。
答案 3 :(得分:1)
@Eric Postpischil和@Nahuel Fouilleul提供了很好的信息。我会添加一些不太适合评论的想法。
1)FP的文本需要在另一个方向上进行评估。而不是最重要的数字。形成从最小到最重要的结果。忽略前导零。这将最好地保持您最不重要的文本数字的微妙影响。当你从右到左时,最后将power_of_10保持为倍数。
power_of_ten *= 10.0;
...
loop()
// tmp_f = tmp_f * 10 + (dec[i] - '0');
tmp_f = tmp_f/10 + (dec[i] - '0');
power_of_ten *= 10.0;
...
tmp_f *= power_of_10;
2)注意到DP .
,(从右到左),将power_of_10重置为1.0。
3)将您的-
和+
代码折叠成一个。
4)使用“%.9e”来比较结果。
5)使用next_afterf(x,0.99*x)
和next_afterf(x,1.01*x)
来包含可接受的结果。
6)典型的float
具有大约1个幂(2,23)精度(~7个十进制数字)。当OP正在接近时,整体转换是正常的,只需要反向解析。