Does money_get<> work correctly?

On coliru, I have tested the money_get<> facet for USD and EUR.

It works correctly (as far as my test data was concerned) for USD, for not for EUR.

The program to test it for USD is:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
int main()
{
    std::string str = "$1.11 $2.22 $3.33 4.44 5.55";

    std::istringstream s1(str);
    s1.imbue(std::locale("en_US.UTF-8"));

    std::cout << std::fixed << std::setprecision(2);
    std::cout << '"' << str << "\" parsed with the I/O manipulator: ";
    
    long double val;

    while(s1 >> std::get_money(val))
        std::cout << val/100 << ' ';
    std::cout << '\n';
 

    str = "USD  1,234.56";

    std::istringstream s2(str);
    s2.imbue(std::locale("en_US.UTF-8"));

    std::cout << '"' << str << "\" parsed with the facet directly: ";

    auto& f = std::use_facet<std::money_get<char>>(s2.getloc());
    std::ios_base::iostate err;
    std::istreambuf_iterator<char> beg(s2), end;

    f.get(beg, end, true, s2, err, val);

    std::cout << val/100 << '\n';
}


http://coliru.stacked-crooked.com/a/be545f641718040e

The O/P is correct:

"$1.11 $2.22 $3.33 4.44 5.55" parsed with the I/O manipulator: 1.11 2.22 3.33 4.44 5.55 
"USD  1,234.56" parsed with the facet directly: 1234.56



The program to test it for EUR is similar:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
int main()
{
    std::string str = "1,11 2,22 3,33 € 456789,12 €";

    std::istringstream s1(str);
    s1.imbue(std::locale("de_DE.utf8"));

    std::cout << std::fixed << std::setprecision(2);
    std::cout << '"' << str << "\" parsed with the I/O manipulator: ";
    
    long double val;

    while(s1 >> std::get_money(val))
        std::cout << val/100 << ' ';
    std::cout << '\n';
 

    str = "1.234,56 EUR";

    std::istringstream s2(str);
    s2.imbue(std::locale("de_DE.utf8"));

    std::cout << '"' << str << "\" parsed with the facet directly: ";

    auto& f = std::use_facet<std::money_get<char>>(s2.getloc());
    std::ios_base::iostate err;
    std::istreambuf_iterator<char> beg(s2), end;

    f.get(beg, end, true, s2, err, val);

    std::cout << val/100 << '\n';
}


http://coliru.stacked-crooked.com/a/a91720a9dbd4eb5e

The O/P is wrong:

"1,11 2,22 3,33 € 456789,12 €" parsed with the I/O manipulator: 1.11 2.22 3.33 
"1.234,56 EUR" parsed with the facet directly: 1234.56


Notice that the 4th Euro amount is not printed. This is because as soon as the money_get<>.get() function encounters the € symbol in the 3rd value, it gives an error. It processes only plain amounts without the € symbol.


The money_punct<> facet on coliru for the German locale gives the following definitions:


moneypunct in locale "de_DE.utf8":
 decimal_point: ,
 thousands_sep: .
 grouping:      3 3 
 curr_symbol:   €
 positive_sign: 
 negative_sign: -
 frac_digits:   2
 pos_format:    sign value space symbol 
 neg_format:    sign value space symbol 


Notice that I had entered the Euro amounts correctly as specified by neg_format.

How can such a problem be solved?

Thanks.
Last edited on
http://en.cppreference.com/w/cpp/locale/money_get/get#Notes talks about this issue:

Because currency symbol is optional if showbase is off but the entire multicharacter negative_sign() is required, given the formatting pattern {sign, value, space, symbol} with showbase off and negative_sign of "-", the string "-1.23 €" parses as -123 and leaves "€" unconsumed on the input stream


So, add s2 >> std::showbase; before parsing the ones with the "€"

"3,33 € 456789,12 €" parsed with the I/O manipulator: 3.33 456789.12 
Last edited on
Thanks for this. I shall try it and let you know.

I forgot to provide attribution for the source code posted by me earlier. It is originally from cppreference.com: http://en.cppreference.com/w/cpp/locale/money_get
Well, the solution certainly works - for EURO.

But, there's still something strange, which I can't explain completely:

1) With USD, this works fine, with both the get_money() manipulator as well as using the facet directly, though the stream's showbase flag isn't set. This can be explained - The symbol is consumed because the pattern is "sign symbol space value". Since "symbol" appears before "value", it is consumed.

