为了账号安全,请及时绑定邮箱和手机立即绑定

为什么此浮点计算在不同的计算机上给出不同的结果?

/ 猿问

为什么此浮点计算在不同的计算机上给出不同的结果?

C#
慕慕森 2019-10-21 15:20:15

我有一个简单的例程,该例程根据浮点值计算纵横比。因此,对于值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”。


查看完整描述

3 回答

?
繁花如伊

这是来自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);

}

请注意,代码现在的读取方式非常类似于您要执行的操作:从此常用比率列表中,选择一个将给定比率与常用比率之间的差异最小化的比率。


查看完整回答
反对 回复 2019-10-21
?
侃侃尔雅

除非确实需要,否则我不会使用浮点数。由于舍入错误,他们太容易出现这种情况。

您可以更改代码以双精度工作吗?(小数点会导致过大的杀伤力)。如果这样做,是否会给出更一致的结果?

至于为什么在不同的计算机上有所不同,那么两台计算机之间有什么区别?

  • 32位和64位?

  • Windows 7 vs Vista vs XP?

  • 英特尔与AMD处理器?(感谢Oded)

这样的事情可能是原因。


查看完整回答
反对 回复 2019-10-21
?
侃侃无极

尝试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");

}


查看完整回答
反对 回复 2019-10-21

添加回答

回复

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信