C# 将两个数四舍五入到最近的可等分数的算法

C# 将两个数四舍五入到最近的可等分数的算法,c#,math,rounding,C#,Math,Rounding,我知道,这个标题的措词不太好,但想不出更好的写作方法 这是一个场景——我有两个输入框,都表示整数量。一个代表在我们的单位中,另一个代表在供应商的单位中。有一个乘数定义了如何从我们的转换到他们的。在下面的例子中,我是说我们的两个单位等于他们的五个单位。那么比如说, decimal multiplier = 0.4; // Two of our units equals five of theirs int requestedQuantity = 11; // Our units int suppl

我知道,这个标题的措词不太好,但想不出更好的写作方法

这是一个场景——我有两个输入框,都表示整数量。一个代表在我们的单位中,另一个代表在供应商的单位中。有一个乘数定义了如何从我们的转换到他们的。在下面的例子中,我是说我们的两个单位等于他们的五个单位。那么比如说,

decimal multiplier = 0.4; // Two of our units equals five of theirs
int requestedQuantity = 11; // Our units
int suppliedQuantity = 37; // Their units

// Should return 12, since that is the next highest whole number that results in both of us having whole numbers (12 of ours = 30 of theirs)
int correctedFromRequestedQuantity = GetCorrectedRequestedQuantity(requestedQuantity, null, multiplier); 

// Should return 16, since that is the next highest whole number that results in both of us having whole numbers (16 of ours = 40 of theirs);
int correctedFromSuppliedQuantity = GetCorrectedRequestedQuantity(suppliedQuantity, multiplier, null);
这是我为处理这个问题而编写的函数。我没有对乘法器/取整器进行零除检查,因为我已经在其他地方检查过了。这么做似乎很疯狂,但有更好的方法吗

public int GetCorrectedRequestedQuantity(int? input, decimal? multiplier, decimal? rounder)
{
  if (multiplier == null)
  {
    if (rounder == null)
      return input.GetValueOrDefault();
    else
      return (int)Math.Ceiling((decimal)((decimal)Math.Ceiling(input.GetValueOrDefault() / rounder.Value) * rounder.Value));
  }
  else if (input.HasValue)
  {
    // This is insane...
    return (int)Math.Ceiling((decimal)((decimal)Math.Ceiling((int)Math.Ceiling((decimal)input * multiplier.Value) / multiplier.Value) * multiplier.Value));
  }
  else
    return 0;
}

我脑袋里最好的办法就是半暴力地强迫它。听起来确实是这样。所以可能有一种更简单的解决方法

首先,我们需要找出乘法器在什么样的“批量”中成为整体。这样,我们就可以完全停止使用浮动/双浮动。理想情况下,这应该与乘法器一起提供(因为浮点数学很混乱)

获取最接近的BatchSize倍数:

int requestedQuantity = 11; // Our units
int result = OurBatchSize;
int batchCount = 0;

while(temp < requestedQuantity){
  result += OurBatchSize;
  batchCount++
}

