- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
近期 question ,是否允许编译器用浮点乘法代替浮点除法,启发我提出这个问题。
在严格要求下,代码转换后的结果应与实际除法运算按位相同,
很容易看出,对于二进制 IEEE-754 算术,这对于 2 的幂的除数是可能的。只要互惠互利
除数的可表示性,乘以除数的倒数得出与除法相同的结果。例如,乘以 0.5
可以用 2.0
代替除法.
然后人们想知道这种替换的其他除数是否有效,假设我们允许任何替换除法但运行速度明显更快的短指令序列,同时提供位相同的结果。除了普通乘法之外,特别允许融合乘加运算。
在评论中,我指出了以下相关论文:
尼古拉斯·布里斯巴尔、让-米歇尔·穆勒和索拉布·库马尔·雷纳。当除数已知时,加速正确舍入的浮点除法。 IEEE 计算机交易,卷。 53,第 8 期,2004 年 8 月,第 1069-1072 页。
论文作者提倡的技术将除数 y 的倒数预先计算为归一化的头尾对 zh:zl 如下:zh = 1/y, zl = fma (-y, zh, 1)/y。之后,除法 q = x/y 然后计算为 q = fma (zh, x, zl * x)。该论文推导出除数 y 必须满足的各种条件才能使该算法工作。正如人们很容易观察到的那样,当头部和尾部的符号不同时,该算法存在无穷大和零的问题。更重要的是,它无法为数量级非常小的红利 x 提供正确的结果,因为商尾 zl * x 的计算存在下溢。
该论文还顺便提及了一种替代的基于 FMA 的除法算法,该算法由 Peter Markstein 在 IBM 时开创。相关引用是:
P.W.马克斯坦。在 IBM RISC System/6000 处理器上计算基本函数。 IBM 研究与开发杂志,卷。 34,第 1 期,1990 年 1 月,第 111-119 页
在 Markstein 的算法中,首先计算倒数 rc,从中形成初始商 q = x * rc。然后,使用 FMA 精确计算除法的余数,如 r = fma (-y, q, x),最后计算改进的、更准确的商为 q = fma (r, rc, q)。
该算法对于为零或无穷大的 x 也存在问题(通过适当的条件执行很容易解决),但使用 IEEE-754 单精度 float
进行了详尽的测试。数据表明,对于许多除数 y,在这些许多小整数中,它提供了所有可能的红利 x 的正确商。这段 C 代码实现了它:
/* precompute reciprocal */
rc = 1.0f / y;
/* compute quotient q=x/y */
q = x * rc;
if ((x != 0) && (!isinf(x))) {
r = fmaf (-y, q, x);
q = fmaf (r, rc, q);
}
3.0f
,
nvcc
CUDA 7.5 的编译器为 Kepler 级 GPU 生成以下机器代码:
LDG.E R5, [R2]; // load x
FSETP.NEU.AND P0, PT, |R5|, +INF , PT; // pred0 = fabsf(x) != INF
FMUL32I R2, R5, 0.3333333432674408; // q = x * (1.0f/3.0f)
FSETP.NEU.AND P0, PT, R5, RZ, P0; // pred0 = (x != 0.0f) && (fabsf(x) != INF)
FMA R5, R2, -3, R5; // r = fmaf (q, -3.0f, x);
MOV R4, R2 // q
@P0 FFMA R4, R5, c[0x2][0x0], R2; // if (pred0) q = fmaf (r, (1.0f/3.0f), q)
ST.E [R6], R4; // store q
PASS: 1, 2, 3, 4, 5, 7, 8, 9, 11, 13, 15, 16, 17, 19, 21, 23, 25, 27, 29, 31, 32, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63, 64, 65, 67, 69,
x
编码中正确工作。对于那些除数
y
是奇数或 2 的幂。当然,轶事证据,不是证据。
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
int main (void)
{
float r, q, x, y, rc;
volatile union {
float f;
unsigned int i;
} arg, res, ref;
int err;
y = 1.0f;
printf ("PASS: ");
while (1) {
/* precompute reciprocal */
rc = 1.0f / y;
arg.i = 0x80000000;
err = 0;
do {
/* do the division, fast */
x = arg.f;
q = x * rc;
if ((x != 0) && (!isinf(x))) {
r = fmaf (-y, q, x);
q = fmaf (r, rc, q);
}
res.f = q;
/* compute the reference, slowly */
ref.f = x / y;
if (res.i != ref.i) {
err = 1;
break;
}
arg.i--;
} while (arg.i != 0x80000000);
if (!err) printf ("%g, ", y);
y += 1.0f;
}
return EXIT_SUCCESS;
}
最佳答案
让我第三次重新开始。我们正在努力加速
q = x / y
哪里
y
是整数常量,且
q
,
x
, 和
y
都是
IEEE 754-2008 binary32浮点值。下面,
fmaf(a,b,c)
表示融合乘加
a * b + c
使用 binary32 值。
C = 1.0f / y
这样在运行时(更快)乘法就足够了:
q = x * C
Brisebarre-Muller-Raina 加速度使用两个预先计算的常数,
zh = 1.0f / y
zl = -fmaf(zh, y, -1.0f) / y
这样在运行时,一次乘法和一次融合乘加就足够了:
q = fmaf(x, zh, x * zl)
Markstein 算法将朴素方法与两个融合乘加相结合,如果朴素方法在最不重要的位置产生 1 个单位内的结果,则通过预先计算得出正确的结果
C1 = 1.0f / y
C2 = -y
这样除法可以近似使用
t1 = x * C1
t2 = fmaf(C1, t1, x)
q = fmaf(C2, t2, t1)
y
,但除此之外就很糟糕了。例如,对于除数 7、14、15、28 和 30,它在所有可能的
x
中超过一半产生了错误的结果。 .
y
,但要少得多
x
产生不正确的结果(不到所有可能的一半
x
,取决于
y
)。
y
,以及奇数
y
. (我没有发现 Markstein 方法的失败奇整数除数。)
x
的值数量,其中 Markstein 方法对于所述除数失败),我们可以看到一个简单的模式发生:
4194304/x
. (请记住,该图仅考虑可能的浮点数的一半,因此在考虑所有可能的浮点数时,将其加倍。)
8388608/x
和
2097152/x
将整个错误模式完全括起来。
rev(y)
计算除数的位反转
y
,然后
8388608/rev(y)
是案例数量的一个很好的一阶近似值(在所有可能的浮点数中),其中 Markstein 方法为偶数、非 2 的幂除数
y
产生不正确的结果. (或者,
16777216/rev(x)
作为上限。)
function markstein_failure_estimate(divisor):
if (divisor is zero)
return no estimate
if (divisor is not an integer)
return no estimate
if (divisor is negative)
negate divisor
# Consider, for avoiding underflow cases,
if (divisor is very large, say 1e+30 or larger)
return no estimate - do as division
while (divisor > 16777216)
divisor = divisor / 2
if (divisor is a power of two)
return 0
if (divisor is odd)
return 0
while (divisor is not odd)
divisor = divisor / 2
# Use return (1 + 83833608 / divisor) / 2
# if only nonnegative finite float divisors are counted!
return 1 + 8388608 / divisor
这在我测试过的 Markstein 故障案例中产生了 ±1 以内的正确误差估计(但我还没有充分测试大于 8388608 的除数)。最后的划分应该是这样的,它不会报告假零,但我不能保证(还)。它没有考虑具有下溢问题的非常大的除数(比如 0x1p100 或 1e+30,以及更大的数量级)——无论如何我肯定会从加速中排除这些除数。
关于c - 具有恒定整数除数的高效浮点除法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35527683/
我正在尝试编写一个简单的除法函数,但出现错误 PS C:\Users\john> Function Div($x, $y) { $x / $y } PS C:\Users\john> Div (1,
试图找出这个伪代码。以下假设...... 我只能使用无符号和有符号整数(或长整数)。 除法返回一个没有余数的实数。 MOD 返回一个实数。 不处理分数和小数。 INT I = 41828; INT C
如果我有以下表格并且我在关系代数中执行 R1/R2,结果会是一个具有 A 值 1 和 3 的表格吗?我有点困惑,因为我知道 3 将是一个结果,因为它包含 5 和 1,但结果 1 除了匹配的值之外还有
//Declare and intialize variables - programmer to provide initial values Scanner in = new Scanne
除法运算符在 scala BigDecimal 上有什么用? val d1 = BigDecimal(2) val d2 = BigDecimal(3) val div = d1 / d2 //thr
这个问题在这里已经有了答案: How can I divide properly using BigDecimal (2 个答案) 关闭 6 年前。 我在这里做错了什么?很确定这是正确的,我能够打印
好的 - 已经为此苦苦挣扎了一段时间。我刚刚开始学习 Python,所以非常新。 我有一个元组列表,需要按每个元组中值的比率进行排序。 输入: L = [(1,3), (1,7), (4,8)] 返回
我有一个奇怪的问题,我收到计算机生成的方程式(作为字符串),其中偶尔会出现零或一和零的乘法/除法。这些等式将以字符串形式呈现给用户。 我知道我可以通过实现一种解析器来删除等式中的这些冗余部分,但我很好
我有两个变量:count,这是我过滤的对象的数量,以及每页的常量值。我想将计数除以 per_page 并获得整数值,但无论我尝试什么 - 我都得到 0 或 0.0: >>> count = frien
我尝试在 Go 中获得 2.4/0.8 == 3 w:=float64(2.4) fmt.Println(math.Floor(w/0.8),math.Floor(2.4/0.8) ) 它给了我“2
程序清单: # val_caculate.py a = 10 # a是整数 print('10/3 = ',10/3) print('9/3 = ',9/3) pri
我是 java 新手,所以我需要你对我正在进行的项目的帮助!我定义了一些计数器,这些是我将使用的: int[] acceptCounters = {}; int[] acceptFailCounter
我正在除 2 个 BigInteger 值 N = 9440056782685472448790983739834832785827768777249804302814308027414135716
我的应用程序中有使用 array.reduce 将数字相乘的代码。它看起来像这样: // Private function to multiply field values together func
我目前创建了一个名为 Array Math 的类,它将乘法加载到 10x10 数组中,如代码下显示的图像所示,但是我想要做的是在乘法后将每个位置除以 2。换句话说,(行 * 列)/2 目前我只是将这些
我正在使用代表货币金额的 BigDecimal 值。我需要将此金额分成 6 个费率,前 5 个费率四舍五入为 5,其余的为第 6 个费率。 BigDecimal numberOfRates = new
这个问题必须使用递归来解决。 我尝试使用 “else” 之后的代码来使用 int temp 计算商,该 temp 计算可以除以多少次 (temp = dividend - divisor)。 int
我知道这一定是有史以来最简单的事情,但我是这里的初学者。为什么我运行时会出现语法错误 document.write(10 / 2 + ""); //Divide 10 by 5 to get 2
这应该是一个非常基本的东西,但不知何故我没有看到问题。 #include template inline void i2c(const int & ind, int & i, int &j) {
我正在做课本中的一些家庭作业,并且有一些关于某些算术运算的浮点舍入/精度的问题。 如果我像这样从 int 中转换 double : int x = random(); double dx = (dou
我是一名优秀的程序员,十分优秀!