你会如何重构这段代码?
double p = (Convert.ToDouble(inta) / Convert.ToDouble(intb)) * 100;
double v = (p / 100) * Convert.ToDouble(intc);
return (int)v;
对我而言似乎非常混乱,我知道我可以把它挤到一条线上,但我有兴趣知道其他人会怎么做。
由于
答案 0 :(得分:7)
假设inta
,intb
和intc
被输入为int
/ Int32
,那么Convert.ToDouble
基本上与简单投射相同到double
。
return (int)((inta / (double)intb) * intc);
这实际上是否值得重构是另一回事。将中间计算保持为单独的语句以提高可读性通常更有意义,即使您不需要这些中间结果。当然,拥有有意义的变量名会产生大的差异。
答案 1 :(得分:4)
说真的,不要。代码有什么问题 - 忽略可能存在的数学问题,只是查看代码结构本身?
我不会重构。将它全部压缩到一行将使其难以阅读。如果我绝对有做某事,我会为a,b和c的双重版本创建新变量,如下所示:
//set up variables
double doubleA = Convert.ToDouble(inta);
double doubleB = Convert.ToDouble(intb);
double doubleC = Convert.ToDouble(intc);
//do calculations
double p = (doubleA / doubleB) * 100
double v = (p / 100) * doubleC; //why did we divide by 100 when we multiplied by it on the line above?
return (int)v; //why are we casting back to int after all the fuss and bother with doubles?
但实际上我宁愿不管它!
答案 2 :(得分:4)
好吧,首先我会使用更有意义的名字,并且猜测这是采用整数比率,将其转换为百分比,将该百分比应用于另一个原始值,并返回一个新值,即结果被截断为整数。
double percent = (Convert.ToDouble( numer ) / Convert.ToDouble( denom )) * 100;
double value = (percent / 100) * Convert.ToDouble( originalValue );
return (int)value;
使用Convert和强制转换之间的一个区别是Convert将为越界引发异常,但是不会强制转换,并且转换为int会导致Int32.MinValue。因此,如果value
对于int或Infinity
或NaN
而言太大或太小,您将获得Int32.MinValue而不是最后的异常。其他转换器不能失败,因为任何int都可以表示为double。
所以你可以使用没有改变含义的强制转换来编写它,并利用这样一个事实:在一个涉及整数和双精度的表达式中,整数被自动转换成双精度:
double percent = ((double) numer ) / denom ) * 100;
double value = (percent / 100) * originalValue;
return (int)value;
现在,C#在分配到15-16时截断了双重结果,但它的实现定义了中间体是否以更高的精度运行。我认为这不会改变可以转换为int的范围内的输出,但我不知道,并且值空间太大而无法进行详尽的测试。因此,如果没有完全的规范,那么函数的目的是什么,你可以更改其他内容并确保不会更改输出。
如果比较这些重构,每个重构都是天真的数学等价物,并通过它们运行一系列值:
static int test0(int numer, int denom, int initialValue)
{
double percent = (Convert.ToDouble(numer) / Convert.ToDouble(denom)) * 100;
double value = (percent / 100) * Convert.ToDouble(initialValue);
return (int)value;
}
static int test1(int numer, int denom, int initialValue)
{
return (int)((((((double)numer) / denom) * 100 ) / 100 ) * initialValue);
}
static int test2(int numer, int denom, int initialValue)
{
return (int)((((double)numer) / denom) * initialValue);
}
static int test3(int numer, int denom, int initialValue)
{
return (int)((((double)numer) * initialValue) / denom);
}
static int test4(int numer, int denom, int initialValue)
{
if (denom == 0) return int.MinValue;
return (numer * initialValue / denom);
}
然后,您会得到以下结果,即计算testN
不等于test0
并让它运行几个小时的次数:
numer in [-10000,10000]
denom in [-10000,0) (0,10000]
initialValue in [-10000,-8709] # will get to +10000 eventually
test1 fails = 0 of 515428330128 tests, 100% accuracy.
test2 fails = 110365664 of 515428330128 tests, 99.9785875828803% accuracy.
test3 fails = 150082166 of 515428330128 tests, 99.9708820495057% accuracy.
test4 fails = 150082166 of 515428330128 tests, 99.9708820495057% accuracy.
因此,如果您想要一个完全等效的函数,那么您似乎可以到达test1
。虽然100s应该在test2
中取消,但实际上它们会在一些边缘情况下影响结果 - 中间值的舍入推动值的一侧或另一个整数。对于此测试,输入值在-10000到+10000范围内,因此test4
中的整数乘法不会溢出,因此test3
和test4
是相同的。对于更宽的输入范围,test4将更频繁地偏离。
始终验证您对自动化测试的重构。并且不要认为计算机所运用的值与高中数学中的数字相似。
答案 3 :(得分:1)
首先,我将给出p,v,inta,intb等有意义的名称。
前两行可以合并:
double pv = ((double)inta/intb)*intc;
return (int)pv;
答案 4 :(得分:0)
return (int)(Convert.ToDouble(inta * intc) / Convert.ToDouble(intb));
答案 5 :(得分:0)
return (int)(((double)inta / intb) * intc);
<强>(固定)强>
答案 6 :(得分:0)
当然这只是
inta * intc / intb
如果您不需要显式的p值,则根本不需要进行任何转换。
答案 7 :(得分:0)
@ FrustratedWithFormsDes答案的变体:
double doubleA = (double) (inta * intc);
double doubleB = (double) intb;
return (int) (doubleA / doubleB);
答案 8 :(得分:0)
有一些有趣的观点似乎没有其他人覆盖,所以我会加入混合......
所以(假设非巨大的int值,并接受我们可能抛出一个div-by-zero异常)最终结果(为了清晰起见没有重命名)可能是:
return((int) ((inta * intc) / (double) intb));
它与接受的答案没有太大的不同,但在某些平台上表现稍好一些(通过使用整数乘法而不是双精度乘法)。