首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何在C#中进行位移位乘法

如何在C#中进行位移位乘法
EN

Stack Overflow用户
提问于 2012-12-24 18:13:18
回答 2查看 3.3K关注 0票数 1

我知道在不动点运算中使用的位移位乘法,例如,如果我需要乘两个浮点数,我应该按比例因子乘以它(例如,在这种情况下,20),然后,我应该把结果值乘以整数值,然后我应该将它们返回到数字的正常表示,应该再次除以比例因子,如何用位移位运算来实现它呢?

基于的5.4定点算法

下面我尝试了这个代码示例,我希望结果floatResShiftfloatResNormal是相同的,但是它们是不同的,我所做的是错误的吗?:

代码语言:javascript
运行
复制
        float mul1 = 18.579434f;
        float mul2 = 34.307951f;

        int shiftMul1 = (int)((2 ^ 32) * mul1);
        int shiftMul2 = (int)((2 ^ 32) * mul2);

        var resultMul = shiftMul1 * shiftMul2;
        float floatResShift = resultMul >> 32; // wrong value
        float floatResNormal = mul1 * mul2; //expected value

更新:

定点算术解释:

当A= 2.5和B= 8.4时,使用fi连接点算法计算A·B的结果时,使用32位整数将涉及以下操作: 决定一个缩放因子。这在很大程度上取决于可能会看到什么样的数字。由于这个例子中的数字是如此之低,所以它就不那么重要了,并且16个小数位(基数右边的位)是可以接受的。然后,缩放因子为f= 216 = 65536。这种格式称为Q15.16 (基点左侧15位,右侧16位,符号1位)。 使用普通整数乘法将Ai和Bi相乘。Ri = Ai·Bi = 163840·550502 = 90194247680。之所以会出现如此大的数字,是因为Ai和Bi都被刻度成我们的Q15.16格式,因此乘法产生的数字实质上是(A·f)·(B·f) =A·B·f2。 为了使我们的结果返回到Q15.16格式,结果必须除以缩放因子。这也可以使用位移位算法,但为了简单起见,这里使用了除法。Ri/f = 90194247680/65536 = 1376255,这是我们以Q15.16格式得出的结果 要将数字转换回正常实数,只需将其转换为所需的格式,然后再用缩放因子除以,因此: 1376255.0/65536.0 = 20.999985,接近预期的数字21。 具有缩放因子的标度数。在二进制算法中,这可以通过位移位来实现,但为了简单起见,我们将使用比例因子乘法。Ai = A·f = 2.5·65536 = 163840,B·f= 8.4·65536 = 550502.4,然后将其截断为整数,这样Bi =550502。 要将数字转换回正常实数,只需将其转换为所需的格式,然后再用缩放因子除以,因此: 1376255.0/65536.0 = 20.999985,接近预期的数字21。

如何在上面的注释中做同样的修改,但是有位移位。在点之后有大的浮点值

我试过上面的代码,但没有运气。

例如,我需要乘两个值18.579434f和34.307951f,但需要使用不动点算法。

更新:

我试过的比例系数较小,但没有运气。

解决方案:

也许我没有清楚地解释这个问题,但我解决了这个问题,我找到了一个解决办法:

谢谢大家,问题是封闭的,这里是带定点乘法的完整代码:

代码语言:javascript
运行
复制
    float mul1 = 18.579434f;
    float mul2 = 34.307951f;

    int scaleFactor = (int) Math.Pow(2, 20);

    long shiftMul1 = (int)((scaleFactor) * mul1);
    long shiftMul2 = (int)((scaleFactor) * mul2);

    var resultMul = shiftMul1 * shiftMul2;
    float floatResShift = resultMul >> 40; 
    float floatResNormal = mul1 * mul2; // the result floatResNormal almost same as floatResShift
EN

回答 2

Stack Overflow用户

发布于 2012-12-24 19:32:21

代码语言:javascript
运行
复制
var k = 20;
var k_2 = k/2;
var p = 1 << k;

float mul1 = 18.579434f;
float mul2 = 34.307951f;

int shiftMul1 = (int)(p * mul1);
int shiftMul2 = (int)(p * mul2);

//fixed point multiplication         
var resultMul = ((shiftMul1 >> k_2) * (shiftMul2 >> k_2));

float floatResShift = ((float)resultMul)/p;
float floatResNormal = mul1 * mul2; 

Console.WriteLine("{0} {1}", floatResNormal, floatResShift);

输出:

代码语言:javascript
运行
复制
637,4223 637,4043
票数 1
EN

Stack Overflow用户

发布于 2012-12-24 18:49:35

检查这段代码

