c - 输入加倍直到 EOF
问题描述
我有一个问题,我需要输入双精度,在 EOF 到来之前输出平方根。我做了 2 个功能:1)使字符串成为双精度。2) 不使用 <math.h> 输出平方根。但是在EOF到来之前,我不知道如何输入双打。你能帮我解决这个问题吗?
#include <stdio.h>
double todouble(char *a)
{
double ans=0;
while(*a!='.' && *a!='\0')
{
for(int i=48; i<58; i++)
{
if(*a==i)
{
ans=ans*10+(i-48);
break;
}
}
a++;
}
if(*a=='\0')
{
return ans;
}
else if(*(a+1)=='\0')
{
return ans;
}
a++;
double temp=1;
int count=1;
do
{
for(int i=48; i<58; i++)
{
if(*a==i)
{
for(int j=0; j<count; j++)
{
temp=temp/10;
}
temp*=(i-48);
ans+=temp;
temp=1;
count++;
break;
}
}
a++;
}
while(*a!='\0');
return ans;
}
double myabs(double a, double b)
{
if(a>b)
{
return (a-b);
}
else
{
return (b-a);
}
}
double squareroot(double pr, double x)
{
double a=1;
double b=(x+1)/2;
double dif=myabs(a,b);
while(dif>=pr)
{
a=b;
b=(a+(x/a))/2;
dif=myabs(a,b);
}
return b;
}
int main()
{
double eps, x;
printf("Please type in the value of Epsilon: ");
scanf("%lf %lf", &eps, &x);
double ans=squareroot(eps, x);
printf("%.10g\n", ans);
return 0;
}
这是我到目前为止所做的一切。
解决方案
如果我理解您的问题,我怀疑您在尝试将代码拆分为多个函数时混淆了这个问题。首先让你的代码工作,然后担心拆分成函数(这对于这个问题是不必要的,因为只需要大约 8 行代码 - 减去验证)
如果您的文件中每行有一个双精度,那么处理输入和转换的一种可靠方法是将每一行读入一个字符数组,fgets()
然后使用 将该行转换为双精度sscanf()
。虽然您可以简单地使用fscanf()
由于任何匹配失败导致从文件中提取字符在错误点停止而变得脆弱。使用fgets()
/sscanf()
如果转换失败 - 您已经使用了整行输入,所以只需跳到下一行。
一个简短的例子,它将文件作为程序的第一个参数读取双精度数(或者stdin
如果没有提供参数,则从它读取)将是:
#include <stdio.h>
#include <math.h>
#define MAXC 256 /* if you need a constant, #define one (or more) */
int main (int argc, char **argv) {
char buf[MAXC]; /* buffer to hold each line */
double d; /* double */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
while (fgets (buf, MAXC, fp)) { /* read each line into buf */
if (sscanf (buf, "%lf", &d) != 1) /* convert string to double */
continue; /* get next if bad conversion */
printf ("sqrt(%f) = %.2f\n", d, sqrt(d)); /* output result */
}
if (fp != stdin) /* close file if not stdin */
fclose (fp);
return 0;
}
示例输入文件
$ cat ../dat/doubles_10.txt
237788.0792625
291066.0970219
845814.281938
152208.0507361
585537.195179
193475.0644918
810623.270208
173531.0578436
484983.161661
151863.0506209
示例使用/输出
$ ./bin/sqrtdoublesfromfile ../dat/doubles_10.txt
sqrt(237788.079262) = 487.64
sqrt(291066.097022) = 539.51
sqrt(845814.281938) = 919.68
sqrt(152208.050736) = 390.14
sqrt(585537.195179) = 765.20
sqrt(193475.064492) = 439.86
sqrt(810623.270208) = 900.35
sqrt(173531.057844) = 416.57
sqrt(484983.161661) = 696.41
sqrt(151863.050621) = 389.70
仔细检查一下,告诉我我是否猜对了您的问题,如果您还有其他问题,请告诉我。(如果您需要更多帮助,也可以编辑您的问题并发布您的问题)
编辑答案中代码的每次更新
出于所有实际目的,您的代码有效。您使用的逻辑错误,<
而不是<=
导致for (int i = '0'; i <= '9'; i++)
您无法评估'9'
数字中的数字。
您的功能略有变化。您的todouble()
功能已更改为:
int todouble (char *a, double *ans)
每当您在函数中执行可能成功或失败的操作时,您必须能够将该信息传达回调用进程。返回double
值不会。零值与任何其他双精度值一样有效。因此,改为将返回类型更改为,int
以便您可以返回1
成功和0
失败并传递一个指针,ans
以便该内存地址处的值可以在函数内更新并可供调用者使用。
(更新作为参数传递的指针值是一种向调用者返回更多信息的方法,而不仅仅是返回值)
在todouble()
(以及您编写的任何代码中)不要使用Magic-Numbers或Hardcoded-Filenames。在这里,您已经48
完成58
了所有代码,您应该在其中包含 ASCII 数字的字符文字,例如
for (int i = '0'; i <= '9'; i++)
不是
for (int i = 48; i <= 58; i++)
幻数使您的代码不可读,除非那些有方便确认的 ASCII 图表的人。
进行这些更改,您的todouble()
函数可以写成:
/* todouble takes array holding number and pointer to double
* as parameters. On function return 1-indicates success, 0-failure.
* the value for the pointer ans is updated to hod the new answer so
* it is made available to the caller.
*/
int todouble (char *a, double *ans)
{
*ans = 0;
while (*a != '.' && *a != '\0') { /* handle real-part */
for (int i = '0'; i <= '9'; i++) { /* must be <= 9, not < */
if (*a == i) {
*ans = *ans * 10 + (i - '0');
break;
}
}
a++;
}
if (*a == '\0' || a[1] == '\0') { /* test end-of-string */
return 1;
}
a++;
double temp=1;
int count=1;
do { /* handle fractional-part of number */
for (int i = '0'; i <= '9'; i++) { /* same <, <= problem */
if (*a == i) {
for (int j = 0; j < count; j++) {
temp = temp / 10;
}
temp *= (i - '0');
*ans += temp;
temp = 1;
count++;
break;
}
}
a++;
}
while (*a != '\0');
return 1;
}
您的myabs()
功能和squareroot()
功能在逻辑上都很好,但由于代码都是“挤在一起”(技术术语),老眼睛几乎不可能阅读。间距使您的代码对老年人更具可读性(想想您的教授在这里......)示例:
double myabs (double a, double b)
{
if (a > b) {
return (a-b);
}
else {
return (b-a);
}
}
double squareroot (double pr, double x)
{
double a = 1;
double b = (x + 1) / 2;
double dif = myabs (a, b);
while (dif >= pr) {
a = b;
b = (a + (x / a)) / 2;
dif = myabs (a, b);
}
return b;
}
让你的代码从文件中读取esp
和x
值
您的问题想esp
从文件中读取双精度(可能是值)。与我最初所做的类似,只需从作为程序的第一个参数提供的文件名中读取,或者stdin
如果没有提供参数则读取。(大多数 Linux 实用程序的工作方式)。你的main()
函数可以写成:
int main (int argc, char **argv) {
double eps, x;
char epsstr[MAXC], xstr[MAXC];
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
while (fscanf (fp, "%s %s", epsstr, xstr) == 2) { /* read eps & x */
if (todouble (epsstr, &eps) != 1 || todouble (xstr, &x) != 1) {
fputs ("error: conversion of eps or x to double failed.\n",
stderr);
continue;
}
printf ("sqrt(%f) = %.2f\n", x, squareroot (eps, x));
}
if (fp != stdin) /* close file if not stdin */
fclose (fp);
}
示例输入文件 w/esp
向文件中添加一个esp
您可以执行的操作:
$ cat ../dat/eps_doubles_10.txt
0.0001 237788.0792625
0.0001 291066.0970219
0.0001 845814.281938
0.0001 152208.0507361
0.0001 585537.195179
0.0001 193475.0644918
0.0001 810623.270208
0.0001 173531.0578436
0.0001 484983.161661
0.0001 151863.0506209
示例使用/输出
$ ./bin/sqmess ../dat/eps_doubles_100.txt
sqrt(237788.079262) = 487.64
sqrt(291066.097022) = 539.51
sqrt(845814.281938) = 919.68
sqrt(152208.050736) = 390.14
sqrt(585537.195179) = 765.20
sqrt(193475.064492) = 439.86
sqrt(810623.270208) = 900.35
sqrt(173531.057844) = 416.57
sqrt(484983.161661) = 696.41
sqrt(151863.050621) = 389.70
仔细看一下,如果您还有其他问题,请告诉我。