我有一个简单的例程,它从浮点值计算宽高比.因此,对于值1.77777779,例程返回字符串"16:9".我在我的机器上测试了它,它工作正常.
例程如下:
public string AspectRatioAsString(float f)
{
bool carryon = true;
int index = 0;
double roundedUpValue = 0;
while (carryon)
{
index++;
float upper = index * f;
roundedUpValue = Math.Ceiling(upper);
if (roundedUpValue - upper <= (double)0.1 || index > 20)
{
carryon = false;
}
}
return roundedUpValue + ":" + index;
}
Run Code Online (Sandbox Code Playgroud)
现在在另一台机器上,我得到了完全不同的结果.所以在我的机器上,1.77777779给出"16:9",但在另一台机器上我得到"38:21".
Eri*_*ert 24
这是C#规范的一个有趣的部分,来自4.1.6节:
可以以比操作的结果类型更高的精度执行浮点运算.例如,某些硬件体系结构支持"扩展"或"长双"浮点类型,其范围和精度比double类型更大,并使用此更高精度类型隐式执行所有浮点运算.只有在性能成本过高的情况下,才能使这种硬件架构以较低的精度执行浮点运算,而不是要求实现放弃性能和精度,C#允许更高精度的类型用于所有浮点运算.除了提供更精确的结果外,这几乎没有任何可衡量的影响.
由于对Ceiling的调用,这可能是"可测量的效果"之一.正如其他人所指出的那样,采用浮点数的上限将0.000000002的差异放大9个数量级,因为它将15.99999999变为16,将16.00000001变为17.在操作之前略有不同的两个数字之后大不相同; 可以通过不同的机器在其浮点运算中具有或多或少的"额外精度"这一事实来解释微小的差异.
一些相关问题:
为了解决你如何从浮点数计算纵横比的具体问题:我可能会以完全不同的方式解决这个问题.我会做一个这样的桌子:
struct Ratio
{
public int X { get; private set; }
public int Y { get; private set; }
public Ratio (int x, int y) : this()
{
this.X = x;
this.Y = y;
}
public double AsDouble() { return (double)X / (double)Y; }
}
Ratio[] commonRatios = {
new Ratio(16, 9),
new Ratio(4, 3),
// ... and so on, maybe the few hundred most common ratios here.
// since you are pinning results to be less than 20, there cannot possibly
// be more than a few hundred.
};
Run Code Online (Sandbox Code Playgroud)
现在你的实施是
public string AspectRatioAsString(double ratio)
{
var results = from commonRatio in commonRatios
select new {
Ratio = commonRatio,
Diff = Math.Abs(ratio - commonRatio.AsDouble())};
var smallestResult = results.Min(x=>x.Diff);
return String.Format("{0}:{1}", smallestResult.Ratio.X, smallestResult.Ratio.Y);
}
Run Code Online (Sandbox Code Playgroud)
请注意代码现在如何读取非常类似于您尝试执行的操作:从此常用比率列表中,选择给定比率与公共比率之间的差异最小化的那个.
我不会使用浮点数,除非我真的不得不这样做.由于舍入错误,他们太容易发生这种事情.
你能改变代码以双精度工作吗?(十进制将是矫枉过正).如果你这样做,它会给出更一致的结果吗?
至于为什么它在不同的机器上有所不同,这两台机器有什么区别?
这样的事情可能是原因.
尝试Math.Round
而不是Math.Ceiling
.如果您以16.0000001结束并向上舍入,则会错误地丢弃该答案.
杂项其他建议:
(double) 0.1
演员是不必要的.carryon
变量.修订(未经测试):
public string AspectRatioAsString(double ratio)
{
for (int height = 1; height <= 20; ++height)
{
int width = (int) Math.Round(height * ratio);
double guess = (double) width / height;
if (Math.Abs(guess - ratio) <= 0.01)
{
return width + ":" + height;
}
}
throw ArgumentException("Invalid aspect ratio", "ratio");
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
8730 次 |
最近记录: |