代码语言:javascript
运行
复制
/*
Fixed Point Arithmatic structure and relevant methods. Simple fixed point structure included as well.

Created from information and code gathered here: http://stackoverflow.com/questions/605124/fixed-point-math-in-c

May be used for anything without permission.

To quote the original author (x4000 of stackoverflow.com):
"The accuracy of these functions as they are coded here is more than enough for my purposes, but if you need more you can increase the SHIFT AMOUNT on FInt.
Just be aware that if you do so, the constants on [trigonomic] functions will then need to be divided by 4096 and then multiplied by whatever your new SHIFT AMOUNT requires.
You're likely to run into some bugs if you do that and aren't careful, so be sure to run checks against the built-in Math functions to make sure that your results aren't
being put off by incorrectly adjusting a constant."

Code credit: x4000 of stackoverflow.com

Compiled into a usable source file by: Paul Bergeron

Date: 7/1/2009

More fixed point functions can be found written in Java here: http://home.comcast.net/~ohommes/MathFP/

*/

public struct FInt
{
    public long RawValue;
    public const int SHIFT_AMOUNT = 12; //12 is 4096

    public const long One = 1 << SHIFT_AMOUNT;
    public const int OneI = 1 << SHIFT_AMOUNT;
    public static FInt OneF = new FInt( 1, true );

    #region Constructors
    public FInt( long StartingRawValue, bool UseMultiple )
    {
        this.RawValue = StartingRawValue;
        if ( UseMultiple )
            this.RawValue = this.RawValue << SHIFT_AMOUNT;
    }
    public FInt( double DoubleValue )
    {
        DoubleValue *= (double)One;
        this.RawValue = (int)Math.Round( DoubleValue );
    }
    #endregion

    public int IntValue
    {
        get { return (int)( this.RawValue >> SHIFT_AMOUNT ); }
    }

    public int ToInt()
    {
        return (int)( this.RawValue >> SHIFT_AMOUNT );
    }

    public double ToDouble()
    {
        return (double)this.RawValue / (double)One;
    }

    public FInt Inverse
    {
        get { return new FInt( -this.RawValue, false ); }
    }

    #region FromParts
    /// <summary>
    /// Create a fixed-int number from parts.  For example, to create 1.5 pass in 1 and 500.
    /// </summary>
    /// <param name="PreDecimal">The number above the decimal.  For 1.5, this would be 1.</param>
    /// <param name="PostDecimal">The number below the decimal, to three digits.
    /// For 1.5, this would be 500. For 1.005, this would be 5.</param>
    /// <returns>A fixed-int representation of the number parts</returns>
    public static FInt FromParts( int PreDecimal, int PostDecimal )
    {
        FInt f = new FInt( PreDecimal );
        if ( PostDecimal != 0 )
            f.RawValue += ( new FInt( PostDecimal ) / 1000 ).RawValue;

        return f;
    }
    #endregion

    #region *
    public static FInt operator *( FInt one, FInt other )
    {
        return new FInt( ( one.RawValue * other.RawValue ) >> SHIFT_AMOUNT, false );
    }

    public static FInt operator *( FInt one, int multi )
    {
        return one * (FInt)multi;
    }

    public static FInt operator *( int multi, FInt one )
    {
        return one * (FInt)multi;
    }
    #endregion

    #region /
    public static FInt operator /( FInt one, FInt other )
    {
        return new FInt( ( one.RawValue << SHIFT_AMOUNT ) / ( other.RawValue  ), false );
    }

    public static FInt operator /( FInt one, int divisor )
    {
        return one / (FInt)divisor;
    }

    public static FInt operator /( int divisor, FInt one )
    {
        return (FInt)divisor / one;
    }
    #endregion

    #region %
    public static FInt operator %( FInt one, FInt other )
    {
        return new FInt( ( one.RawValue ) % ( other.RawValue ), false );
    }

    public static FInt operator %( FInt one, int divisor )
    {
        return one % (FInt)divisor;
    }

    public static FInt operator %( int divisor, FInt one )
    {
        return (FInt)divisor % one;
    }
    #endregion

    #region +
    public static FInt operator +( FInt one, FInt other )
    {
        return new FInt( one.RawValue + other.RawValue, false );
    }

    public static FInt operator +( FInt one, int other )
    {
        return one + (FInt)other;
    }

    public static FInt operator +( int other, FInt one )
    {
        return one + (FInt)other;
    }
    #endregion

    #region -
    public static FInt operator -( FInt one, FInt other )
    {
        return new FInt( one.RawValue - other.RawValue, false );
    }

    public static FInt operator -( FInt one, int other )
    {
        return one - (FInt)other;
    }

    public static FInt operator -( int other, FInt one )
    {
        return (FInt)other - one;
    }
    #endregion

