C++ double-double扩展精度类浮点编译优化问题
关于Double-Double高精度类在MSVC Release模式下精度丢失的问题
我用C++实现了一个double-double类,通过两个double类型提升数值精度,数值表示为number = hi + lo(实际不会直接计算该和,因为hi + lo == hi)。以下是精简后的代码:
class doubledouble { public: double hi, lo; doubledouble() { hi = 0.0; lo = 0.0; } doubledouble quickTwoSum(const double a, const double b) const { int old; doubledouble out; double s = a + b; double e = b - (s - a); out.hi = s; out.lo = e; return (out); } doubledouble twoSum(const double a, const double b) const { double s = a + b; double v = s - a; double e = (a - (s - v)) + (b - v); doubledouble out; out.hi = s; out.lo = e; return (out); } doubledouble operator+(const doubledouble& in) const { doubledouble s, t; s = twoSum(in.hi, this->hi); t = twoSum(in.lo, this->lo); s.lo += t.hi; s = quickTwoSum(s.hi, s.lo); s.lo += t.lo; s = quickTwoSum(s.hi, s.lo); return(s); } doubledouble split(const double a) const { const double split = (1 << 12) + 1; double t = a * split; doubledouble out; out.hi = t - (t - a); out.lo = a - out.hi; return (out); } doubledouble twoProd(const double a, const double b) const { double p = a * b; doubledouble aS = split(a); doubledouble bS = split(b); double err = ((aS.hi * bS.hi - p) + aS.hi * bS.lo + aS.lo * bS.hi) + aS.lo * bS.lo; aS.hi = p; aS.lo = err; return (aS); } doubledouble operator*(const doubledouble& in) const { doubledouble p; p = twoProd(this->hi, in.hi); p.lo += this->hi * in.lo + this->lo * in.hi; p = quickTwoSum(p.hi, p.lo); return(p); } };
这段代码在Debug模式下工作正常,但在Release模式下仅能达到普通double的精度。我使用MSVC编译器,若在类的前后添加#pragma optimize("", off)和对应的on,代码可正常运行但速度极慢。
我原本推测问题出在t - (t - a)这类表达式上——从数学上看它等于a,编译器可能会对其进行优化。为定位问题,我尝试在特定函数前后添加#pragma optimize,但即使给类内所有函数都添加该指令,代码仍无法正常工作。
我的两个问题:
- 为什么
#pragma optimize在类内的特定函数中无效?这对定位问题至关重要。 - 如何让编译器不对
t - (t - a)这类特定表达式进行优化?使用临时变量似乎也会被编译器优化掉。
编辑补充
作为可复现的测试代码,我使用了如下片段:
double a = 1.23456789012345; const double split = (1 << 12) + 1; double t = a * split; double hi = t - (t - a); double lo = a - hi; std::cout << "hi: " << hi << ", lo: " << lo << std::endl;
Debug和Release模式下的输出均为:
hi: 1.23457, lo: -3.48832e-13
由此可见t - (t - a)似乎不是问题所在。
至于能复现错误行为的“简单”示例——我计算了缩放倍数超过10^14的曼德博集合,Debug模式下结果完美,但Release模式下出现像素化。生成图像的函数如下:
void CalcMandelCPU_doubledouble(std::atomic<unsigned int>* RowCounter) { std::complex<doubledouble> Z, C; int Y = (*RowCounter)++; while (Y < ResY) { for (int X = 0; X < ResX; X++) { C.real(minX + (X + 0.5) * (maxX - minX) / ResX); C.imag(minY + (Y + 0.5) * (maxY - minY) / ResY); int i; Z = 0; for (i = 0; i < MaxIter; i++) { Z = Z * Z + C; if (Z.real() * Z.real() + Z.imag() * Z.imag() > 4.0) break; } if (i == MaxIter) { Result[Y * ResX + X] = 0; } else { Result[Y * ResX + X] = i + 1 - log(log(Z.real() * Z.real() + Z.imag() * Z.imag())) / log(2.0); } } Y = (*RowCounter)++; } }
需定义MaxIter、ResX和ResY,minX、maxX、minY和maxY用于指定缩放后的视图,示例视图变量(均为doubledouble类型):
minX.hi = -1.9997740601362948,minX.lo = 9.2915246622385581e-17maxX.hi = -1.9997740601362861,maxX.lo = 7.5150963188138267e-17minY.hi = -3.2900430992572130e-09,minY.lo = 1.7490722630808694e-25maxY.hi = -3.2900375437016574e-09,maxY.lo = 1.0427104313278710e-25
内容的提问来源于stack exchange,提问作者Paul Aner
相关产品推荐
相关产品推荐

