【问题标题】:How can I represent fractions like 1/3 in C#?如何在 C# 中表示像 1/3 这样的分数?
【发布时间】:2021-09-26 15:12:43
【问题描述】:

我正在编写一个食谱应用程序,它需要对成分执行加法/减法来制作主购物清单。我想不通的是如何以不会导致 .9999999 .... 而不是 1 的方式表示 1/3 之类的分数(不会转换为干净的小数)。我可以'甚至不能使用盎司作为基本单位,因为三分之一杯是 2.6666666 盎司。

简而言之,这个想法是,如果用户正在制作使用糖的食谱,他们可能会有一个要求一杯,另一个要求半杯,另一个要求三分之一杯。然后这会告诉他们下一次购物时需要购买 1 5/6 杯糖。

有没有人解决这个问题,还是我应该禁止所有带有丑陋分数的食谱(这远非理想)?

到目前为止,我想出的最接近的方法是将每种成分存储为三分之一盎司的倍数,但这会使后端逻辑有点复杂!

如果修复将允许我轻松地将值存储在 Postgres 数据库中,则奖励积分!

【问题讨论】:

  • 为什么不乘以60?您将拥有20 而不是1 / 3110 而不是1 5 /6
  • stackoverflow.com/questions/1662538/… 与这个问题无关。这不是那个的副本。
  • 您可以轻松地创建一个表示分数的结构,然后随意使用它。在 Postgresql 中,您可以使用 user defined data types
  • 以克(或磅,如果你喜欢的话)存储绝对量,并让用户选择单位。然后在将金额格式化为杯子时,将金额四舍五入到最接近的常规分数(5/6,而不是 11/13 等等。)
  • 我想你会发现 C# 本身可以很好地处理这个问题。 (1.0/3)*3==1.0。几乎可以肯定,问题在于您如何将这些数据存储在 Postgres 数据库中。我建议围绕该问题重新提出您的问题,因为这不是 C# 的具体问题。

标签: c# asp.net postgresql asp.net-core .net-core


【解决方案1】:

硬币有两个面。首先,存储精确值不是错误,而是一个特性。即使除以一些数字会产生丑陋的结果。存放它们时不要担心它们。但是,您需要解决的是硬币的另一面,即显示器。如果需要,您可以使用带有几位小数的Math.round。当我们谈论食谱时,需要一定程度的精确性,但是如果一个 NaCl 分子比绝对精确值更多地进入其中,您的汤不会被破坏,因此为此目的,四舍五入似乎是一种合理的方法。

【讨论】:

    【解决方案2】:

    我认为您最好将现有的分数实现结合起来,例如Fractions NuGet package 中的Fraction 类型与一个单元,这将是一个枚举。类似的东西

    struct Amount
    {
        public Amount(Fraction quantity, Unit unit)
        {
            Quantity = quantity;
            Unit = unit;
        }
    
        public Fraction Quantity { get; }
        public Unit Unit { get; }
    
        ...
    }
    
    public enum Unit { Ounce, Cup, Gram, ... };
    

    这使您可以在不同的单位(如杯子/盎司)之间进行转换,但我会在其中添加一些关于四舍五入的规则,以便您仍然可以使用合理的分数。因为你在谈论食谱,所以这似乎是一个合理的限制,因为如果你少了一点,它不会对食谱产生太大影响(希望如此)!调味!

    转换将隐藏在运算符后面;你会超载+=等。

    对于存储,您需要 3 列:分子、分母和单位。这实现了从 DB 到 C# 的 1:1 映射。

    您甚至可以按照 @Cleptus 在 cmets 中的建议创建 user defined data type,这将保持 1:1 映射。正如@Jeremy 在 cmets 中所说,这种方法有一个缺点:缺乏对平均值等聚合的支持。如果您不需要担心这一点(例如相关食谱的平均糖用量),那么这不是问题。

    是的,你可以自己做所有的数学运算,甚至封装它,但我认为自然表示会更易于维护,并且可以很好地从客户端映射到服务器,再到存储。

    最后,因为涉及到舍入,所以应该在最后一刻进行以减少误差累积,尤其是在执行求和、平均等聚合函数时。

    【讨论】:

    • "对于存储,您需要 3 列:分子、分母和单位" 或者具有这三个属性的用户定义数据类型
    • 这将阻止您在 sql 中使用聚合函数,尽管您也可以添加质量/体积 SI 基本单位。
    • 好的cmets。谢谢!
    【解决方案3】:

    假设你正在使用“简单”粗俗分数(你可以面对,比如1/31 5/63/4,但不是103/2043)你可以尝试乘以通过一些好的共同因素60600420 是典型的选择:

       1/3 * 60   ==  20
       1 5/6 * 60 == 110
       3/4 * 60   ==  45
    

    所以你有很好的旧整数值,易于存储和操作。当我们要将结果表示粗俗分数时,我们只需要计算Gretest Common Divisor gcd(result, factor)。例如:

       1 5/6 - 1/4 => // let factor be 60
       110 - 15 == 95
    

    所以我们存储95。当我们要表示它时,由于gcd(95, 60) = 5

       95 / 60 == 19 / 12 == 1 7/12
    

    一般情况中,当任何粗俗分数在技术上是可能的时,我们必须使用BigRational 或类似的结构,例如https://www.nuget.org/packages?q=BigRational 并存储分子和分母

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-04-10
      • 2010-09-15
      • 2020-12-16
      • 2017-12-24
      • 2021-03-25
      • 2018-02-24
      • 1970-01-01
      相关资源
      最近更新 更多