【问题标题】:When cast NaN to int, why does the value change depending on whether it is assigned to a variable or not?当将 NaN 转换为 int 时,为什么值会根据是否分配给变量而改变?
【发布时间】:2017-03-01 16:27:04
【问题描述】:

将 double.NaN 转换为 int 将为 0。

Console.WriteLine(unchecked((int)double.NaN));    // 0

但是,将 double.NaN 分配给变量然后强制转换为 int 将是 -2147483648。

double value = double.NaN;
Console.WriteLine (unchecked ((int) value));    // -2147483648
Console.WriteLine ((int) value);                // -2147483648

为什么结果会根据是否赋值给变量而改变?

C# 规范

6.2.1 显式数字转换

  • 在检查的上下文中,转换过程如下:
    • 如果操作数的值为 NaN 或无穷大,则会引发 System.OverflowException。
    • 否则,源操作数将向零舍入到最接近的整数值。如果这个整数值在目标类型的范围内,那么这个值就是转换的结果。
    • 否则,将引发 System.OverflowException。
  • 在未经检查的上下文中,转换始终成功,并按如下方式进行。
    • 如果操作数的值为 NaN 或无穷大,则转换的结果是目标类型的未指定值。
    • 否则,源操作数将向零舍入到最接近的整数值。如果这个整数值在目标类型的范围内,那么这个值就是转换的结果。
    • 否则,转换的结果是目标类型的未指定值。

环境

  • 编译器:Visual Studio 2013
  • Rimtime:.NET Framework 4.6.0
  • 操作系统:Windows 10 版本 1607
  • CPU:英特尔酷睿 i7 920

窗户:

Console.WriteLine(Environment.OSVersion);    // Microsoft Windows NT 6.2.9200.0
Console.WriteLine(Environment.Version);      // 4.0.30319.42000

【问题讨论】:

  • 我建议你看看 IL - 我怀疑 compiler 正在执行第一次转换,只是将结果硬编码为 0。而在你的变量情况下您是在要求 CLR 执行此操作。
  • 我无法复制。 Console.WriteLine(unchecked((int)double.NaN)); 在我尝试时打印 -2147483648
  • 您使用的是什么版本的编译器?我也没有收到0
  • 这让我想起了无数“为什么int *c; printf("%d", *c); print 37` 人们问的关于 C 的未定义行为问题。规范告诉你这将是一个未指定的数字。你甚至不应该感到惊讶如果每次运行程序时它都是不同的数字。
  • 你在Nasal Deamons的领域,它返回两个不同的数字,因为它“感觉像它”。

标签: c#


【解决方案1】:

默认情况下,控制台应用程序编译为unchecked。这就是为什么您的上一个示例显示的值与显式使用 unchecked 的示例相同。

对于您的其余示例,您引用的规范部分适用:

  • 在未经检查的上下文中,转换始终成功,并按如下方式进行。
    • 如果操作数的值为 NaN 或无穷大,则转换的结果是目标类型的未指定值

(我强调)。

未指定表示您不能依赖结果。可以是0-2147483648 或其他任何内容。

我想弄清楚在你的特殊情况下会发生什么,但我得到-2147483648 两种方式。所以很难追踪。

【讨论】:

  • 我也无法在我的系统上重现它,但将 .NET Fiddle 编译器更改为“.NET 4.5”而不是“Roslyn 1.0.0-rc1”,它显示为零:dotnetfiddle.net/ckfngN
【解决方案2】:

环顾四周后,我想我明白这里发生了什么。您不是在投射 0,而是在投射 NaN。 intInt32 的范围比 double 小得多。我认为这些令人愉快的不太清楚的规则共同作用导致了差异:

  • 如果操作数的值为 NaN 或无穷大,则转换的结果是目标类型的未指定值。 否则,源操作数向零舍入到最接近的整数值。
  • 如果这个整数值在目标类型的范围内,那么这个值就是转换的结果。

这里的目标类型是较小的int,而您得到的值“-2147483648”是 32 位有符号整数的最小值。这表明铸造操作正在将 NaN 转换为 double.NegativeInfinity 以便对其执行操作。与double.NegativeInfinity 最接近的整数是int.MinValue,这是您得到的值。

综上所述,该值未指定这一事实意味着您获得的结果可能取决于太多因素,甚至取决于您是否分配了变量,甚至是什么处理器架构你有。您不应该使用 NaN 强制转换来做任何有意义的事情,因此 NaN 永远不会被分配一个实际的设置值(主要是因为根据定义,它会是一个数字)。出于这个原因,在大多数情况下,NaN 结果应被视为错误。最终,实际值是没有意义的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-05-24
    • 2021-10-15
    • 1970-01-01
    • 2023-04-08
    • 2012-06-19
    • 2015-01-07
    • 1970-01-01
    • 2016-05-12
    相关资源
    最近更新 更多