0

im having trouble, returning my defined structure, my function scan_sci is suppose to take from the input source a string representing a positive number in scientific notation, and breaks it into components for storage in a scinot_t structure. An example input would be 0.25000e4

  #include <stdio.h>
    #include <stdlib.h>
    #include <math.h>

    typedef struct{
        double mantissa;
        int exp;
    }sci_not_t;

    int scan_sci (sci_not_t *c);

    int main(void){

        sci_not_t inp1, inp2;

        printf("Enter the mantissa in the range [0.1, 1.0) then its exponent: ");
        scan_sci(&inp1);
        printf("Enter second number with same specifications as above: ");
        scan_sci(&inp2);






      system("PAUSE");  
      return 0;
    }

    int scan_sci (sci_not_t *c){

        int status;
        double a;
        int b;

        status = scanf("%lf %d", &c->mantissa, &c->exp);

        a = c->mantissa;
        b = c->exp;
        *c = pow(a,b);

        if (status == 2 && (c->mantissa >= 0.1 && c->mantissa < 1.0) ){
            status = 1;
        }else{
            printf("You did not enter the mantissa in the correct range \n");
            status = 0;
        }


        return (status);
    }

    sci_not_t sum(sci_not_t c1, sci_not_t c2){

        return c1 + c2;
    }

    sci_not_t product(sci_not_t c1, sci_not_t c2){

        return c1 * c2;
    }
  • 1) you need variable with type `scinot_t` 2) why do you pass `value` / `inp1` ? – Karoly Horvath Aug 09 '13 at 21:30
  • because my function scan_sci is suppose to take from the input source a string – user2669266 Aug 09 '13 at 21:34
  • If you use `%lf` in `scanf`, then `scanf` will read the entire floating point value into your double - including the exponent. It will read 12.3e4 and convert the entire string to a double. You probably want to use scanf with a %s and then parse the values. – agbinfo Aug 09 '13 at 22:09

4 Answers4

1

There is a lot wrong here. To start, your parameter is wrong for scan_sci. You are not passing a pointer to the structure you declared, you are passing an array of characters. Your declaration should be as follows:

scinot_t scan_sci( scinot_t *collection );

To conform with passing a pointer to the structure, you will want to change the declaration to the following. Note, it is extremely bad practice and error prone to return a pointer to a variable declared on the stack, hence why we are mutating the structure within scan_sci.

void scan_sci( scinot_t *collection );

Now you will need to create the structure before you make a call to the function and pass its memory address using the & operator.

Jacob Pollack
  • 3,703
  • 1
  • 17
  • 39
  • It is incorrect to say returning a variable on the stack shouldn't be done! http://stackoverflow.com/questions/9653072/return-a-struct-from-a-function-in-c Nevermind where it is stored, as soon as it is returned you'll be assigning it to a variable on your current stack. There could be debate on either side of returning via stack or putting values into memory pointed to, but I'd say in this case the return should be a `int` 0 or non-0 representing success or failure of `scanf`. – Macattack Aug 09 '13 at 21:40
  • Edited my answer to better communicate what I meant -- I see it was originally very confusing. – Jacob Pollack Aug 09 '13 at 21:49
  • the problem is how am i suppose to take from the input source a string and return scinot_t – user2669266 Aug 09 '13 at 21:52
  • @user2669266 you'll want to use the `@` sign to talk at people. I'm working on an answer, but using scanf really complicates things, 123.01e123 is all part of a single valid float, so the scanf never fills in the in `int` exponent part. – Macattack Aug 09 '13 at 22:11
  • 1
    +1: Ouch — the code in the question keeps morphing, which makes it hard for you to critique it coherently. The version I see is better than the version you saw as you were answering. One way I deal with such questions is to copy the code I'm dissecting into my answer, so that people can see exactly what I was critiquing. – Jonathan Leffler Aug 09 '13 at 22:30
  • @user2669266, please post your original code and the change you made under it in a new code box. My answer was for your previous revision of your code. Keeping a history of all changes you made in the original post will help us better critique it as Jonathan said. – Jacob Pollack Aug 09 '13 at 22:35
0

You had a few problems, this should fix them. First off, scan_sci now takes a pointer to return the value, and returns 0 on success or non-0 on failure (doesn't catch all failure modes). Other than that, scanf is greedy and e is a legitimate part of a float string representation, so, your original version was only parsing 1 argument and the double was eating the whole input string e.g. "123e123" all went into the double. To address that, parsing has been split into two separate parses based on splitting on the e.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
    double mantissa;
    int exp;
}scinot_t;

int scan_sci(scinot_t * tl){
    char buf[80]; 
    fgets(buf, 80, stdin);
    char * e = strchr(buf, 'e');
    if (!e) { return -1; }
    // Split buf up to act like 2 strings
    // e.g. 123.01e321 becomes:
    // 123.01 and 321 with e pointing to 321
    *e = '\0';
    e++;
    // Not checking any errors
    tl->mantissa = strtod(buf, NULL);
    tl->exp = strtol(e, NULL, 10);
    return 0;
}