//result contains the answer here. Return it
//batchCount * TheirBatchSize will also tell you how much they get.
对于BatchSize 4,您将获得:

  • 十三,;13%4=1; 4-1=3; 13+3=16;
  • 十四,;14%4=2; 4-2=2; 14+2=16;
  • 十五,;15%4=3; 4-3=1; 15+1=16;
  • 十六,;16%4=0; 使用了Else。16已经是正确的了

    • 我头脑中最好的想法是半暴力地强迫它。听起来确实是这样。所以可能有一种更简单的解决方法

      首先,我们需要找出乘法器在什么样的“批量”中成为整体。这样,我们就可以完全停止使用浮动/双浮动。理想情况下,这应该与乘法器一起提供(因为浮点数学很混乱)

      获取最接近的BatchSize倍数:

      int requestedQuantity = 11; // Our units
      int result = OurBatchSize;
      int batchCount = 0;
      
      while(temp < requestedQuantity){
        result += OurBatchSize;
        batchCount++
      }
      
      //result contains the answer here. Return it
      //batchCount * TheirBatchSize will also tell you how much they get.
      
      对于BatchSize 4,您将获得:

      • 十三,;13%4=1; 4-1=3; 13+3=16;
      • 十四,;14%4=2; 4-2=2; 14+2=16;
      • 十五,;15%4=3; 4-3=1; 15+1=16;
      • 十六,;16%4=0; 使用了Else。16已经是正确的了

      将乘数表示为最低值的分数。我不知道.NET是否有一个分数类,但如果没有,您可能会找到一个C#实现,或者自己编写。因此,假设乘法器由两个整数a/b以最小项给出,其中≠ 0和b≠ 0这也意味着在另一个方向上的转换是通过乘以b/a得到的。在您的示例中,a=2和b=5,a/b=0.4

      现在假设您要转换一个整数X。如果您仔细考虑一下,您将看到您真正想要的是向上移动X,直到b除以X。您需要添加到X的数字只是(b-(X%b))%b。所以在一个方向上转换就是

      return (a * (X + (b - (X % b) % b))) / b;
      
      return (b * (Y + (a - (X % a) % a))) / a;
      
      把Y转换成另一个方向就是

      return (a * (X + (b - (X % b) % b))) / b;
      
      return (b * (Y + (a - (X % a) % a))) / a;
      

      将乘数表示为最低项的分数。我不知道.NET是否有一个分数类,但如果没有,您可能会找到一个C#实现,或者自己编写。因此,假设乘法器由两个整数a/b以最小项给出,其中≠ 0和b≠ 0这也意味着在另一个方向上的转换是通过乘以b/a得到的。在您的示例中,a=2和b=5,a/b=0.4

      现在假设您要转换一个整数X。如果您仔细考虑一下,您将看到您真正想要的是向上移动X,直到b除以X。您需要添加到X的数字只是(b-(X%b))%b。所以在一个方向上转换就是

      return (a * (X + (b - (X % b) % b))) / b;
      
      return (b * (Y + (a - (X % a) % a))) / a;
      
      把Y转换成另一个方向就是

      return (a * (X + (b - (X % b) % b))) / b;
      
      return (b * (Y + (a - (X % a) % a))) / a;
      

      如果某些东西可以在没有休息的情况下进行划分,那么很容易使用模运算符进行检查:|当然,对于Double,您必须在精度上处理浮点:规则和当前的处理步骤非常不清楚。请添加很多评论来解释你在做什么。嗨,克里斯托弗,我添加了更多的评论,如果我能进一步澄清,请让我知道。基本上,挑战在于获取输入(以他们的单位或我们的单位)以使我们的单位和他们的单位都是基于乘法器的整数。如果某些东西可以在没有休息的情况下进行划分,则可以使用模运算符进行检查:|当然是使用Double,您必须在精度上处理浮点:规则和当前的处理步骤非常不清楚。请添加很多评论来解释你在做什么。嗨,克里斯托弗,我添加了更多的评论,如果我能进一步澄清,请让我知道。基本上,挑战在于获取输入(以他们的单位或我们的单位)以使我们的单位和他们的单位都是基于乘数的整数。嗨,克里斯托弗,谢谢你的回答-我正在尽可能避免使用while循环。实际上,它应该运行得很好,但感觉应该有一种更优雅的方式来实现这一点:/@KiranRamaswamy如果只是四舍五入,像“(int/int)+1”这样的代码就可以了。您可以使用“(int/int)”作为起点/方法。并且只计算到一个完全匹配的
      candidate%OurBatchSize==0
      @KiranRamaswamy,我确信如果没有循环,它是不可解的。在大多数情况下,人类可以快速从记忆中提取下一个最接近的值。但是计算机呢?它们必须循环。如果您可以预先生成可能值的列表(4、8、12、16、20),则可以将其转化为内存访问问题。但这仍然是一个循环。但是在某个地方,CPU必须以某种方式循环。@KiranRamaswamy
      Math.InternalRound()
      -在
      Math.Round()的双变量后面执行实际工作的CPU使用了一个快捷方式。但我不确定你是否能把它翻译成有用的东西@KiranRamaswamy找到了另一个解决方案,使用数学和简单的if。嗨,Christopher,谢谢你的回答-我正在尽可能避免使用while循环。实际上,它应该运行得很好,但感觉应该有一种更优雅的方式来实现这一点:/@KiranRamaswamy,如果它只是四舍五入,代码就像“(in