为什么使用非十进制数据类型为差钱? [英] Why is using a NON-decimal data type bad for money?

查看:164
本文介绍了为什么使用非十进制数据类型为差钱?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

TL;博士:有什么毛病我的姜黄素(货币)的结构。

tl;dr: What's wrong with my Cur (currency) structure?

TL;博士2: 阅读问题的其余部分请,给予与浮动的: - )

tl;dr 2: Read the rest of the question please, before giving an example with float or double. :-)

我知道,这个问题想出了无数次的前四周互联网,但的我还没有看到令人信服的答案的,所以我想我会再问。

I'm aware that this question has come up numerous times before all around the internet, but I have not yet seen a convincing answer, so I thought I'd ask again.

我不明白,为什么使用非十进制数据类型是坏弄钱。 (这指的是存储而不是十进制数字的二进制数字数据类型。)

I fail to understand why using a non-decimal data type is bad for handling money. (That refers to data types that store binary digits instead of decimal digits.)

诚然,这不是明智的做法来比较两个双击 s的 A == b 。但是你可以很容易地说 A - B< = EPSILON 或类似的东西。

True, it's not wise to compare two doubles with a == b. But you can easily say a - b <= EPSILON or something like that.

什么是错了这种方法?

举例来说,我只是做了一个结构在C#中,我相信正确处理金钱,而无需使用任何基于十进制数据格式:

For instance, I just made a struct in C# that I believe handles money correctly, without using any decimal-based data formats:

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"); }
}



(对不起,更改名称货币姜黄素,为穷人变量名称,省略公共,并为不良布局;我试图把它所有在屏幕上,让你可以无需滚动阅读):)

(Sorry for changing the name Currency to Cur, for the poor variable names, for omitting the public, and for the bad layout; I tried to fit it all onto the screen so that you could read it without scrolling.) :)

您可以用它喜欢的:

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



当然,C#有小数数据类型,但是这是题外话这里 - 问题是关于为什么上面是危险的,不是我们不应该使用小数

Of course, C# has the decimal data type, but that's beside the point here -- the question is about why the above is dangerous, not why we shouldn't use decimal.

所以,会有人心中为我提供的真实世界的反的一个危险的声明,将在C#中失败这一点?我想不出任何。

So would someone mind providing me with a real-world counterexample of a dangerous statement that would fail for this in C#? I can't think of any.

谢谢!

注:我的讨论小数是否是一个不错的选择。我问,为什么一个二进制为基础的系统,被认为是不恰当的。

Note: I am not debating whether decimal is a good choice. I'm asking why a binary-based system is said to be inappropriate.

推荐答案

花车不是为积累和递减稳定资金。这是你的实际的例子:

Floats aren't stable for accumulating and decrementing funds. Here's your actual example:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BadFloat
{
    class Program
    {
        static void Main(string[] args)
        {
            Currency yourMoneyAccumulator = 0.0d;
            int count = 200000;
            double increment = 20000.01d; //1 cent
            for (int i = 0; i < count; i++)
                yourMoneyAccumulator += increment;
            Console.WriteLine(yourMoneyAccumulator + " accumulated vs. " + increment * count + " expected");
        }
    }

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

}

在我的机器上这给$ 4,000,002,000.0203积累与4000002000预计在C#。这是一个糟糕的协议,如果这丢失了许多交易在一家银行 - 它并不一定要路数,只是众多。这是否帮助?

On my box this gives $4,000,002,000.0203 accumulated vs. 4000002000 expected in C#. It's a bad deal if this gets lost over many transactions in a bank - it doesn't have to be large ones, just many. Does that help?

这篇关于为什么使用非十进制数据类型为差钱?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