首页 > 解决方案 > 输入加倍直到 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;
}

这是我到目前为止所做的一切。

标签: c

解决方案


如果我理解您的问题,我怀疑您在尝试将代码拆分为多个函数时混淆了这个问题。首先让你的代码工作,然后担心拆分成函数(这对于这个问题是不必要的,因为只需要大约 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-NumbersHardcoded-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;
}

让你的代码从文件中读取espx

您的问题想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

仔细看一下,如果您还有其他问题,请告诉我。


推荐阅读