c/c++语言开发共享用于高性能加法和乘法的常量forms

我需要在循环中有效地将一些常量添加或乘以double类型的结果以防止下溢。 例如,如果我们有int,则乘以2的幂会很快,因为编译器将使用位移。 有效的double加法和乘法是否有一种常量forms?

编辑:似乎没有多少人理解我的问题,为我的邋iness道歉。 我会添加一些代码。 如果a是int,则(乘以2的幂)将更有效

 int a = 1; for(...) for(...) a *= somefunction() * 1024; 

比1024更换为1023时。不确定如果我们想要添加到int中最好的是什么,但这不是我感兴趣的。 我对a是双倍的情况感兴趣。 我们可以有效地添加乘以双精度的常数forms (例如2的幂)是什么? 常量是任意的 ,只需要足够大以防止下溢。

这可能不仅限于C和C ++,但我不知道更合适的标签。

    在大多数现代处理器上,简单地乘以2的幂(例如, x *= 0x1p10;乘以2 10x *= 0x1p-10;除以2 10 )将是快速且无错误的(除非结果大到足以溢出或小到足以下溢)。

    对于某些浮点运算,有一些处理器具有“早期出局”。 也就是说,当某些位为零或满足其他标准时,它们会更快地完成指令。 但是,浮点加法,减法和乘法通常在大约四个CPU周期内执行,所以即使没有提前输出它们也相当快。 此外,大多数现代处理器一次执行多个指令,因此其他工作在乘法发生时进行,并且它们是流水线的,因此,通常,每个CPU周期中可以启动一次乘法(并且一次完成)。 (有时更多。)

    乘以2的幂没有舍入误差,因为有效数(值的小数部分)不会改变,因此新的有效数是完全可表示的。 (除非乘以小于1的值,有效位的位数可以低于浮点类型的限制,导致下溢。对于常见的IEEE 754双格式,直到值小于0x1p-1022。)

    不要使用除法进行缩放(或者用于反转先前缩放的效果)。 相反,乘以逆。 (要删除先前的0x1p57缩放,请乘以0x1p-57。)这是因为除法指令在大多数现代处理器上都很慢。 例如,30个周期并不罕见。

    首先得到你的双联合并选择“范围”“指数”部分。 然后只移动“指数”“范围”部分。 寻找IEEE浮点标准。 不要忘记标志和最后一个尾数位

     union int_add_to_double { double this_is_your_double_precision_float; struct your_bit_representation_of_double { int range_bit:53;//you can shift this to make range effect //i dont know which is mantissa bit. maybe it is first of range_bit. google it. int exponent_bit:10; //exponential effect int sign_bit:1; //take negative or positive }dont_forget_struct_name; }and_a_union_name; 

    浮点加法和乘法通常在现代处理器中需要几个周期。

    也许您应该退一步思考算法正在做什么。 在你的例子中,你有一个双嵌套循环…这意味着“somefunction()”可能被多次调用。 “double”的公共表示是IEEE,它使用11位作为指数,52位作为尾数(53实际上因为除了零之外还有隐含的’1’)。 这意味着您可以在从非常小到非常大的数字范围内将数字表示为53位精度 – 二进制“浮点”可以在数字“1.0”的左侧或右侧移动1024(2 ^ 10)个位置。 ..如果“somefunction()”被调用了一千次并且它总是返回一个小于或等于0.5的数字你的下溢(每次你乘以0.5你将你的数字“a”减少一半,这意味着你移动二进制浮动在x86上,您可以通过在控制寄存器中设置一个位来告诉处理器“将非正规数刷新为零” – 没有可执行此操作的可移植编程接口,您使用的是gcc

     _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); 

    告诉处理器将非正规数刷新为零将使您的代码运行得更快,因为处理器不会尝试表示超出(小于)法线(子法线或非正规)的数字。 看起来你正试图在生成子法线的算法面前保持精度(这会导致精度损失)。 如何最好地处理这取决于你是否控制“somefunction()”。 如果您确实控制了该函数,那么您可以将其返回的值“规范化”到该范围内的值

     0.5 <= X <= 2.0 

    换句话说,返回值以1.0为中心,并分别跟踪2的幂,您需要将最终答案相乘以正确缩放。

    如果你正在使用SSE,直接向exponent字段添加常量是一个合法的技巧(在FPU代码中它非常糟糕) – 它通常具有两倍的吞吐量和4倍更好的延迟(除了具有float-> int的处理器)和/或int->浮动惩罚)。 但是既然你只是这样做是为了防止非正规,为什么不打开FTZ(刷新到零)和DAZ(非正规为零)?

    您可以使用标准的frexp / ldexp函数将IEE 754值分解为其组件:

    这是一个简单的示例代码:

     #include  #include  int main () { double value = 5.4321; int exponent; double significand = frexp (value , &exponent); double result = ldexp (significand , exponent+1); std::cout << value << " -> " << result << "n"; return 0; } 

    执行处理: http : //ideone.com/r3GBy

    在千兆赫兹处理器上,您可以通过优化这种方式(移位与算术)来节省1或2纳秒。 但是,从内存加载和存储所需的时间大约为100 nsecs,而磁盘则为10 msecs。 与优化缓存使用和磁盘活动相比,担心算术运算毫无意义。 它永远不会对任何真正的生产计划产生影响。

    为了防止误解,我不是说差别很小所以不要担心,我说的是零。 您不能编写一个简单的程序,其中ALU时间的差异与CPU等待内存或I / O的时间不完全重叠。

      以上就是c/c++开发分享用于高性能加法和乘法的常量forms相关内容,想了解更多C/C++开发(异常处理)及C/C++游戏开发关注计算机技术网(www.ctvol.com)!)。

      本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

      ctvol管理联系方式QQ:251552304

      本文章地址:https://www.ctvol.com/c-cdevelopment/560300.html

      (0)
      上一篇 2021年1月27日
      下一篇 2021年1月27日

      精彩推荐