Real
Real

Reputation: 91

Using both commas and dots to represent decimal numbers

I'm having a very specific problem and haven't found a solution elsewhere. I'm working on a small project and i want to make it more robust by allowing users to input prices with both a comma or dot. So i made a little function that allows me to do that:

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

int main () {
    setlocale(LC_ALL, "Portuguese");
    float val;
    char str[20];

    scanf("%s", str);
    for (int i = 0; str[i] != '\0'; ++i)
        if (str[i] == ',') 
            str[i] = '.';
        val = atof(str);
    printf("String value = %s, Float value = %f\n", str, val);
    return(0);
}

This would work as intended, were I not Portuguese. Since we mostly use a comma in decimal numbers, using the atof function doesn't work because it converts to floats with a dot, and then when I try to printf the float it will show 0.0 but if you delete the line setlocale(LC_ALL, "Portuguese"); it will work just fine. Any ideas?

Upvotes: 2

Views: 1086

Answers (2)

Lundin
Lundin

Reputation: 213276

There's two problems:

  • If you encounter the decimal point which is not the correct one for the current locale, then and only then, change it.
  • You are using atof which is an unsafe function that should never be used - it has no error handling. Use strtof instead.

You can use the standard function localeconv to get all kind of useful info about the current locale.

Example:

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

int main (void) 
{
  setlocale(LC_ALL, "Portuguese");
  char ok_decimal_point  = localeconv()->decimal_point[0];
  char bad_decimal_point = (ok_decimal_point=='.') ? ',' : '.';

  float val;
  char str[20] = "123.456";

  for (int i = 0; str[i] != '\0'; ++i)
  {
    if (str[i] == bad_decimal_point)
    {
      str[i] = ok_decimal_point;
    }
  }

  val = strtof(str, NULL);
  printf("String value = %s, Float value = %f\n", str, val);
  return(0);
}

(Although, coming from another country that uses ,, I prefer to educate users to use the . form instead, since this is more of an international standard. Having two different standards for decimal points around the world isn't helping mankind. Those with the least used version should adapt.)

Upvotes: 3

Jabberwocky
Jabberwocky

Reputation: 50778

Your code works as expected:

The for loop transforms all , into . and therefore atof fails converting a number with . as decimal point because you have called setlocale(LC_ALL, "Portuguese"); beforehand.

You need this:

if (str[i] == '.') str[i] = ',';

instead of this:

if (str[i] == ',') str[i] = '.';

This sample makes it clear:

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

int main() {

  float val;

  // converting with default locale
  char str[20] = "1.234";
  val = atof(str);
  printf("Default locale: String value = %s, Float value = %f\n", str, val);

  // converting with Portugese locale    
  setlocale(LC_ALL, "Portuguese");

  char strport[20] = "1,234";
  val = atof(strport);
  printf("Portugese locale: String value = %s, Float value = %f\n", strport, val);
  return(0);
}

Upvotes: 2

Related Questions