即使条件计算结果为 false,if 语句似乎也在计算

发布于 2024-11-08 05:29:10 字数 2426 浏览 0 评论 0 原文

昨晚工作到很晚,我们试图找出出现问题的原因。验证检查在不应该失败的时候失败了。

我们最终在这段代码中添加了一条打印语句(从 Reflector 中反汇编,以检查该代码实际上是我们编写的代码):

public static string Redacted(string name, DateTime lastModified)
{
    long ticks = lastModified.Ticks;
    if ((ticks != (ticks - (ticks % 10000L))) &&
            (lastModified != DateTime.MaxValue))
    {
        Log.Debug(string.Format("Last Modified Date = '{0}'. Ticks = '{1}'. TicksCalc = '{2}'",
            lastModified.ToString("dd/MM/yyyy hh:mm:ss.fff"),
            ticks, ticks - (ticks % 10000L)));

它打印(重新格式化):

Last Modified Date = '22/03/2011 12:16:22.000'.
Ticks     = '634363497820000000'.
TicksCalc = '634363497820000000'            

但条件是“ticks” (等于上面打印的 Ticks)不等于“(ticks - (ticks % 10000))”(等于 TicksCalc)! 634363497820000000!= 634363497820000000?!

为了确定这里发生了什么,我们添加了另外两条语句:

long ticks = lastModified.Ticks;
/* Added following two lines: */
long num2 = ticks - (ticks % 10000L);
Log.Debug((ticks == num2).ToString());
/* */
if ((ticks != (ticks - (ticks % 10000L))) &&
        (lastModified != DateTime.MaxValue))
{
    Log.Debug(string.Format("Last Modified Date = '{0}'. Ticks = '{1}'. TicksCalc = '{2}'",
        lastModified.ToString("dd/MM/yyyy hh:mm:ss.fff"),
        ticks, ticks - (ticks % 10000L)));

正如它应该有的那样,这条语句打印了 true (当使用相同的值进行测试时),并且没有编写第二行。

感觉有点失落,然后我们再次删除这两行,重新编译并重新运行。原来的行为又重演了。

今天早上,我录制了一个视频

该视频首先显示使用“损坏”代码在方法中命中断点,然后使用“工作”代码重建并重新运行。请注意,即使调试器显示 if 条件的计算结果为 false,仍会输入正文。

我以前见过在调试器观察时发生这样的事情,因为调试器强制评估某些东西,但无论是否使用调试器,这种情况都会发生。

此外,这只发生在发布模式下(即启用了 JIT 优化)。

以下是两个版本的反汇编方法:working不工作。我无法真正阅读汇编,所以我将它们发布在这里希望得到澄清。

我希望答案不是我完全忽略的显而易见的事情......!

编辑:这是 IL。我不认为它有什么问题,因为它反编译为正确的 C#:

更新

已被 Microsoft 确认为错误,将在下一版本中修复

Late At Work last night, we were trying to figure out why something was failing. A validation check was failing when it shouldn't have been.

We ended up adding a print statement to this code (disassembled from Reflector in order to check that the code was actually what we had written):

public static string Redacted(string name, DateTime lastModified)
{
    long ticks = lastModified.Ticks;
    if ((ticks != (ticks - (ticks % 10000L))) &&
            (lastModified != DateTime.MaxValue))
    {
        Log.Debug(string.Format("Last Modified Date = '{0}'. Ticks = '{1}'. TicksCalc = '{2}'",
            lastModified.ToString("dd/MM/yyyy hh:mm:ss.fff"),
            ticks, ticks - (ticks % 10000L)));

It printed (reformatted):

Last Modified Date = '22/03/2011 12:16:22.000'.
Ticks     = '634363497820000000'.
TicksCalc = '634363497820000000'            

But the condition is that "ticks" (which is equal to Ticks printed above) is not equal to "(ticks - (ticks % 10000))" (which is equal to TicksCalc)! 634363497820000000 != 634363497820000000?!

In order to determine what is going on here, we added another two statements:

long ticks = lastModified.Ticks;
/* Added following two lines: */
long num2 = ticks - (ticks % 10000L);
Log.Debug((ticks == num2).ToString());
/* */
if ((ticks != (ticks - (ticks % 10000L))) &&
        (lastModified != DateTime.MaxValue))
{
    Log.Debug(string.Format("Last Modified Date = '{0}'. Ticks = '{1}'. TicksCalc = '{2}'",
        lastModified.ToString("dd/MM/yyyy hh:mm:ss.fff"),
        ticks, ticks - (ticks % 10000L)));

As it should have, this one printed true (when testing with the same value), and didn't write the second line.

Feeling a bit lost, we then removed the two lines again, recompiled, and reran. The original behaviour repeated itself.

This morning, I recorded a video.

The video first of all shows hitting a breakpoint in the method using the 'broken' code, then rebuilding and rerunning using the 'working' code. Note that even though the debugger displays that the if condition evaluates as to false, the body is still entered.

I've seen things like this happen before when observed by the debugger, due to the debugger forcing some things to be evaluated, but this happens whether or not the debugger is employed.

Furthermore, this only happens in Release mode (i.e. with JIT optimizations enabled).

Here are the disassembled methods for both versions: working, not working. I can't really read assembly, so am posting them here in the hopes of elucidation.

I hope that the answer isn't something obvious that I've overlooked completely...!

Edit: Here is the IL. I don't think there's anything wrong with it because it decompiles to the correct C#:

Update:

Confirmed as a bug by Microsoft, to be fixed in the next release.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

傲鸠 2024-11-15 05:29:10

我用简化的代码进行了一些实验:
http://nopaste.info/2c99a0e028_nl.html

最有趣的变化是:

static readonly long variableZero=0; 
const long constZero=0; 

public static void Broken2( long ticks2) 
 { 
     long ticks = ticks2+variableZero; 
     if (ticks != (ticks - (ticks % 10000L))) 
     { 
         string.Format("Last Modified Date = '{0}'. Ticks = '{1}'. TicksCalc = '{2}'", 
             "n/A", 
             ticks, ticks - (ticks % 10000L)).Dump(); 
     } 
 }

如果我替换 variableZero使用constantZero它可以工作。


所以我很确定这要么是抖动,要么是编译器错误。

我已在 MS Connect 上提交了错误报告:
https://connect.microsoft.com/VisualStudio/feedback/ details/671105/jitter-or-c-compiler-bug#details


更新:只有在没有附加调试器的情况下才会出现奇怪的行为。即当启用 Jit 优化时。所以我很确定这是一个抖动错误。

对于没有 linq-pad 的人来说,现在有一个简单的 C# 控制台项目: http://nopaste.info/00a0e37328_nl.html

I experimented a bit with simplified code:
http://nopaste.info/2c99a0e028_nl.html

The most interesting variation is:

static readonly long variableZero=0; 
const long constZero=0; 

public static void Broken2( long ticks2) 
 { 
     long ticks = ticks2+variableZero; 
     if (ticks != (ticks - (ticks % 10000L))) 
     { 
         string.Format("Last Modified Date = '{0}'. Ticks = '{1}'. TicksCalc = '{2}'", 
             "n/A", 
             ticks, ticks - (ticks % 10000L)).Dump(); 
     } 
 }

If I replace variableZero with constantZero it works.


So I'm pretty sure it is either a jitter or a compiler bug.

I've filed a bugreport on MS Connect:
https://connect.microsoft.com/VisualStudio/feedback/details/671105/jitter-or-c-compiler-bug#details


Update: The strange behavior only occurs if no debugger is attached. i.e. when Jit optimization is enabled. So I'm pretty sure it's a jitter bug.

And for people without linq-pad there is now a plain C# console project: http://nopaste.info/00a0e37328_nl.html

十雾 2024-11-15 05:29:10

查看此线程。

Visual Studio 2008 中的 If 语句怪异

归结为这一点,您不能一直信任调试器。

要“修复”该 if 语句,请向其中添加一个空的 else {} 语句。调试器将按预期工作。

Check out this thread.

If statement weirdness in Visual Studio 2008

It comes down to this, you can't trust the debugger all the time.

To "fix" that if statement, add an empty else {} statement to it. The debugger will work as expected.

ぶ宁プ宁ぶ 2024-11-15 05:29:10

这确实看起来像——咳咳——吉特巴舞。您能否在“if”语句上设置一个断点,并向我们展示断点后的反汇编视图的屏幕截图?

That does indeed look like an - ahem - jitterbug. Can you set a break-point on the "if" statement and show us a screenshot of the disassembly view after it hits?

ζ澈沫 2024-11-15 05:29:10

不久前我也有过类似的事情。
就我而言,这与我正在比较两个整数值这一事实有关,其中一个值实际上是对装箱整数的引用,另一个是真正的原始值。

问题是,如果打印出装箱整数和基元的值,它们看起来是一样的,但比较它们是另一回事。您将得到参考比较而不是值比较。

答案很简单:

long ticks = lastModified.Ticks;
long num2 = ticks - (ticks % 10000L);
if ((ticks != num2) && (lastModified != DateTime.MaxValue))
{ do your thing here! }

I had something similar a while ago.
In my case it had to do with the fact that i was comparing 2 integer values where one value was actually a reference to a boxed integer and the other was a real primitive value.

The thing is that if you print out the value of the boxed integer and the primitive, they look the same, but comparing them is another thing. You'll get a reference comparison in stead of a value comparison.

The answer is easy:

long ticks = lastModified.Ticks;
long num2 = ticks - (ticks % 10000L);
if ((ticks != num2) && (lastModified != DateTime.MaxValue))
{ do your thing here! }
心在旅行 2024-11-15 05:29:10

太疯狂了。您是否尝试过无缘无故地重新排序 if 语句?

if (lastModified != DateTime.MaxValue && ticks != (ticks - (ticks % 10000L))

另外,如果这不起作用(因为它不应该起作用,考虑到它一开始就不应该成为问题),您能否以有问题的形式显示代码的实际 IL?

另一件事,ticks 检查不能简化为:

(ticks % 10000L) != 0

That's crazy. Have you tried, for no good reason, to reorder the if statement?

if (lastModified != DateTime.MaxValue && ticks != (ticks - (ticks % 10000L))

Also, if this doesn't work (as it shouldn't, considering it shouldn't be a problem in the first place), can you show the actual IL for the code in in the problematic form?

One other thing, couldn't the ticks check be simplified to:

(ticks % 10000L) != 0
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文