Почему использование НЕ-десятичного типа данных плохо для денег?

tl; dr: Что не так с моей Cur (денежной) структурой?

tl; dr 2: Прочтите, пожалуйста, остальную часть вопроса, прежде чем приводить пример с float или double . : -)


Я знаю, что этот вопрос уже много раз поднимался по всему Интернету, но я еще не видел убедительного ответа , поэтому решил спросить еще раз.

Я не понимаю, почему использование недесятичного типа данных плохо для работы с деньгами. (Это относится к типам данных, которые хранят двоичные цифры вместо десятичных.)

Верно, неразумно сравнивать два double s с a == b . Но вы можете легко сказать a - b <= EPSILON или что-то в этом роде.

Что не так в этом подходе?

Например, я только что создал struct в C #, который, как мне кажется, обрабатывает деньги правильно, без использования каких-либо десятичных форматов данных:

struct Cur
{
  private const double EPS = 0.00005;
  private double val;
  Cur(double val) { this.val = Math.Round(val, 4); }
  static Cur operator +(Cur a, Cur b) { return new Cur(a.val + b.val); }
  static Cur operator -(Cur a, Cur b) { return new Cur(a.val - b.val); }
  static Cur operator *(Cur a, double factor) { return new Cur(a.val * factor); }
  static Cur operator *(double factor, Cur a) { return new Cur(a.val * factor); }
  static Cur operator /(Cur a, double factor) { return new Cur(a.val / factor); }
  static explicit operator double(Cur c) { return Math.Round(c.val, 4); }
  static implicit operator Cur(double d) { return new Cur(d); }
  static bool operator <(Cur a, Cur b) { return (a.val - b.val) < -EPS; }
  static bool operator >(Cur a, Cur b) { return (a.val - b.val) > +EPS; }
  static bool operator <=(Cur a, Cur b) { return (a.val - b.val) <= +EPS; }
  static bool operator >=(Cur a, Cur b) { return (a.val - b.val) >= -EPS; }
  static bool operator !=(Cur a, Cur b) { return Math.Abs(a.val - b.val) < EPS; }
  static bool operator ==(Cur a, Cur b) { return Math.Abs(a.val - b.val) > EPS; }
  bool Equals(Cur other) { return this == other; }
  override int GetHashCode() { return ((double)this).GetHashCode(); }
  override bool Equals(object o) { return o is Cur && this.Equals((Cur)o); }
  override string ToString() { return this.val.ToString("C4"); }
}

(Извините за изменение имени Currency на Cur , из-за плохих имен переменных, за пропуск public , а также из-за плохого макета; я попытался уместить все это на экране, чтобы вы могли читать без прокрутки.):)

Вы можете использовать это как:

Currency a = 2.50;
Console.WriteLine(a * 2);

Конечно , C # имеет тип данных decimal , но это ' s здесь не в тему - вопрос в том, почему это опасно, а не в том, почему мы не должны использовать десятичное .

Так что не мог бы кто-нибудь предоставить мне контрпример из реального мира опасного оператора, который не смог бы выполнить это в C #? Я ничего не могу придумать.

Спасибо!


Примечание: я не спорю, является ли десятичным числом хорошим выбором. Я спрашиваю, почему двоичная система считается неуместной.

8
задан Mehrdad 24 April 2011 в 06:25
поделиться