2) With EUR, this works correctly with both the get_money() manipulator, as well as using the facet directly, if the stream's showbase flag is set, for the local € symbol.

However, when using the international EUR symbol, it works correctly, only if the showbase flag isn't set: http://coliru.stacked-crooked.com/a/14dff0d08506da67. The neg_format() pattern here is {sign value space symbol}.

If showbase is set for the EUR symbol, it won't work. An error is returned and the value is returned as 0.

3) With INR, this works correctly with the get_money() manipulator, if the stream's showbase flag is set, for the local ₹ symbol.

However, when the facet is used directly, with or without showbase being set, it doesn't work, and 0 is read instead: http://coliru.stacked-crooked.com/a/d79428e0ca2d318c

The neg_format() pattern here is {sign symbol space value}, and should have been consumed, since "symbol" occurs before "value".
OK, I've solved the problem and I have explanations for why this happens.

1) The program for reading USD amounts was already working. No problem there.

http://coliru.stacked-crooked.com/a/be545f641718040e


2) To read EUR amounts, you have to set the input stream's showbase flag, as indicated by Cubbi.

When the EUR amount involved uses the local symbol €, that's all that's necessary.

However, when the EUR amount involved uses the international symbol EUR, the symbol in the read data must be a 4-character string "EUR ". The last space is important, else it won't be consumed. This may be because the international representation is a 4-character C-style string, with the last character being the terminating zero. [Stroustrup, "The C++ Programming Language", 4th Ed, pg 1136.]

http://coliru.stacked-crooked.com/a/ef38fb1d1c98aa22

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/// ...

    str = "1.234,56 EUR ";

    std::istringstream s2(str);
    s2.imbue(std::locale("de_DE.utf8"));
    // s2 >> std::showbase;

    std::cout << '"' << str << "\" parsed with the facet directly: ";

    auto& f = std::use_facet<std::money_get<char>>(s2.getloc());
    std::ios_base::iostate err;
    std::istreambuf_iterator<char> beg(s2), end;

    f.get(beg, end, true, s2, err, val);

    std::cout << val/100 << '\n';



"1.234,56 EUR " parsed with the facet directly: 1234.56



3) Similarly, to read INR amounts, you have to set the input stream's showbase flag.

When the INR amount involved uses the local symbol ₹, that's all that's necessary.

However, when the INR amount involved uses the international symbol INR, the symbol in the read data must be a 4-character string "INR ". The last space is important, else it won't be consumed.

http://coliru.stacked-crooked.com/a/216cc78592141ae1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    /// ...

    str = "INR  100,234.56";

    std::istringstream s2(str);
    s2.imbue(std::locale("en_IN"));

//  s2 >> std::showbase;

    std::cout << '"' << str << "\" parsed with the facet directly: ";

    auto& f = std::use_facet<std::money_get<char>>(s2.getloc());
    std::ios_base::iostate err;
    std::istreambuf_iterator<char> beg(s2), end;

    f.get(beg, end, true, s2, err, val);

    std::cout << val/100 << '\n';


Notice there are 2 adjacent spaces in the data string: 1 after the INR symbol and 1 before the value. This is required by the pattern {sign symbol space value}.


"INR  100,234.56" parsed with the facet directly: 100234.56

I have confirmed that the international currency symbol is treated as a four-character string, rather than as a three-character one. (eg: "USD " rather than "USD", "EUR " rather than "EUR", etc., with a terminating space.)

This was already demonstrated for the money_get<> facet above.

It is also true for the money_put<> facet - see the program at:

http://coliru.stacked-crooked.com/a/e8b4a49dccc95aad

Output:

locale:de_DE.utf8 intl?true units:double
 [123,46 EUR ]

locale:de_DE.utf8 intl?true units:string
 [123,45 EUR ]


Note the space between the "EUR" string and the closing right bracket ].
As mentioned on cppreference page for std::money_get::get, std::showbase is necessary to make the currency symbol in the end position non-optional:

std::string str = "3,33 € 456789,12 €";
std::istringstream s1(str);
s1.imbue(std::locale("de_DE.utf8"));
std::cout << std::fixed << std::setprecision(2);
std::cout << '"' << str << "\" parsed with the I/O manipulator: ";
long double val;
s1 >> std::showbase; // <-- this
while(s1 >> std::get_money(val))
std::cout << val/100 << ' ';
std::cout << '\n';
output on coliru
http://www.cetpainfotech.com/technology/c-language-training
Registered users can post here. Sign in or register to post.