使用两个花车进行双重划分?

时间:2010-11-04 13:21:20

标签: c# floating-point directx-11 compute-shader

我想使用两个浮点数进行双重划分(看起来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;

然后结果是正确的。

5 个答案:

答案 0 :(得分:5)

使用浮点除法计算计数的倒数,然后使用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;

答案 1 :(得分:3)

  

这可能吗?

是的,只要你:

  • 接受不可避免的精确度损失
  • 请记住,首先并非所有双打都适合花车

更新

阅读完评论后(需要双精度),我更新的答案是:

没有

答案 2 :(得分:3)

显然你的算术错误并不是很明显。让我拼出来。

假设一个double有两个部分,大部分和小部分,每个部分大约有32位精度。 (这不是双打的确切方式,但它可以用于我们的目的。)

一个浮点只有一个部分。

想象一下,我们一次只做32位,但保持所有内容都是双倍的:

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

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

double littlequotient = dividendlittle / divisor;

再次,小商数是小商品+小商品。现在我们添加商:

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很小。

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

你把它们加在一起,你得到bigquotientbig + littlequotientbig。 bigquotientlittle发生了什么变化?你已经失去了32位的精度,所以你一路上就会得到32位不准确的东西也就不足为奇了。 你没有想出用32位近似64位算术的正确算法。

为了计算(big + little)/divisor,您不能简单地执行(big / divisor) + (little / divisor)。在每个分区期间舍入时,该代数规则不适用!

现在清楚了吗?

答案 3 :(得分: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%的时间。

答案 4 :(得分:0)

在评论中,您说:

  

当然不应该有任何损失   精度这就是我使用的原因   两个花车。如果我愿意接受损失   精确,然后我可以投两个   漂浮并做分工。

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

也就是说,可能使用双精度和单精度之间的转换,双精度减法/加法和单精度运算来进行正确舍入的双精度除法,但是如果真的那么它会非常复杂我想做对。您是否需要实际的IEEE-754正确舍入,或者只需要最后一两位的答案?