    #region ==
    public static bool operator ==( FInt one, FInt other )
    {
        return one.RawValue == other.RawValue;
    }

    public static bool operator ==( FInt one, int other )
    {
        return one == (FInt)other;
    }

    public static bool operator ==( int other, FInt one )
    {
        return (FInt)other == one;
    }
    #endregion

    #region !=
    public static bool operator !=( FInt one, FInt other )
    {
        return one.RawValue != other.RawValue;
    }

    public static bool operator !=( FInt one, int other )
    {
        return one != (FInt)other;
    }

    public static bool operator !=( int other, FInt one )
    {
        return (FInt)other != one;
    }
    #endregion

    #region >=
    public static bool operator >=( FInt one, FInt other )
    {
        return one.RawValue >= other.RawValue;
    }

    public static bool operator >=( FInt one, int other )
    {
        return one >= (FInt)other;
    }

    public static bool operator >=( int other, FInt one )
    {
        return (FInt)other >= one;
    }
    #endregion

    #region <=
    public static bool operator <=( FInt one, FInt other )
    {
        return one.RawValue <= other.RawValue;
    }

    public static bool operator <=( FInt one, int other )
    {
        return one <= (FInt)other;
    }

    public static bool operator <=( int other, FInt one )
    {
        return (FInt)other <= one;
    }
    #endregion

    #region >
    public static bool operator >( FInt one, FInt other )
    {
        return one.RawValue > other.RawValue;
    }

    public static bool operator >( FInt one, int other )
    {
        return one > (FInt)other;
    }

    public static bool operator >( int other, FInt one )
    {
        return (FInt)other > one;
    }
    #endregion

    #region <
    public static bool operator <( FInt one, FInt other )
    {
        return one.RawValue < other.RawValue;
    }

    public static bool operator <( FInt one, int other )
    {
        return one < (FInt)other;
    }

    public static bool operator <( int other, FInt one )
    {
        return (FInt)other < one;
    }
    #endregion

    public static explicit operator int( FInt src )
    {
        return (int)( src.RawValue >> SHIFT_AMOUNT );
    }

    public static explicit operator FInt( int src )
    {
        return new FInt( src, true );
    }

    public static explicit operator FInt( long src )
    {
        return new FInt( src, true );
    }

    public static explicit operator FInt( ulong src )
    {
        return new FInt( (long)src, true );
    }

    public static FInt operator <<( FInt one, int Amount )
    {
        return new FInt( one.RawValue << Amount, false );
    }

    public static FInt operator >>( FInt one, int Amount )
    {
        return new FInt( one.RawValue >> Amount, false );
    }

    public override bool Equals( object obj )
    {
        if ( obj is FInt )
            return ( (FInt)obj ).RawValue == this.RawValue;
        else
            return false;
    }

    public override int GetHashCode()
    {
        return RawValue.GetHashCode();
    }

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

    #region PI, DoublePI
    public static FInt PI = new FInt( 12868, false ); //PI x 2^12
    public static FInt TwoPIF = PI * 2; //radian equivalent of 260 degrees
    public static FInt PIOver180F = PI / (FInt)180; //PI / 180
    #endregion

    #region Sqrt
    public static FInt Sqrt( FInt f, int NumberOfIterations )
    {
        if ( f.RawValue < 0 ) //NaN in Math.Sqrt
            throw new ArithmeticException( "Input Error" );
        if ( f.RawValue == 0 )
            return (FInt)0;
        FInt k = f + FInt.OneF >> 1;
        for ( int i = 0; i < NumberOfIterations; i++ )
            k = ( k + ( f / k ) ) >> 1;

        if ( k.RawValue < 0 )
            throw new ArithmeticException( "Overflow" );
        else
            return k;
    }

    public static FInt Sqrt( FInt f )
    {
        byte numberOfIterations = 8;
        if ( f.RawValue > 0x64000 )
            numberOfIterations = 12;
        if ( f.RawValue > 0x3e8000 )
            numberOfIterations = 16;
        return Sqrt( f, numberOfIterations );
    }
    #endregion

    #region Sin
    public static FInt Sin( FInt i )
    {
        FInt j = (FInt)0;
        for ( ; i < 0; i += new FInt( 25736, false ) ) ;
        if ( i > new FInt( 25736, false ) )
            i %= new FInt( 25736, false );
        FInt k = ( i * new FInt( 10, false ) ) / new FInt( 714, false );
        if ( i != 0 && i != new FInt( 6434, false ) && i != new FInt( 12868, false ) &&
            i != new FInt( 19302, false ) && i != new FInt( 25736, false ) )
            j = ( i * new FInt( 100, false ) ) / new FInt( 714, false ) - k * new FInt( 10, false );
        if ( k <= new FInt( 90, false ) )
            return sin_lookup( k, j );
        if ( k <= new FInt( 180, false ) )
            return sin_lookup( new FInt( 180, false ) - k, j );
        if ( k <= new FInt( 270, false ) )
            return sin_lookup( k - new FInt( 180, false ), j ).Inverse;
        else
            return sin_lookup( new FInt( 360, false ) - k, j ).Inverse;
    }

