4

状況は次のとおりです。

プロジェクトの 1 つで使用する一般的なグラフィックス コードがいくつかあります。コードのクリーンアップを行った後、何かが機能していないように見えます (グラフィック出力が完全に間違っているように見えます)。

正しい出力が得られた最新バージョンのコードに対して diff を実行したところ、関数の 1 つを次のように変更したようです。

static public Rectangle FitRectangleOld(Rectangle rect, Size targetSize)
{
    if (rect.Width <= 0 || rect.Height <= 0)
    {
        rect.Width = targetSize.Width;
        rect.Height = targetSize.Height;
    }
    else if (targetSize.Width * rect.Height > 
        rect.Width * targetSize.Height)
    {
        rect.Width = rect.Width * targetSize.Height / rect.Height;
        rect.Height = targetSize.Height;
    }
    else
    {
        rect.Height = rect.Height * targetSize.Width / rect.Width;
        rect.Width = targetSize.Width;
    }

    return rect;
}

static public Rectangle FitRectangle(Rectangle rect, Size targetSize)
{
    if (rect.Width <= 0 || rect.Height <= 0)
    {
        rect.Width = targetSize.Width;
        rect.Height = targetSize.Height;
    }
    else if (targetSize.Width * rect.Height > 
             rect.Width * targetSize.Height)
    {
        rect.Width *= targetSize.Height / rect.Height;
        rect.Height = targetSize.Height;
    }
    else
    {
        rect.Height *= targetSize.Width / rect.Width;
        rect.Width = targetSize.Width;
    }

    return rect;
}

すべての単体テストはすべて合格であり、いくつかの構文上のショートカットを除いて、コードは何も変更されていません。しかし、私が言ったように、出力は間違っています。おそらく古いコードに戻るだけでしょうが、ここで何が起こっているのか誰か知っているかどうか知りたいです.

ありがとう。

4

3 に答える 3

22

十分な単体テストがないようです:]

残念ながら、あなたの声明

「いくつかのシンタックス ショートカットを除いて、コードは何も変更されていません」

間違っています、そしてそれがあなたの問題があるところだと思います。(それは確かにあなたの問題の1つです!)

はい、

a *= b;

と同等です

a = a * b;

しかし

a *= b / c;

と同じではありません

a = a * b / c;

代わりは

a *= b / c;    // equivalent to a = a * (b / c)
a = a * b / c; // equivalent to a = (a * b) / c

( msdn のc# 演算子の優先順位を参照)

ターゲットの高さが元の長方形の高さの正確な倍数ではない(または幅も同じ)場合、問題が発生していると思います。

そうすると、次のような状況になります。

rect.Size = (8, 20)、targetSize = (15, 25) と仮定しましょう。

元の方法を使用すると、次の計算に到達します。

rect.Width     = rect.Width * targetSize.Height / rect.Height;
//             = 8          * 25                / 20
//             = 200 / 20 (multiplication happens first)
//             = 10
// rect.Width  = 10

新しいコードを使用すると、

rect.Width    *= targetSize.Height / rect.Height;
//            *= 25 / 20
//            *= 1 (it's integer division!)
// rect.Width  = rect.Width * 1
//             = 8
// rect.Width  = 8

これは同じではありません。(ターゲット サイズが元のサイズよりも小さい場合はさらに悪化します。この場合、整数除算の結果、次元の 1 つが 0 になります!)

「[your] 単体テストがすべて合格している」場合は、特に整数以外の倍数を処理する追加のテストが必要です。

また、あなたの計算に注意してください

else if(targetSize.Width * rect.Height > 
        rect.Width * targetSize.Height)

信頼できません。非常に大きな四角形の場合、オーバーフローして誤った結果が得られる可能性があります。乗算の一部として、より大きな型 (つまり long) にキャストした方がよいでしょう。(繰り返しますが、この趣旨の単体テストが必要です)

それが役立つことを願っています!

于 2009-02-13T21:07:04.493 に答える
6

Rectangle.Width と Rectangle.Height が整数の場合、次の 2 行が異なります。

rect.Width = rect.Width * targetSize.Height / rect.Height;
rect.Width *= targetSize.Height / rect.Height;

最初の行は、乗算、除算、int へのキャスト、代入の順に実行します。2 つ目は、除算、int へのキャスト、乗算、代入を実行します。問題は、機能しないコードで、除算が乗算の前に整数にキャストされていることです。

元のコードを保持するか、除算を強制的に浮動小数点にします。

この問題をチェックするには、より適切な単体テストを作成してください。(偶数の倍数を持たない幅と高さの組み合わせを試してください (素数など)。)

于 2009-02-13T21:08:14.490 に答える
0

Daniel Lの答えに追加します。

この「最適化」のポイントは何ですか? このコードをクリーンアップして読みやすくするより良い方法があります。

于 2009-02-13T21:11:17.010 に答える