【问题标题】:How to Calculate the Square Root of a Float in C#如何在 C# 中计算浮点数的平方根
【发布时间】:2010-12-03 21:09:42
【问题描述】:

如何计算C#Float 的平方根,类似于XNA 中的Core.Sqrt

【问题讨论】:

  • 使用强大的魔法 - 0x5f3759df
  • 魔法就是平方根的倒数。但类似的魔法也存在于 sqrt。这会失去精度。
  • @CodeInChaos - 文章中的第二个代码示例有一个 sqrt 的实现:"请注意,唯一真正的区别在于返回值 - 而不是返回 y,而是返回数字y 作为平方根"*
  • 是的。 jball 发布的内容主要是出于一种很酷的好奇心,并且只有在性能比精度更重要的情况下才有用。首先,我会使用简单的内置东西,只有在性能确实需要时才切换到复杂的解决方案,并且分析表明更改确实很重要。
  • @CodeInChaos 是绝对正确的(因此我的 "强大的魔法" 声明,而不是发布它作为答案)。除非您遇到实际的性能问题,否则始终为可读性、可维护性和准确性编写代码(例如 (float)Math.Sqrt(inputFloat)

标签: c# math square-root


【解决方案1】:

double 计算它,然后转换回浮点数。可能有点慢,但应该可以。

(float)Math.Sqrt(inputFloat)

【讨论】:

  • 我一直希望 .Net 能以某种方式将其优化为幕后的全浮点(全 32 位)操作。有谁知道这是否得到优化?
  • @Chris,精度将与输入相同。计算是使用双精度数完成的。
  • @Chris:不,有一个著名的浮点分析定理保证这会给你正确的结果。
  • 由于 double 的精度比 float 高得多,因此损失将非常小或不存在。通过使用浮点数,您已经说过您不太关心精度。
  • @CodeInChaos:请注意,精度损失甚至不是“非常小”。假设转换和双精度平方根是正确舍入的,这将为所有可能的输入提供一个正确舍入的单精度平方根。
【解决方案2】:

不想这么说,但 0x5f3759df 似乎需要 Math.Sqrt 的 3 倍。我只是用计时器做了一些测试。 访问预先计算的数组的 for 循环中的 Math.Sqrt 大约需要 80 毫秒。 0x5f3759df 相同情况下导致180+ms

使用发布模式优化进行了多次测试。

来源如下:

/*
    ================
    SquareRootFloat
    ================
    */
    unsafe static void SquareRootFloat(ref float number, out float result)
    {
        long i;
        float x, y;
        const float f = 1.5F;

        x = number * 0.5F;
        y = number;
        i = *(long*)&y;
        i = 0x5f3759df - (i >> 1);
        y = *(float*)&i;
        y = y * (f - (x * y * y));
        y = y * (f - (x * y * y));
        result = number * y;
    }

    /*
    ================
    SquareRootFloat
    ================
    */
    unsafe static float SquareRootFloat(float number)
    {
        long i;
        float x, y;
        const float f = 1.5F;

        x = number * 0.5F;
        y = number;
        i = *(long*)&y;
        i = 0x5f3759df - (i >> 1);
        y = *(float*)&i;
        y = y * (f - (x * y * y));
        y = y * (f - (x * y * y));
        return number * y;
    }

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        int Cycles = 10000000;
        Random rnd = new Random();
        float[] Values = new float[Cycles];
        for (int i = 0; i < Cycles; i++)
            Values[i] = (float)(rnd.NextDouble() * 10000.0);

        TimeSpan SqrtTime;

        float[] Results = new float[Cycles];

        DateTime Start = DateTime.Now;

        for (int i = 0; i < Cycles; i++)
        {
            SquareRootFloat(ref Values[i], out Results[i]);
            //Results[i] = (float)Math.Sqrt((float)Values[i]);
            //Results[i] = SquareRootFloat(Values[i]);
        }

        DateTime End = DateTime.Now;

        SqrtTime = End - Start;

        Console.WriteLine("Sqrt was " + SqrtTime.TotalMilliseconds.ToString() + " long");
        Console.ReadKey();
    }
}

【讨论】:

【解决方案3】:
var result = Math.Sqrt((double)value);

【讨论】:

  • 浮点和双精度计算不同吗?
  • @Chris - Math.Sqrt 方法采用双精度并返回双精度。这就是我将参数转换为双精度的原因。
  • 我明白了。但我说的是花车。还是谢谢
【解决方案4】:
private double operand1;  

private void squareRoot_Click(object sender, EventArgs e)
{ 
    operand1 = Math.Sqrt(operand1);
    this.textBox1.Text = operand1.ToString();
}

【讨论】:

  • 欢迎来到 Stack Overflow!尽管此答案可能是正确且有用的,但如果您 include some explanation along with it 解释它如何帮助解决问题,则最好。如果有更改(可能不相关)导致它停止工作并且用户需要了解它曾经是如何工作的,这在未来变得特别有用。
猜你喜欢
  • 1970-01-01
  • 2023-03-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多