    private static FInt sin_lookup( FInt i, FInt j )
    {
        if ( j > 0 && j < new FInt( 10, false ) && i < new FInt( 90, false ) )
            return new FInt( SIN_TABLE[i.RawValue], false ) +
                ( ( new FInt( SIN_TABLE[i.RawValue + 1], false ) - new FInt( SIN_TABLE[i.RawValue], false ) ) /
                new FInt( 10, false ) ) * j;
        else
            return new FInt( SIN_TABLE[i.RawValue], false );
    }

    private static int[] SIN_TABLE = {
        0, 71, 142, 214, 285, 357, 428, 499, 570, 641,
        711, 781, 851, 921, 990, 1060, 1128, 1197, 1265, 1333,
        1400, 1468, 1534, 1600, 1665, 1730, 1795, 1859, 1922, 1985,
        2048, 2109, 2170, 2230, 2290, 2349, 2407, 2464, 2521, 2577,
        2632, 2686, 2740, 2793, 2845, 2896, 2946, 2995, 3043, 3091,
        3137, 3183, 3227, 3271, 3313, 3355, 3395, 3434, 3473, 3510,
        3547, 3582, 3616, 3649, 3681, 3712, 3741, 3770, 3797, 3823,
        3849, 3872, 3895, 3917, 3937, 3956, 3974, 3991, 4006, 4020,
        4033, 4045, 4056, 4065, 4073, 4080, 4086, 4090, 4093, 4095,
        4096
    };
    #endregion

    private static FInt mul( FInt F1, FInt F2 )
    {
        return F1 * F2;
    }

    #region Cos, Tan, Asin
    public static FInt Cos( FInt i )
    {
        return Sin( i + new FInt( 6435, false ) );
    }

    public static FInt Tan( FInt i )
    {
        return Sin( i ) / Cos( i );
    }

    public static FInt Asin( FInt F )
    {
        bool isNegative = F < 0;
        F = Abs( F );

        if ( F > FInt.OneF )
            throw new ArithmeticException( "Bad Asin Input:" + F.ToDouble() );

        FInt f1 = mul( mul( mul( mul( new FInt( 145103 >> FInt.SHIFT_AMOUNT, false ), F ) -
            new FInt( 599880 >> FInt.SHIFT_AMOUNT, false ), F ) +
            new FInt( 1420468 >> FInt.SHIFT_AMOUNT, false ), F ) -
            new FInt( 3592413 >> FInt.SHIFT_AMOUNT, false ), F ) +
            new FInt( 26353447 >> FInt.SHIFT_AMOUNT, false );
        FInt f2 = PI / new FInt( 2, true ) - ( Sqrt( FInt.OneF - F ) * f1 );

        return isNegative ? f2.Inverse : f2;
    }
    #endregion

    #region ATan, ATan2
    public static FInt Atan( FInt F )
    {
        return Asin( F / Sqrt( FInt.OneF + ( F * F ) ) );
    }

    public static FInt Atan2( FInt F1, FInt F2 )
    {
        if ( F2.RawValue == 0 && F1.RawValue == 0 )
            return (FInt)0;

        FInt result = (FInt)0;
        if ( F2 > 0 )
            result = Atan( F1 / F2 );
        else if ( F2 < 0 )
        {
            if ( F1 >= 0 )
                result = ( PI - Atan( Abs( F1 / F2 ) ) );
            else
                result = ( PI - Atan( Abs( F1 / F2 ) ) ).Inverse;
        }
        else
            result = ( F1 >= 0 ? PI : PI.Inverse ) / new FInt( 2, true );

        return result;
    }
    #endregion

    #region Abs
    public static FInt Abs( FInt F )
    {
        if ( F < 0 )
            return F.Inverse;
        else
            return F;
    }
    #endregion

}

public struct FPoint
{
    public FInt X;
    public FInt Y;

    public FPoint( FInt X, FInt Y )
    {
        this.X = X;
        this.Y = Y;
    }

    public static FPoint FromPoint( Point p )
    {
        FPoint f = new FPoint();
        f.X = (FInt)p.X;
        f.Y = (FInt)p.Y;
        return f;
    }

    public static Point ToPoint( FPoint f )
    {
        return new Point( f.X.IntValue, f.Y.IntValue );
    }
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/14024472

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档