【问题标题】:Can "System.Math.Cos" return a (float)?“System.Math.Cos”可以返回(浮点数)吗?
【发布时间】:2010-12-03 21:35:22
【问题描述】:

在 C# 中,没有返回浮点数的“Math.Cos”函数让我很困扰。双精度值是您可以取回的唯一值,因此迫使您将其转换为浮点数。 像这样:
float val = (float)Math.Cos(someVal);

我需要使用浮点数,因为我在 Direct3D 中做一些严格使用浮点数的东西。 浮点数在图形世界中更为常见(就目前而言),因为它们是 32 位的。

在 C# 中是否有任何我可以使用的功能,可以像 C++ 那样简单地处理浮点数??

我不想包装任何 C++ 东西,因为这需要在 XNA 和 Linux 上运行 OpenGL。

注意:最好有没有将双精度转换为浮点的代码。

【问题讨论】:

  • 你可以用你自己的版本重载 math.cos() ,它只返回一个演员版本。
  • 虽然 Direct3D 需要浮点数,但在必须转换之前使用 double 会更好,因为它可以避免大多数(但不是全部)舍入错误问题。
  • @ChrisF - 游戏真的不是这样。舍入很少成为问题,或者可以通过夹紧来解决,避免 Double 的性能节省是显着的。
  • 你不能重载Math.Cos,因为你不能编辑数学类。您可以在自己的类中定义一个函数。
  • 我仍在努力克服所谓的现代语言缺乏单精度数学库的事实。

标签: c# .net math floating-point double


【解决方案1】:

从 .NET Core 2.0 (.NET Standard 2.1) (C# 8.0) 及更高版本开始这可能是内置函数。

您可以只使用带有内置常量的 MathF 类或使用 float 类型的函数。

例子:

float cos = MathF.Cos(MathF.PI);

有关更多信息,请参阅documentation on MSDN 关于MathF 类型。

【讨论】:

    【解决方案2】:

    如果不深入数学,您将无法编写自己的准确 Cos 函数。这是一个使用扩展方法的建议:

    class Program
    {
        static void Main(string[] args)
        {
            float cos = Math.Cos(.25d).ToFloat();
    
            Console.WriteLine("cos(.25d) = {0}", cos);
    
            Console.ReadKey();
        }
    }
    
    public static class MathExtensions
    {
        public static float ToFloat(this double value)
        {
            return (float)value;
        }
    }
    

    这是使用Func<T, TResult> 并创建自己的MathF 静态类的另一种方式:

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("cos(.25d) = {0}", MathF.Cos(.25d));
            Console.WriteLine("sin(.25d) = {0}", MathF.Sin(.25d));
    
            Console.ReadKey();
        }
    }
    
    public static class MathF
    {
        public static Func<double, float> Cos = angleR => (float)Math.Cos(angleR);
        public static Func<double, float> Sin = angleR => (float)Math.Sin(angleR);
    }
    

    正如其他人所指出的,Zezba 在他的测试代码中证实,Func 代表会更慢(我不知道代表会慢得多)。最快的是直接施放浮动。中间立场是 MathF 静态类中的简单静态方法调用。

    【讨论】:

    • Cos() 不是转换为单精度扩展函数的好名字。
    • 为什么你会写static Func&lt;double, float&gt; Cos = angleR =&gt; ...而不是static float Cos(double angleR) { ... }?
    • 非常感谢您提供这么棒的代码。我不知道这些方法中的任何一种,并将它们存档以备后用。如果你知道任何方式你不必强制转换那会很酷,但我怀疑没有包装 c++。
    • 我不喜欢你对MathF 的实现。定义MathF 类的想法很好。但是在这里使用代表是愚蠢的(你的甚至不是只读字段)。代表在这里只是丢掉了性能并使代码更难理解,因为例如文档会将它们列为静态字段而不是静态方法。而且我认为函数应该采用浮点而不是双精度。 float Cos(float angle){return (float)Math.Cos(angle);}
    【解决方案3】:

    好的,所以我运行了一些基准测试,看看在阅读“dboarman”回复后哪种方法最快。遗憾的是,如果不使用严格的 c# 进行投射,就无法做到这一点,最快的方法是现场投射,所以因为我关心速度,因为它主要用于游戏,我会坚持使用旧的投射方法。

    这些测试是使用以下规范编译的::

    C# .NET 4.0
    ConsoleApplication - Release - Optimized code - x64
    4gb ram, 2.4ghz AMD_X2_DualCore 4600 CPU, running Windows7 Ultimate.
    

    代码:

    static void Main(string[] args)
    {
        //Start
        Console.Write("Hit Enter to Start\n");
        Console.ReadLine();
        long num = 100;
        long mil = 0;
        float val = 0.01f;
        Stopwatch startTime = new Stopwatch();
    
        //Run
        for(long i = 0; i != num; ++i)
        {
            startTime.Restart();
            for(uint i2 = 0; i2 != 1000000; ++i2) val = (float)System.Math.Cos(val);// 48 Milliseconds
            //for(uint i2 = 0; i2 != 1000000; ++i2) val = System.Math.Cos(val).ToFloat();// 53 Milliseconds
            //for(uint i2 = 0; i2 != 1000000; ++i2) val = MathF2.Cos(val);// 59 Milliseconds
            //for(uint i2 = 0; i2 != 1000000; ++i2) val = MathF.Cos(val);// 63 Milliseconds
            startTime.Stop();
            mil += startTime.ElapsedMilliseconds;
        }
    
        //End
        mil /= num;
    
        //Print
        Console.Write("Milliseconds = "+mil.ToString());
        Console.ReadLine();
    }
    

    这是测试的基本数学代码::

    public static class MathF
    {
        public static Func<double, float> Cos = angleR => (float)System.Math.Cos(angleR);
        public static Func<double, float> Sin = angleR => (float)System.Math.Sin(angleR);
    }
    
    public static class MathF2
    {
        public static float Cos(float pValue) {return (float)System.Math.Cos(pValue);}
    }
    
    public static class MathExtensions
    {
        public static float ToFloat(this double value)
        {
            return (float)value;
        }
    }
    

    【讨论】:

    • 所以如果我正确阅读了您的测试代码,使用标准投射方法完成 100 万次投射的时间是 48 毫秒?在测试中做得很好。老实说,我没有意识到 Func 代表会慢多少。
    • 是的,你是对的,100 万次投射将在 48 毫秒内完成。我重复测试 100 次,然后取平均值并打印结果。
    【解决方案4】:

    我将创建一个 MathCommonMethods 类,其中包含将所有常用返回类型转换为双精度浮点数的方法。从长远来看,这将为您节省一些打字时间。

    至于有一个以浮点数形式返回值的现有函数,我还没有听说过。

    如果精度对您的应用程序很重要,您在转换时也应该小心不要失去精度。

    【讨论】:

    • “重载”是指在一个类中定义一组同名的方法。如果您正在谈论将方法添加到不同的类,那不是重载。它只是定义一个方法。
    • 我的意思是派生类,据我了解,这仍然被认为是重载。不过很好,我最初的答案并不清楚。
    • System.Math 是一个静态类。你不能从中得到。
    • 在这种情况下,过载不是正确的术语。 :) 再次感谢。
    • @x0n 如何使用扩展方法添加静态方法?我认为扩展方法只能添加实例方法。
    【解决方案5】:

    如果你想避免强制转换,你需要一个不同的实现。如果你在做跨平台,你可以创建一个平台抽象层,你可以根据平台放置不同的实现。这将有助于提高性能。如果性能不是问题(游戏曾经是这种情况),那么创建一个实用函数并执行转换是一个很好的解决方案。

    【讨论】:

    • 我曾想过这样做,这是个好主意。但我不想让事情变得过于复杂。虽然我想我可以尝试一下,看看我从中获得了哪些速度改进。对 PC、Mac 和 Linux 会很好,但不适用于 XBOX 或 Phone7 上的 XNA。
    猜你喜欢
    • 2014-02-19
    • 1970-01-01
    • 2018-06-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-20
    • 1970-01-01
    相关资源
    最近更新 更多