我有一个简单的例程,该例程根据浮点值计算纵横比。因此,对于值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; }
现在在另一台机器上,我得到了完全不同的结果。因此,在我的计算机上,1.77777779给出了“ 16:9”,但是在另一台计算机上,我得到了“ 38:21”。
这是来自4.1.6节的一些有趣的C#规范:
浮点运算可以比运算的结果类型更高的精度执行。例如,某些硬件体系结构支持比双精度类型具有更大范围和精度的“扩展”或“长双精度”浮点类型,并使用此更高精度类型隐式执行所有浮点运算。只有付出过高的性能代价,才能使此类硬件体系结构以较低的精度执行浮点运算,而不是要求实现同时牺牲性能和精度,而C#允许将高精度类型用于所有浮点运算。 。除了提供更精确的结果外,这几乎没有任何可测量的效果。
由于对吊顶的调用,这可能是“可测量的效果”之一。正如其他人所指出的那样,采用浮点数的上限会将0.000000002的差异放大九个数量级,因为它将15.99999999转换为16,将16.00000001转换为17。两个在操作前稍有不同的数字其后相差很大。微小的差异可能是由于不同的机器在其浮点运算中可能具有或多或少的“额外精度”这一事实造成的。
一些相关的问题:
C#XNA Visual Studio:“发布”和“调试”模式之间的区别?
CLR JIT优化违反因果关系吗?
为了解决您的特定问题,即如何从float中计算宽高比:我可能会以完全不同的方式解决此问题。我会做一个这样的表:
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. };
现在您的实现是
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); }
请注意,代码现在的读取方式非常类似于您要执行的操作:从此常用比率列表中,选择一个将给定比率与常用比率之间的差异最小化的比率。