-1

There was once this little function

string format_dollars_for_screen(float d)
{
  char ch[50];
  sprintf(ch,"%1.2f",d);
  return ch;
}

who liked to return -0.00.

I modified it to

string format_dollars_for_screen(float d)
{
  char ch[50];
  float value;
  sprintf(ch,"%1.2f",d);
  value = stof(ch);
  if(value==0.0f) sprintf(ch,"0.00");
  return ch;
}

And it started returning 0.00 as desired. My question is, why doesn't this other solution work?

string format_dollars_for_screen(float d)
{
  char ch[50];
  float value;
  sprintf(ch,"%1.2f",d);
  value = stof(ch);
  if(value==0.0f) sprintf(ch,"%1.2f", value);
  return ch;
}

And/or is there a more efficient way to do this? This is just off the top of my head, so critiques are welcome. =)

pdm
  • 1,027
  • 1
  • 9
  • 24
  • Considering you're using `sprintf` and fixed-length character buffers (yikes!) this is just C code apart from the fact you return `string`. – tadman Sep 29 '16 at 21:27
  • @tadman: They return a string, and in C++ you still don't have a good standard formatting solution. – Yakov Galka Sep 29 '16 at 21:28
  • @ybungalobill Depends on your definition of "good". It's not like [this is an unsolved problem](http://stackoverflow.com/questions/29200635/convert-float-to-string-with-set-precision-number-of-decimal-digits). – tadman Sep 29 '16 at 21:29
  • What do you mean by "worked"? Do you want `"-0.00"`, or do you *not* want `"-0.00"`? – Keith Thompson Sep 29 '16 at 21:31
  • @tadman: "good" is "as good as C's". I/o streams are more verbose to use and are on some implementations significantly slower. They are compile-time type-safe, but so is C's with a compiler checking format strings. With `vsnprintf` the fixed-buffer problem evaporates too... Yes it is solved, but the solution is to use a wrapper of `vsnprintf` returning an `std::string` or a [dedicated library](http://stannum.sourceforge.net/doc/io/printf/index.xhtml). – Yakov Galka Sep 29 '16 at 21:37
  • http://en.cppreference.com/w/cpp/io/manip/put_money – user4581301 Sep 29 '16 at 21:46
  • @ybungalobill The fixed buffer problem never evaporates, it just becomes less of an issue if you get everything perfectly right. C++ might not be as terse, but some would argue that's a good thing since it's less cryptic. I'm just mentioning this because C++ does have a native way of handling it that doesn't involve junk like `char char[50]` – tadman Sep 29 '16 at 22:48
  • Updated to define "worked". Also, thank you for the discussion and links. =) – pdm Sep 30 '16 at 13:35

1 Answers1

3

Floating point numbers have both, a plus-zero and a minus-zero. They compare equal with the == operator, but produce different results in other arithmetic expressions: 1/+0 == +inf but 1/-0 == -inf.

As for your case, you shall not use floating point numbers for monetary quantities. Instead use integers for counting cents (or other decimal fractions of cents), and format them accordingly:

string format_dollars_for_screen(int cents)
{
  bool neg = cents < 0;
  if(neg) cents = -cents;
  char ch[50];
  sprintf(ch, "%s%d.%.2d", "-"+!neg, cents/100, cents%100);
  return ch;
}
Yakov Galka
  • 70,775
  • 16
  • 139
  • 220
  • 2
    It's really worth emphasizing **never use floating point for representing money**. It will lead to nothing but confusion, pain, suffering, and a nightmare for your accountants. – tadman Sep 29 '16 at 21:28
  • Besides, there's no -0 for ints. – Bob__ Sep 29 '16 at 21:30
  • For what it's worth, the money is stored as `int`s. The value being passed in as a float is literally just being chopped down to pretty cents. Hence `format_dollars_for_screen()`. So, no worries. ;) Also: could I trouble you to explain how `"+"+!neg` works? – pdm Sep 30 '16 at 13:31
  • @musasabi: for `neg=1` we add nothing to string and print `"-"`. For `neg=0` we add 1 to the string, it shifts the pointer by one char, which prints an empty string `""`. – Yakov Galka Sep 30 '16 at 13:43