情况如下:
我们有一些通用的图形代码,我们用于其中一个项目。在对代码进行一些清理之后,似乎某些东西不再起作用了(图形输出看起来完全错误)。
我针对提供正确输出的代码的最后一个版本运行了一个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;
}
我们所有的单元测试都已通过,除了一些语法快捷方式外,代码中没有任何内容发生变化。但就像我说的那样,输出是错误的。我们可能只是回到旧代码,但我很好奇是否有人知道这里发生了什么。
感谢。
答案 0 :(得分:23)
听起来你没有足够的单元测试:]
不幸的是,你的陈述
“代码中的任何内容都没有改变,除了一些语法快捷方式”
错了,我猜你的问题就在那里。 (这肯定是你的一个问题!)
是,
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# operator precedence)
当您的目标高度不是原始矩形高度的精确倍数(或宽度相同)时,我猜你遇到了麻烦。
然后你最终会遇到以下情况:
我们假设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)
不可靠;对于非常大的矩形,它有可能溢出并给你不正确的结果。作为乘法的一部分,你最好能够投射到更大的类型(即长)。 (同样,应该有一些单元测试来实现这种效果)
希望有所帮助!
答案 1 :(得分:6)
如果Rectangle.Width和Rectangle.Height是整数,则以下两行不同:
rect.Width = rect.Width * targetSize.Height / rect.Height;
rect.Width *= targetSize.Height / rect.Height;
第一行按顺序执行乘法,除法,转换为int,然后执行赋值。第二个执行除法,转换为int,乘法,然后赋值。问题是,在你的非工作代码中,你的鸿沟在乘法之前被转换为整数。
保留原始代码或强制除法为浮点。
编写更好的单元测试来检查此问题。 (尝试没有偶数倍的宽度/高度组合(例如素数)。)
答案 2 :(得分:0)
添加丹尼尔的答案。
这种'优化'有什么意义?有更好的方法来清理这些代码,并使其更具可读性。