如何在C#中计算浮点数的平方根

11

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


1
使用强大的魔法 - 0x5f3759df - jball
那个神奇的东西是倒数平方根。但是对于sqrt也存在类似的魔法。而且这会失去精度。 - CodesInChaos
@CodeInChaos - 文章中的第二个代码示例实现了sqrt函数:“请注意,唯一的真正区别在于返回值-不要返回y,而是返回number*y作为平方根”。 - jball
1
主要是针对性能比精度更加重要的情况,jball发表了一篇很有趣的帖子,但这只是一个酷炫的好奇心。首先,我会使用简单内置的解决方案,只有在性能真正需要时,而且分析结果表明更改确实有意义的情况下,才转向复杂的解决方案。 - CodesInChaos
1
@CodeInChaos 是绝对正确的(这也是我所说的“强大的魔法”的原因,而不是将其发布为答案)。始终编写易读、易维护和准确的代码(例如(float)Math.Sqrt(inputFloat)),除非您真正遇到性能问题。 - jball
显示剩余2条评论
4个回答

19

自 .net core 2.0 起,您可以使用 MathF.Sqrt

在旧版本中,您可以计算双精度浮点类型的平方根,然后强制转换回浮点型。这可能会有点慢,但应该可以工作。

(float)Math.Sqrt(inputFloat)

1
我一直希望 .Net 在幕后进行全浮点数 (32 位) 操作的优化。有人知道这是否得到优化了吗? - Detmar
3
@Chris:不,有一个众所周知的浮点数分析定理保证这将为您提供正确的结果。 - Stephen Canon
2
由于双精度比“浮点数”具有更高的精度,因此损失将非常小或不存在。使用浮点数已经表明您不太关心精度。 - CodesInChaos
4
请注意,精度损失甚至不是“非常小”。假设转换和双精度平方根是正确舍入的,则对于所有可能的输入,这将提供一个正确舍入的单精度平方根。 - Stephen Canon
或者更好的方法是使用 MathF.Sqrt(inputFloat)。 - Paul Childs
显示剩余2条评论

6
抱歉要说这个,但0x5f3759df似乎比Math.Sqrt慢3倍。我用计时器进行了一些测试。 在for循环中访问预先计算的数组的Math.Sqrt结果约为80毫秒。 相同情况下的0x5f3759df结果为180+毫秒。
该测试使用了发布模式优化,并进行了多次测试。
以下是源代码:
/*
    ================
    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
说实话,这似乎有些离题,但仍然很有趣! - Tara
1
可以在C语言中编写类似Quake的快速invsqrt函数吗? - Stan Prokop

0
var result = Math.Sqrt((double)value);

1
浮点数和双精度数计算不同,对吧? - Chris
2
@Chris - Math.Sqrt方法接受一个double类型的参数并返回一个double类型的值。这就是为什么我将参数强制转换为double类型的原因。 - Randy Minder
我明白你的意思。但是我在谈论浮点数。无论如何还是谢谢。 - Chris

-5
private double operand1;  

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

2
欢迎来到Stack Overflow!虽然这个答案可能是正确和有用的,但最好还是附上一些解释,以解释它如何帮助解决问题。如果将来发生了变化(可能与此无关),导致它停止工作,用户需要了解它曾经是如何工作的,这时解释就尤为重要了。 - Kevin Brown-Silva

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接