【问题标题】:Where is the code that does floating-point division for C#?为 C# 进行浮点除法的代码在哪里?
【发布时间】:2019-09-27 22:44:24
【问题描述】:

问题

我想查看执行浮点除法并在除以零时返回Infinity 的代码。我在哪里可以找到它?

代码示例

想知道这一点的动机来自 .NET 文档中的这段代码。我本来希望得到DivideByZeroException,但我得到了Infinity

https://docs.microsoft.com/en-us/dotnet/api/system.dividebyzeroexception?view=netframework-4.8#remarks

using System;

public class Example
{
   public static void Main()
   {
      int number1 = 3000;
      int number2 = 0;

      Console.WriteLine((double)number1 / number2);
   }
}
// The example displays the following output:
//        Infinity

文档

我找到了说明浮点除以零不会引发异常的文档,这很好:

https://docs.microsoft.com/en-us/dotnet/api/system.dividebyzeroexception?view=netframework-4.8#remarks

将浮点值除以零不会引发异常;它 导致正无穷大、负无穷大或不是数字 (NaN),根据 IEEE 754 算术规则。因为 以下示例使用浮点除法而不是整数 除法,该操作不会抛出 DivideByZeroException 例外。

.NET Framework 中的代码

在 Visual Studio 中,我将鼠标悬停在“/”除号上,Resharper 显示

double double.operator /(double left, double right)

现在,我想看看它的实现,看看Infinity 是如何返回的。但我找不到。

我在这里检查过

但看不到除法运算符。

然后上面的链接带我到这里

但也没有关于实现细节。

我也看过这里

但只能看到注释掉的代码

///        Double IArithmetic<Double>.Divide(Double divisor, out bool overflowed) {
///            Double s = m_value / divisor;
///            overflowed = IsInfinity(s) || IsNaN(s);
///            return s;
///        }

corefxDouble 实现中也没有任何内容:

我会期待这样的东西(伪代码):

public static double operator /(double left, double right)
{
   if (right == 0)
   {
      return double.Infinity;
   }
   else
   {
      ...
   }
}

【问题讨论】:

  • 您实际上不需要查看源代码,ECMA 规范应该可以。查看 14.7.2 除法运算符.....有一张表格告诉你它是如何实现的
  • 实现在. FPU,不是一行代码。无论哪种情况,它都是场外资源,因此离题。
  • 这是你的来源 fdivp felixcloutier.com/x86/fdiv:fdivp:fidiv
  • @user207421:与异地资源相关的 Stack Overflow 关闭的原因是请求对异地资源提出建议,因为它们会吸引意见。它不适用于关于资源存在的查询,这些资源是否适用于某个请求是事实而非意见。

标签: c# floating-point divide-by-zero


【解决方案1】:

您的代码编译为以下 CIL:

.method public hidebysig static void  Main() cil managed
{
  .entrypoint
  // Code size       21 (0x15)
  .maxstack  2
  .locals init (int32 V_0,
           int32 V_1)
  IL_0000:  nop
  IL_0001:  ldc.i4     0xbb8
  IL_0006:  stloc.0
  IL_0007:  ldc.i4.0
  IL_0008:  stloc.1
  IL_0009:  ldloc.0
  IL_000a:  conv.r8
  IL_000b:  ldloc.1
  IL_000c:  conv.r8
  IL_000d:  div
  IL_000e:  call       void [mscorlib]System.Console::WriteLine(float64)
  IL_0013:  nop
  IL_0014:  ret
} // end of method Example::Main

IL_000d: div 调用是发生的除法操作。这意味着 CLR 将执行这项工作,因此您不会找到任何源代码来完成这项工作。如果我们深入研究浮点值的除法运算符的 CLR 源代码(此处显示 coreclr):

TFp FpDiv(TFp dividend, TFp divisor)
{
#ifdef _TARGET_ARMARCH_
    // From the ECMA standard:
    //
    // If [dividend] is zero and [divisor] is zero
    //   the result is NaN.
    // If [dividend] is infinity and [divisor] is infinity
    //   the result is NaN.

    if (dividend == 0 && divisor == 0)
    {
        return TFpTraits::NaN();
    }
    else if (!_finite(dividend) && !_isnan(dividend) && !_finite(divisor) && !_isnan(divisor))
    {
        return TFpTraits::NaN();
    }
#endif // _TARGET_ARMARCH_

    return dividend / divisor;
}

您可以看到它正在处理一些先决条件,然后调用编译器的内在操作。在 x86 上,这将是一个 FDIV 调用,它被记录为引发除以零标志,由于 IEEE 754,C 运行时将变成无穷大。这种无穷大的工作方式可以将链备份到您的 C# 应用程序。

【讨论】:

  • C 代码肯定会屏蔽#Z 异常并让 FDIV 完成其余的工作吗?
猜你喜欢
  • 2011-09-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多