Почему наш графический код c # больше не работает? - PullRequest
4 голосов
/ 14 февраля 2009

Вот ситуация:

У нас есть некоторый общий графический код, который мы используем для одного из наших проектов. После некоторой очистки кода кажется, что что-то больше не работает (графический вывод выглядит совершенно неправильно).

Я запустил diff для последней версии кода, который дал правильный вывод, и похоже, что мы изменили одну из наших функций следующим образом:

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;
}

Все наши модульные тесты проходят все, и ничего в коде не изменилось, за исключением некоторых синтаксических ярлыков. Но, как я уже сказал, вывод неправильный. Мы, вероятно, просто вернемся к старому коду, но мне любопытно, если кто-нибудь знает, что здесь происходит.

Спасибо.

Ответы [ 3 ]

23 голосов
/ 14 февраля 2009

Похоже, у вас недостаточно юнит-тестов:]

К сожалению, ваше заявление

"Ничего в коде не изменилось, за исключением некоторых синтаксических ярлыков"

не так, и я думаю, вот в чем твоя проблема. (Это, безусловно, одна из ваших проблем!)

Да,

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

(см. c # приоритет оператора в msdn)

Я предполагаю, что вы столкнулись с проблемами, когда высота вашей цели не кратна первоначальной высоте прямоугольника (или одинаковой для ширины).

Тогда вы получите следующую ситуацию:

Предположим, что 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

что не то же самое. (Становится хуже, если целевой размер меньше исходного размера; в этом случае целочисленное деление приведет к тому, что одно из измерений будет 0!)

Если «[ваши] модульные тесты проходят все», вам определенно потребуются некоторые дополнительные тесты, особенно те, которые работают с нецелыми кратными числами.

Также обратите внимание, что ваш расчет

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

не надежно; для очень больших прямоугольников он может переполниться и дать неверные результаты. Вам было бы лучше привести к более крупному типу (то есть длинному) как часть умножения. (Опять же, для этого нужно провести несколько юнит-тестов)

Надеюсь, это поможет!

6 голосов
/ 14 февраля 2009

Если Rectangle.Width и Rectangle.Height являются целыми числами, следующие две строки различаются:

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

Первая строка выполняет умножение, деление, приведение к int, затем присваивание в указанном порядке. Второй выполняет деление, приведение к int, умножение, затем присвоение. Проблема в том, что в вашем нерабочем коде ваше деление преобразуется в целое число перед умножением .

Сохраняйте исходный код или делайте деление с плавающей точкой.

Напишите лучшие юнит-тесты, чтобы проверить эту проблему (Попробуйте использовать комбинацию ширины и высоты, которая не имеет четных кратных чисел (например, простых чисел).)

0 голосов
/ 14 февраля 2009

Добавить к ответу Даниэля Л.

Какой смысл этой «оптимизации»? Есть лучшие способы очистить этот код и сделать его более читабельным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...