-2

Our teacher gave us this exercise:

"Given a string like '-5,14' write a function that returns the float value of -5,14

I used double here just to test the precision, but it also didn't work with floats.

[also i'm from Europe, we use the comma instead of the dot. Oh also we aren't allowed to use the type string and bool, we have to "make" them like in C]

This is what i came up with, and it seems to work a little bit. Positive numbers are similar, but wrong, and given a negative number, the result is similar to 10 times the positive of the given number.

It should work like this:

  • I read the string into an array of characters;
  • I check if the first character is a minus. if so, subtract 1 from the number of integer figures because i will count them later starting from index 0;
  • I count the number of integer figures with a loop from the start of the array to the ',' character;
  • I count the number of decimal figures with a loop from after the ',' to the end of the string;
  • [Keep in mind for the next step that, following the ASCII table, the code for the character of a number is that number + 48]
  • I add to the result variable every integer figure multiplied by ten to the power of whatever place in the number it has.
  • I do the same for the deicmal values but with the negative exponent.
  • if the number was negative, i multiply the result with -1.

But for some reason it's not working properly. The lower the number is, the less accurate it is (given 4,5 the result is 9, but given 345,543 the result is 350,43)

#include <iostream>

#define EOS '\0'
#define DIM 100

#define TRUE 1
#define FALSE 0

void leggiN(char* c)
{
    std::cout << "Insert a number: ";
    std::cin >> c;
}

double stof(char* str)
{
    double Result = 0;
    double ascii_to_int = 48;

    int i = 0;
    int j = 0;

    int IntegerDigits = 0;
    int DecimalDigits = 0;
    int CommaIndex;

    int isNegative = FALSE;

    if (str[0] == '-')  
    {
        IntegerDigits = -1;
        isNegative = TRUE;
    }

    while (str[i] != ',')   
    {
        ++IntegerDigits;
        ++i;
    }

    CommaIndex = i;  
    ++i;                

    while (str[i] != EOS)   
    {
        ++DecimalDigits;
        ++i;
    }

    for (i = (CommaIndex - 1); i >= 0; --i)
    {
        Result += (str[i] - ascii_to_int) * (std::pow(10, j));
        ++j;
    }

    j = 0;

    for (i = (CommaIndex + 1); str[i] != EOS; ++i)
    {
        Result += (str[i] - ascii_to_int) * (std::pow(10, -j));
        ++j;
    }

    if (isNegative == 1)
        Result = Result * -1;

    return Result;
}

