Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何使扩展范围浮点乘法更有效?_C#_Performance_Floating Point - Fatal编程技术网

C# 如何使扩展范围浮点乘法更有效?

C# 如何使扩展范围浮点乘法更有效?,c#,performance,floating-point,C#,Performance,Floating Point,我正在做一个计算,它经常涉及3.47493E+17298这样的值。这远远超出了双精度运算所能处理的范围,我不需要额外的精度,只需要额外的指数范围,所以我用C#创建了自己的小结构 我的结构使用long表示有效位和符号,int表示指数,因此我实际上有: 1符号位 32个指数位(常规2的补码指数) 63位有效位 我很好奇能采取什么措施使我的乘法程序更有效率。我对这些扩展范围值进行了大量的乘法运算,运算速度非常快,但我一直在寻找加快运算速度的提示 我的乘法程序: public static Bi

我正在做一个计算,它经常涉及3.47493E+17298这样的值。这远远超出了双精度运算所能处理的范围,我不需要额外的精度,只需要额外的指数范围,所以我用C#创建了自己的小结构

我的结构使用long表示有效位和符号,int表示指数,因此我实际上有:

1符号位 32个指数位(常规2的补码指数) 63位有效位

我很好奇能采取什么措施使我的乘法程序更有效率。我对这些扩展范围值进行了大量的乘法运算,运算速度非常快,但我一直在寻找加快运算速度的提示

我的乘法程序:

    public static BigFloat Multiply(BigFloat left, BigFloat right)
    {
        long shsign1;
        long shsign2;

        if (left.significand == 0)
        {
            return bigZero;
        }

        if (right.significand == 0)
        {
            return bigZero;
        }

        shsign1 = left.significand;
        shsign2 = right.significand;

        // scaling down significand to prevent overflow multiply

        // s1 and s2 indicate how much the left and right 
        // significands need shifting.
        // The multLimit is a long constant indicating the
        // max value I want either significand to be
        int s1 = qshift(shsign1, multLimit);
        int s2 = qshift(shsign2, multLimit);

        shsign1 >>= s1;
        shsign2 >>= s2;

        BigFloat r;

        r.significand = shsign1 * shsign2;
        r.exponent = left.exponent + right.exponent + s1 + s2;

        return r;
    }
以及qshift:

它只是找出val的绝对值要比极限值小多少

    public static int qshift(long val, long limit)
    {
        long q = val;
        long c = limit;
        long nc = -limit;

        int counter = 0;

        while (q > c || q < nc)
        {
            q >>= 1;
            counter++;
        }

        return counter;
    }
public static int qshift(长值、长限)
{
长q=val;
长c=极限;
长nc=极限;
int计数器=0;
而(q>c | | q>=1;
计数器++;
}
返回计数器;
}

如果您确定不会出现溢出,您可以使用

这将删除溢出检查,并提高性能。

我不是一个很好的C#程序员,但这里有一些一般的想法

首先,是否有用于C#的分析工具?如果是这样的话,从这些开始

时间很可能花在qshift()函数中;特别是循环。预测失误的分支令人讨厌

我会将其改写为:

long q = abs(val);
int x = q/nc;
(find next power of 2 bigger than x)
有关最后一步,请参见

然后,不用qshift进行移位,只需除以2的幂。(C#是否有“find first set”(又名ffs)?如果有,您可以使用它从2的幂得到移位计数;它应该是一条指令。)

如果编译器不为您执行此操作,则一定要内联此序列


另外,我会放弃零的特殊情况,除非你经常乘零。线性码好;条件句不好。

这里有一个完全不同的想法

使用硬件的浮点机制,但使用您自己的整数指数对其进行扩充。换句话说,将
BigFloat.signifid
设为浮点数而不是整数

然后可以使用
ldexp
frexp
将浮点上的实际指数保持为零。这些应该是单机指令

因此,大浮点乘法变成:

  • r.signifid=left.signifid*right.signifid
  • r.exponent=left.exponent+right.exponent
  • tmp=
    (frexp中r.signifid的实际指数)
  • r.exponent+=tmp
  • (使用ldexp从
    r.signifind
    的实际指数中减去
    tmp
不幸的是,最后两个步骤需要
frexp
ldexp
,搜索结果表明这两个步骤在C#中不可用。所以你可能需要用C写这部分

或者,实际上

对有效位使用浮点数,但只需使其在1和2之间标准化即可。同样,对有效位使用浮点数,并按如下方式进行乘法:

r.significand = left.significand * right.significand;
r.exponent = left.exponent + right.exponent;
if (r.significand >= 2) {
    r.significand /= 2;
    r.exponent += 1;
}
assert (r.significand >= 1 && r.significand < 2);  // for debugging...
换句话说,我建议将其分解为一个“规范化”例程,因为您可能希望在加法、减法、乘法和除法之后使用它

还有所有角落里的案子要担心


您可能希望通过返回零来处理下溢。溢出取决于你的品味;应该是错误或+-无穷大。最后,如果frexp()的结果是无穷大或NaN,那么
tempep2
的值是未定义的,因此您可能也要检查这些情况。

您计算什么需要“值如3.47493E+17298”?而不是创建自己的,为什么不使用现有库,例如@Mitch:“我可以在1e17298注释中命名该调谐…”。另一个改进是
qshift
中的循环展开。我正在计算一个序列的值,它的增长速度几乎与阶乘一样快。这是一个非常奇怪的序列,所以需要大约3000个值才能真正开始了解序列何时为正,何时为负,并对其增长做出很好的估计。性能改进越多,计算速度越快。我已经从N ^ 3到n^ 2的主算法大幅度改进,只是把我当前的算法移植到C++是一个很大的改进。谢谢所有的提示。我将对此进行研究。我也有一个分析工具,它只显示了qshift中乘法所用时间的1/5。将q除以nc,然后求出商的2的幂实际上比仅仅移动q直到它的大小变小要慢,至少这是我的分析器所显示的。我知道会有溢出,因为我使用尽可能多的符号位来保持精度。所以在我的序列的前几项之后,有效位的大部分值都将接近max或minint。听起来是个好主意。无论如何,我想尝试一个C实现,所以我会支持你的第一个想法。使用更多浮点硬件听起来是个好主意。BigFloat BigFloat::Multiply(BigFloat left,BigFloat right){double temp=left.magnifid*right.magnifid;double tempep=left.exponent+right.exponent;double temp2;double tempep2;double tempep2=frexp(temp,&tempep2)我的应用程序的C++版本实际上是比较慢的,我认为这是因为BigS浮体使用浮点而不是整数类型,在这两种情况下,你必须缩放你的结果。我将通过在C++中实现BigFrand的整数版本来测试这个。目前,C版本已经计算出了
BigFloat BigFloat::normalize(BigFloat b)
{
    double temp = b.significand;
    double tempexp = b.exponent;
    double temp2, tempexp2;
    temp2 = frexp(temp, &tempexp2);
    // Need to test temp2 for infinity and NaN here
    tempexp += tempexp2;
    if (tempexp < MIN_EXP)
        // underflow!
    if (tempexp > MAX_EXP)
        // overflow!
    BigFloat r;
    r.exponent = tempexp;
    r.significand = temp2;
}