int main(void){
    printf("Enter a value\n");
    // Init to 0 
    scinot_t sav = {0};
    int ret = scan_sci(&sav);
    if (ret) {
        printf("error, 'e' expected\n");
    }
    else {
        printf("%lfe%d", sav.mantissa, sav.exp);
    }

    return 0;
}
Macattack
  • 1,917
  • 10
  • 15
  • Reply if you don't understand, there is some less intuitive stuff going on with regards to splitting on the `e` and getting the pointer to after the `e`. – Macattack Aug 09 '13 at 22:33
0

Here's some code that reads from the console and puts the value in scinot_t There's no validation whatsoever.

It reads the entire string using scanf into two parts. The first part %[^e] reads any char that's not an 'e'. Then it reads the 'e' and finally the exponent.

The mantissa is read as a string first and reparsed using sscanf.

#include <stdlib.h>

typedef struct {
    double mantissa;
    int exp;
}scinot_t;

void scan_sci(scinot_t* s);

int main(void){
  scinot_t d;

  printf("Enter a value: ");
  scan_sci(&d);

  printf("Mantissa is %lf Exponent is %d\n", d.mantissa, d.exp);
  return 0;
}

void scan_sci (scinot_t *sn){
  char inp1[20];
  scanf("%[^e]e%d", inp1, &sn->exp);
  sscanf(inp1, "%lf", &sn->mantissa);
  return;
}

To test for mantissa range update main like this:

int main(void){
  scinot_t d;

  printf("Enter a value: ");
  for (;;) {
    scan_sci(&d);
    if (d.mantissa>=0.1 && d.mantissa<=1.0)
      break;
    printf("You did not enter the mantissa in the correct range\nEnter a value: ");
  }

  printf("Mantissa is %lf Exponent is %d\n", d.mantissa, d.exp);
  return 0;
}
agbinfo
  • 793
  • 5
  • 17
  • You can improve the parsing too. For example, if you use `scanf("%[^e]%*[eE]%d", inp1, &sn-exp);`, it will accept both `123.4e9` or `123.4E9`. -- untested – agbinfo Aug 09 '13 at 22:48
  • i see thats interesting, well what im trying to do now is see if the user entereed the mantissa in the correct range `for(;;){ if(sn->mantissa >= 0.1 && sn->mantissa < 1.0){ return; break; }else printf("You did not enter the mantissa in the correct range \n"); break; }` – user2669266 Aug 09 '13 at 23:03
  • except i realize this doesnt really work since it is still scanning in the values even if the mantissa range is incorrect – user2669266 Aug 09 '13 at 23:07
  • ok ive been messing around for a while with the code but cant figure out how to return scinot_t from a function sum, which adds 2 inputs, and then to create a print_sci function which will print the output , i've edited my code above, since i cannot answer my own question yet – user2669266 Aug 10 '13 at 02:04
  • you can probably tell i cheated the results on the print_sci function but it won't always work for example `0.99e5+0.99e5` would print `1.98e5` when it should print `0.198e6` – user2669266 Aug 10 '13 at 02:09
  • @user2669266 I'm not certain about the rules here but I believe you should probably create a new question otherwise the answer is no longer helpful to others. – agbinfo Aug 12 '13 at 15:53
0

One problem in the code I see is:

printf("%lfe%d", inp1);

This does not work reliably (though there's a chance it will work); you need to pass the two elements of inp1 separately:

printf("%lfe%d\n", inp1.mantissa, inp1.exponent);

Another problem is in the scan function:

void scan_sci(scinot_t *value)
{
    *value= scanf("%lfe%d", &value->mantissa, &value->exp);
}

This is simply wrong because it tries to assign an integer value (the return from scanf()) to a structure. I think you should be using something more like:

int scan_sci(scinot_t *value)
{
    if (scanf("%lfe%d", &value->mantissa, &value->exp) != 2)
        return EOF;
    return 0;
}

The return value now indicates whether it was successful (0) or failed (EOF). You still have the problem that %lf will cheerfully read 0.25000e4 using the %lf conversion specification, leaving the e and %d unmatched. There isn't a trivial way around that problem using scanf(), unless you demand that the value be started with 0..

int scan_sci(scinot_t *value)
{
    int fraction;
    if (scanf("0.%de%d", &fraction, &value->exp) != 2)
        return EOF;
    value->mantissa = ...calculation...
    return 0;
}

The trouble there, though, is that the first %d strips leading zeros off the number (so 0.0025e4 will be misinterpreted), so you still run into problems. You may do best with scanning for a string of digits:

int scan_sci(scinot_t *value)
{
    char digits[1024];
    if (scanf("0.%1023[0-9]e%d", digits, &value->exp) != 2)
        return EOF;
    value->mantissa = ...calculation using the digits string...
    return 0;
}

You can decide whether 1024 is too big and adjust the numbers accordingly.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278