舍入错误检测

时间:2018-05-08 07:35:50

标签: c++ floating-point rounding-error

我有两个整数n和d。这些可以用双dn(n)和双dd(d)精确表示。有没有一种可靠的方法在C ++中检查是否

double result = dn/dd

包含舍入错误?如果它只是一个整数除法检查(n/d) * d==n是否可行,但使用双精度算法执行此操作可能会隐藏舍入错误。

编辑:发布后不久,让我感到震惊的是,将舍入模式更改为round_down会使(n/d)*d==n测试工作为double。但如果有一个更简单的解决方案,我仍然希望听到它。

4 个答案:

答案 0 :(得分:3)

如果忽略溢出和下溢(除非表示dn的整数类型非常宽,否则你应该能够做到),那么(二进制)浮点除法{{1确切地说,iff dn/ddd乘以2的幂的除数。

检查此问题的算法可能如下所示:

n

如果你希望舍入模式在其余时间“最接近”,这比改变FPU舍入模式便宜,并且可能存在可以加速提取最大奇数除数的比特诡计。 assert(d != 0); while (d & 1 == 0) d >>= 1; // extract largest odd divisor of d int exact = n % d == 0;

答案 1 :(得分:3)

如果硬件FMA可用,那么,在大多数情况下(预计n不小的情况下,如下),最快的测试可能是:

#include <cmath>
…
double q = dn/dd;
if (std::fma(-q, dd, dn))
    std::cout << "Quotient was not exact.\n";

如果 nd - q dd 如此之小,它可能会失败,它会四舍五入为零,这发生在舍入到最近的位置如果其幅度小于最小可表示正值的一半(通常为2 -1074 ),则为-even-even模式。只有当dn本身很小时才会发生这种情况。如果需要,我希望我可以计算dn上的一些限制,并且,假设dn = nn是整数,则不应该发生。

忽略指数边界,测试可分性的有效位数的方法是:

#include <cfloat>
#include <cmath>
…
int sink; // Needed for frexp argument but will be ignored.
double fn = std::ldexp(std::frexp(n, &sink), DBL_MANT_DIG);
double fd = std::frexp(d, &sink);
if (std::fmod(fn, fd))
    std::cout << "Quotient will not be exact.\n";

鉴于 n d 是在浮点类型中可以完全表示的整数,我想我们可以证明他们的指数不能是这样的,上面的测试会失败。有些情况下 n 是一个小整数且 d 很大(从2 1023 到2 1024 的值我需要考虑-2 972 ,包括在内。

答案 2 :(得分:3)

  

在C ++中是否有可靠的方法来检查double result = dn/dd是否包含舍入错误?

如果您的系统允许访问各种FP标志,请在分割后测试FE_INEXACT

如果FP代码很昂贵,那么至少可以使用此代码来检查仅整数解决方案。

遵循C解决方案,(我无法访问兼容的C ++编译器以立即进行测试)

#include <fenv.h>
// Return 0: no rounding error
// Return 1: rounding error
// Return -1: uncertain
#pragma STDC FENV_ACCESS ON
int Rounding_error_detection(int n, int d) {
  double dn = n;
  double dd = d;

  if (feclearexcept(FE_INEXACT)) return -1;
  volatile double result = dn/dd;
  (void) result;
  int set_excepts = fetestexcept(FE_INEXACT);
  return set_excepts != 0;
}

测试代码

void Rounding_error_detection_Test(int n, int d) {
  printf("Rounding_error_detection(%d, %d) --> %d\n", 
    n, d, Rounding_error_detection(n,d));
}

int main(void) {
  Rounding_error_detection_Test(3, 6);
  Rounding_error_detection_Test(3, 7);
}

输出

Rounding_error_detection(3, 6) --> 0
Rounding_error_detection(3, 7) --> 1

答案 3 :(得分:0)

如果商q=dn/dd是精确的,它将dn精确划分dd次 由于你有dd是整数,你可以用整数除法测试精确度 而不是测试商数乘以dd和(dn/dd)*dd==dn,其中四舍五入错误可以补偿,你应该测试余数。
确实std:remainder总是确切的:

if(std:remainder(dn,dn/dd)!=0)
    std::cout << "Quotient was not exact." << std::endl;