int main()
{
    char str[DIM];

    leggiN(str);

    std::cout << stof(str);
}
  • 3
    Please, please get rid of `TRUE` and `FALSE` and use `true`, `false`, and `bool`. – chris Mar 20 '21 at 02:13
  • 5
    Did you step through your code with a debugger to see at what point the values of your variables no longer match your expectations? – JaMiT Mar 20 '21 at 02:14
  • 3
    Actually, the solution is literally 2 lines of code. 1) Replace the comma with a dot. 2) Call [std::stof](https://en.cppreference.com/w/cpp/string/basic_string/stof). Is there a reason why you couldn't simply use `std::replace` and then `std::stof`? – PaulMcKenzie Mar 20 '21 at 02:18
  • 2
    [Example](http://coliru.stacked-crooked.com/a/0d9a9ebe57612d40). – PaulMcKenzie Mar 20 '21 at 02:30
  • 3
    *std::stof is a very helpful suggestion* -- Let the OP decide this. We have no idea what is allowed or not allowed. – PaulMcKenzie Mar 20 '21 at 03:50
  • We know that OP isn't allowed to use the type `string`, for one thing. – Nathan Pierson Mar 20 '21 at 03:57
  • 2
    Well, `std::strtod` doesn't use `std::string`. Again, two lines of code. – PaulMcKenzie Mar 20 '21 at 04:10
  • Yes, the exercise was to make our own stof(), and whe are not allowed to use bools, strings or any standard library function exept pow() and cin/cout – SorenTheOutcast Mar 20 '21 at 07:24
  • @SorenTheOutcast *or any standard library function exept pow()* -- You should be aware that `pow` is a floating point function, thus in some situations, can give [erroneous results](https://stackoverflow.com/questions/25678481/why-does-pown-2-return-24-when-n-5-with-my-compiler-and-os). – PaulMcKenzie Mar 21 '21 at 15:57

2 Answers2

1

use j = 1 to start your second for loop. You are trying to raise 10 to the power of -0

j = 1;

    for (i = (CommaIndex + 1); str[i] != EOS; ++i)
    {
        Result += (str[i] - ascii_to_int) * (std::pow(10, -j));
        ++j;
    }
ynota
  • 36
  • 4
0

If your code return 9.0 when you enter "4,5", your problem has nothing to do with imprecision.

There are other problems in your code, I've tried to un it and got a SEGFAULT...

#include <iostream>

#define EOS '\0'  // 0 being such a special value, there is no need to 
                  // define a named constant for it.
#define DIM 100  

#define TRUE 1    // the language defines boolean values, avoid defining
#define FALSE 0   // unnecessary named constants for something that already
                  // exists.  

void leggiN(char* c)
{
    std::cout << "Insert a number: ";
    std::cin >> c;     // Inserting from cin to a char* is a BIG no-no.
                       // some compilers won't even allow it, for good reasons
                       // i.e.: what is the length of the array pointed to?
}

double stof(char* str)  // you are indicating that you may modify str? 
{
    double Result = 0;
    double ascii_to_int = 48;   // this is a terrible name.

    int i = 0;
    int j = 0;

    int IntegerDigits = 0;
    int DecimalDigits = 0;
    int CommaIndex;

    int isNegative = FALSE;

    if (str[0] == '-')  // is str a valid pointer? what happens if NULL ??
    {
        IntegerDigits = -1;
        isNegative = TRUE;

        // you fail to skip the sing character, should have ++i here.
    }

    while (str[i] != ',')   // what happens if there is no ',' in the string?
    {                       // you should check for str[i] == 0.  
        ++IntegerDigits;
        ++i;
    }

    CommaIndex = i;  
    ++i;                

    while (str[i] != EOS)   
    {
        ++DecimalDigits;   // why do you count decimal digits?
        ++i;               // you do not use this result anyway... 
    }


    for (i = (CommaIndex - 1); i >= 0; --i)
    {
        // what happens if you have non-digit characters?  they participate
       // in the conversion??
        // you call std::pow(), but do not include <cmath> at the top of the file.
        // isn't str[i] - '0' clearer ? 
        Result += (str[i] - ascii_to_int) * (std::pow(10, j));
        ++j;
    }

    j = 0;

    for (i = (CommaIndex + 1); str[i] != EOS; ++i)
    {
        Result += (str[i] - ascii_to_int) * (std::pow(10, -j));
        ++j;
    }

    if (isNegative == 1)  // you had defined constants fot this, but don't use them.
        Result = Result * -1;  

    return Result;
}

int main()
{
    char str[DIM];

    leggiN(str);

    std::cout << stof(str);
}

Here is one way to achieve what you want.

#include <iostream>
#include <string>

const char DECIMAL_POINT = ',';  // we'll use a named constant here....
                                 // usually, we'd have to check the locale
                                 // for regional specific information.

// works like atod(), conversion stops at end of string of first illegal character.
double stof(const char* str) {

  // check input, must be not null, not empty 
  if (!str || str[0] == 0)
    return 0;

  int i = 0;
  bool isNegative = false;

  // take care of leading sign 
  if (str[0] == '-' || str[0] == '+') {
    isNegative = (str[0] == '-');
    ++i;
  }

  // convert integer part.  

  double result = 0;
  while ('0' <= str[i] && str[i] <= '9') {
    result = (result * 10) + (str[i] - '0');
    ++i;
  }

  // only do decimals if they are there.
  if (str[i] != DECIMAL_POINT) 
    return (isNegative) ? -result : result;

  ++i;  // skip decimal point

  double decimals = 0;
  double multiplier = .1;

  while ('0' <= str[i] && str[i] <= '9') {
    decimals += (str[i] - '0') * multiplier;
    ++i;
    multiplier *= .1;
  }

  result += decimals;

  return (isNegative) ? -result : result;
}

int main() {

  // always use std::string to read strings from cin.
  std::string str;
  std::cout << "Insert a number: ";
  std::cin >> str;

  std::cout << "in: " << str << " out: " << stof(str.c_str()) << '\n';

  return 0;
}
Michaël Roy
  • 6,338
  • 1
  • 15
  • 19