Opengl es 如何在OpenGL ES中使用两个浮点来模拟双精度?

Opengl es 如何在OpenGL ES中使用两个浮点来模拟双精度?,opengl-es,floating-point,glsl,Opengl Es,Floating Point,Glsl,我正在致力于创建深入到Mandelbrot集合的缩放,您可能知道OpenGL ES不支持double数据类型。它所能提供的最高精度是IEEE 754float。在谷歌上,经过大量搜索,我发现了这个博客:它完全致力于这个主题。但是,不幸的是,我不能理解那里介绍的加法、减法和乘法的代码。特别是,我不能理解关于纠错和携带的部分。如果你能向我解释一下错误检查的深度,以及从低位到高位的位的转移,那将是非常有帮助的。 到目前为止,我只理解将double拆分为两个float的基本概念。但是,基本操作的实施对我

我正在致力于创建深入到Mandelbrot集合的缩放,您可能知道OpenGL ES不支持
double
数据类型。它所能提供的最高精度是IEEE 754
float
。在谷歌上,经过大量搜索,我发现了这个博客:它完全致力于这个主题。但是,不幸的是,我不能理解那里介绍的加法、减法和乘法的代码。特别是,我不能理解关于纠错和携带的部分。如果你能向我解释一下错误检查的深度,以及从低位到高位的位的转移,那将是非常有帮助的。
到目前为止,我只理解将double拆分为两个float的基本概念。但是,基本操作的实施对我来说并不清楚。如果使用二进制数的上下文进行解释,将非常有帮助。

首先介绍处理此问题的基本原理。添加或减去具有高指数差的数字后,结果将四舍五入:

12345600000 + 8.76542995683848E-4 = 12345600000
现在,正如我在中所展示的,我们可以将数字存储为更多浮点数的总和,例如
vec2、vec3、vec4
,这些浮点数仍然是浮点数,但加在一起可以组合成更大的总尾数位宽。你问题中的链接并不像我那样使用指数范围,而是使用四舍五入和非四舍五入结果之间的差异。然而,链接库仅使用
vec2
作为
double
的尾数,其精度仍然远低于本机
64
bit
double
53
位和
float
24
位,因此
24+24=48<53
这就是我决定使用
vec3
的原因。现在的诀窍是获得舍入误差。对于与上述相同的示例:

a=12345600000 
b=8.76542995683848E-4
c=a+b=12345600000
a、b
+
操作的
float
操作数,
c
是四舍五入结果。因此差异
e
可以如下获得:

e=c-a; // e= 0
e-=b;  // e=-8.76542995683848E-4
e=-e;  // e=+8.76542995683848E-4
c = (a.x+a.y+a.z)*(b.x+b.y+b.z)     // multiplication of 2 expresions
c = (a.x*b.x)+(a.x*b.y)+(a.x*b.z)   // expanded
   +(a.y*b.x)+(a.y*b.y)+(a.y*b.z)
   +(a.z*b.x)+(a.z*b.y)+(a.z*b.z)
c = (a.x*b.x)                       // ordered desc by magnitude (x>=y>=z)
   +(a.x*b.y)+(a.y*b.x)
   +(a.x*b.z)+(a.z*b.x)+(a.y*b.y)
   +(a.y*b.z)+(a.z*b.y)
   +(a.z*b.z)
其中,
e
是应添加到
c
中的内容,以获得不舍入的结果

现在,如果我们在
vec3
的每个组件中存储数字的某些部分,那么我们可以尝试将这个
e
添加到所有组件中(总是从
e
中删除添加的部分),直到
e
为零

因此,如果
c.x+e
进行四舍五入,我们将其添加到
c.y
中,依此类推。。。基于此,我成功撰写了以下内容:

//---------------------------------------------------------------------------
//---高精度浮子版本:1.000---------------------------------------
//---------------------------------------------------------------------------
#ifndef_GLSL_HP32
#定义(GLSL)HP32
//---------------------------------------------------------------------------
//辅助函数(内部)
void hp32_nor(vec3&c)//气泡排序c坐标按大小描述
{
浮动x;
if(fabs(c.x)vec2
float hp32_get(vec3a){float c;c=a.z+a.y;c+=a.x;返回c;}//vec2->float
vec3 hp32_add(vec3 a,vec3 b)/=a+b
{
//c=a+b;加法
vec3 c=a+b,e;浮点数q;
//e=(a+b)-c;舍入误差
c、 x=a.x+b.x;e.x=c.x-a.x;e.x-=b.x;
c、 y=a.y+b.y;e.y=c.y-a.y;e.y-=b.y;
c、 z=a.z+b.z;e.z=c.z-a.z;e.z-=b.z;
e=-e;hp32_err(c,e);
返回c;
}
vec3 hp32_sub(vec3 a,vec3 b)/=a-b
{
//c=a-b;减法
vec3 c=a-b,e;浮点数q;
//e=(a-b)-c;舍入误差
c、 x=a.x+b.x;e.x=c.x-a.x;e.x+=b.x;
c、 y=a.y+b.y;e.y=c.y-a.y;e.y+=b.y;
c、 z=a.z+b.z;e.z=c.z-a.z;e.z+=b.z;
e=-e;hp32_err(c,e);
返回c;
}
vec3 hp32_mul_half(vec3 a,vec3 b)/=a*b,其中a,b只是尾数的一半!!!内部调用不使用此选项!!!
{
//c=(a.x+a.y+a.z)*(b.x+b.y+b.z)//两个表达式的乘法
//c=(a.x*b.x)+(a.x*b.y)+(a.x*b.z)//扩展
//+(a.y*b.x)+(a.y*b.y)+(a.y*b.z)
//+(a.z*b.x)+(a.z*b.y)+(a.z*b.z)
//c=(a.x*b.x)//按大小顺序描述(x>=y>=z)
//+(a.x*b.y)+(a.y*b.x)
//+(a.x*b.z)+(a.z*b.x)+(a.y*b.y)
//+(a.y*b.z)+(a.z*b.y)
//+(a.z*b.z)
vec3c,e,f;浮点q,r;
//c=a*b;(e,f)=(a*b)-c;乘法
c、 x=(a.x*b.x);
r=(a.x*b.y);q=c.x;c.x+=r;e.x=r-(c.x-q);
r=(a.y*b.x);q=c.x;c.x+=r;e.y=r-(c.x-q);
c、 y=(a.x*b.z);
r=(a.z*b.x);q=c.y;c.y+=r;e.z=r-(c.y-q);
r=(a.y*b.y);q=c.y;c.y+=r;f.x=r-(c.y-q);
c、 z=(a.y*b.z);
r=(a.z*b.y);q=c.z;c.z+=r;f.y=r-(c.z-q);
r=(a.z*b.z);q=c.z;c.z+=r;f.z=r-(c.z-q);
e=+hp32加(e,f);hp32加(c,e);
返回c;
}
vec3 hp32_mul(vec3 a,vec3 b)/=a*b
{
vec3 ah、al、bh、bl、c;
//将操作数拆分为尾数的一半
hp32_分割(ah,al,a);
hp32_分割(bh、bl、b);
//c=(ah+al)*(bh+bl)=ah*bh+ah*bl+al*bh+al*bl
c=hp32μm半(ah,bh);
c=hp32加(c,hp32加一半(ah,bl));
c=hp32加(c,hp32加半(al,bh));
c=hp32加(c,hp32加半(al,bl));
返回c;
}
//---------------------------------------------------------------------------
#恩迪夫
//---------------------------------------------------------------------------
目前,我只在CPU端(C++)进行了测试。为了在GLSL中使用它,只需注释掉或删除我用来验证准确性的双api函数。然后将
fabs
更改为
abs
或声明:

float fabs(float x){ return abs(x); }
同样,我有一些标准化函数
hp32\u nor
,它按大小对组件进行排序,因此
fabs(x)>=fabs(y)>=fabs(z)
需要返回到
float
和乘法。
+,-
不需要它

hp32\u err
类似于addit