0

我想使用两个浮点数进行双重除法(Direct Compute 似乎不支持双重除法)。

那可能吗?

这是我到目前为止尝试过的(c#代码,以后应该是HLSL):

int count = 7;
double value = 0.0073812398871474;
float f1 = (float)value;
float f2 = (float)((value - f1));
float r1 = f1 / count;
float r2 = f2 / count;
double result = (double)r1 + (double)r2;

0,00105446285765182(结果)

0,00105446284102106(正确结果)

它与 f1 中的舍入有关。如果值改为:

 double value = 0.0073812344471474;

那么结果是正确的。

4

5 回答 5

6

使用浮点除法计算计数的倒数,然后使用 Newton-Raphson 倒数公式将精度提高到两倍。

int count = 7;
double value = 0.0073812398871474;
double r = (double) (1.0f / count); // approximate reciprocal
r = r * (2.0 - count*r); // much better approximation
r = r * (2.0 - count*r); // should be full double precision by now.
double result = value * r;
于 2010-11-04T15:46:14.117 回答
3

显然,您的算术错误并没有立即清楚。让我把它拼出来。

假设一个 double 有两个部分,大部分和小部分,每个部分的精度大约为 32 位。(这并不完全是双打的工作方式,但它可以满足我们的目的。)

浮子只有一个部分。

想象一下,我们一次处理 32 位,但将所有内容保持为双精度:

double divisor = whatever;
double dividend = dividendbig + dividendlittle;
double bigquotient = dividendbig / divisor;

什么是大商?是双倍的。所以它有两个部分。bigquotient 等于 bigquotientbig + bigquotientlittle。继续:

double littlequotient = dividendlittle / divisor;

同样,littlequotient 是 littlequotientbig + littlequotientlittle。现在我们添加商:

double quotient = bigquotient + littlequotient;

我们如何计算?商有两个部分。quotientbig 将设置为 bigquotientbig。quotientlittle 将设置为 bigquotientlittle + littlequotientbig。littlequotientlittle 被丢弃。

现在假设您在浮动中执行此操作。你有:

float f1 = dividendbig;
float f2 = dividendlittle;
float r1 = f1 / divisor;

好的,r1 是什么?这是一个浮子。所以它只有一个部分。r1 是 bigquotientbig。

float r2 = f2 / divisor;

什么是r2?这是一个浮子。所以它只有一个部分。r2 是 littlequotientbig。

double result = (double)r1 + (double)r2;

你把它们加在一起,你得到 bigquotientbig + littlequotientbig。 bigquotientlittle怎么了? 您已经失去了 32 位的精度,因此您在此过程中得到 32 位的不准确性也就不足为奇了。 您根本没有提出在 32 位中逼近 64 位算术的正确算法。

为了计算(big + little)/divisor,你不能简单地做(big / divisor) + (little / divisor)当您在每个除法 期间四舍五入时,该代数规则不适用!

现在清楚了吗?

于 2010-11-04T15:28:33.737 回答
3

那可能吗?

是的,只要您:

  • 接受不可避免的精度损失
  • 请记住,并非所有双打一开始都适合浮动

更新

After reading your comments (double precision is a requirement), my updated answer is:

No.

于 2010-11-04T13:28:26.947 回答
1

那么像这样的东西怎么样

result = value * (double)(1f / (float)count); ?

在那里你只划分两个花车。我的演员阵容比需要的多,但重要的是概念。

编辑:
好的,所以你担心实际和圆形之间的差异,对吧?所以一遍又一遍地做,直到你做对!

double result = 0;
double difference = value;
double total = 0;
float f1 = 0;
while (difference != 0)
{
    f1 = (float)difference;
    total += f1;
    difference = value - total;
    result += (double)(f1 / count);
}

...但你知道,简单的答案仍然是“不”。这仍然没有捕捉到所有的舍入错误。根据我的测试,它最多可以将不准确性降低到 1e-17,大约 30% 的时间。

于 2010-11-04T14:44:00.143 回答
0

在评论中,你说:

当然不应该有任何精度损失。这就是我使用两个浮点数的原因。如果我接受精度损失,那么我可以只投两个浮点数并进行除法。

一个 IEEE-754single precision值有 24 个有效二进制数字。一个double precision值有 53 位有效数字。你甚至不能将一个双精度值表示为两个单精度值而不损失精度,更不用说用这种表示法进行算术运算了。

也就是说,可以只使用双精度和单精度之间的转换、双精度减法/加法和单精度运算来进行正确舍入的双精度除法,但如果你真的想正确地做这件事,那就相当复杂了。您需要实际的 IEEE-754 正确舍入,还是只需要最后一位或两位正确的答案?

于 2010-11-04T15:57:03.583 回答