【问题标题】:How do I do fixed-point division?如何进行定点除法?
【发布时间】:2012-10-13 19:29:01
【问题描述】:

我有一个使用 long 的 8 字节定点数,其恒定分母为 (1

谢谢。

public struct Fixed8
{
    private long _numerator;
    public const long DENOMINATOR = 1 << 24;
}

【问题讨论】:

  • 您的意思是要计算a / b,其中ab 是两个Fixed8 值?
  • 抱歉,我认为这很清楚。是的。
  • 我脑后有个隐隐的想法,如果我使用 M32.N32,计算会更容易。虽然这可能需要四个单独的计算。

标签: c# fixed-point


【解决方案1】:

您可以使用BigInteger Structure 来执行计算:

public static Fixed8 operator /(Fixed8 a, Fixed8 b)
{
    Fixed8 result;
    result._numerator = (long)( new BigInteger(a._numerator) *
                                new BigInteger(DENOMINATOR)  /
                                new BigInteger(b._numerator) );
    return result;
}

完整代码:

public const long DENOMINATOR = 1 << 24;

private long _numerator;

public Fixed8(double value)
{
    _numerator = (long)(value * DENOMINATOR);
}

public static Fixed8 operator /(Fixed8 a, Fixed8 b)
{
    Fixed8 result;
    result._numerator = (long)( new BigInteger(a._numerator) * 
                                new BigInteger(DENOMINATOR)  / 
                                new BigInteger(b._numerator) );
    return result;
}

public static explicit operator double(Fixed8 a)
{
    return (double)a._numerator / (double)DENOMINATOR;
}

public override string ToString()
{
    return ((double)this).ToString();
}

例子:

var a = new Fixed8(7);
var b = new Fixed8(1.7);

Console.WriteLine(a);
Console.WriteLine(b);
Console.WriteLine(a / b);

输出:

7 1.69999998807907 4.11764705181122

【讨论】:

  • 虽然这是可能的,但 BigInteger 本质上是缓慢的。我希望快速除法。
  • 除非有一些数学技巧可以在 64 位中执行除法,否则您几乎会被 BigInteger 或您自己实现的更大数据类型所困。也许在math.stackexchange.com 询问?
  • 可能有帮助;这个问题可能会因为是一个编程问题而被关闭。
  • 当然,您必须重新表述一下这个问题,因为您的实际问题与编程没有直接关系。本质上,您想要计算(((i * j) modulo 2^64) / k) modulo 2^64 并获得与(i * j) / k 相同的结果。最好先把标志分开。
  • 不会 ( i * j ) / k 也必须是 ( ( i * j ) / k ) 模 2^64?并且说“只有积极的价值观”会起作用吗?
【解决方案2】:

这是一种将所有计算保存在longs 中的方法。不过,它可能不会更快。我没量过。

public struct Fixed8
{
    public Fixed8(double value)
    {
        _numerator = (long)(value * DENOMINATOR);
    }

    private long _numerator;
    public const long DENOMINATOR = 1 << 24;

    public static Fixed8 operator /(Fixed8 a, Fixed8 b)
    {
        long remainder;
        long quotient = Math.DivRem(a._numerator, b._numerator, out remainder) * DENOMINATOR;

        long morePrecision = remainder * DENOMINATOR / b._numerator;

        return new Fixed8 { _numerator = quotient + morePrecision };
    }
}

【讨论】:

    【解决方案3】:
    public long Division()
    {
        long result = _numerator / DENOMINATOR;
        return result;
    }
    

    【讨论】:

    • 如果您不知道它们是什么,请在回答问题之前查找定点数。它们不是分数。
    【解决方案4】:

    使用System.Numerics.BigInteger 很好。但在这种特定情况下,System.Decimal 实际上具有足够的精度。所以这是我的建议:

     public static Fixed8 operator /(Fixed8 a, Fixed8 b)
     {
       decimal resultNumerator = (decimal)a._numerator * DENOMINATOR / b._numerator;
       return new Fixed8 { _numerator = Convert.ToInt64(resultNumerator) };
     }
    

    【讨论】:

    • 目前正在使用这个;不过,可能不是最有效的。
    • @NarftheMouse 是什么让您认为它不是“最有效的”?你需要什么效率?您的应用程序是否将所有资源都用于划分Fixed8
    • 对不起;应该已经澄清了。这是我分配给自己的练习。加快速度是练习的一部分。因为我从来没有参加过正式的课程(除了一些互联网教程网站),所以这样的事情很重要。
    猜你喜欢
    • 2020-02-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-16
    • 2014-01-10
    • 1970-01-01
    相关资源
    最近更新 更多