如何删除C ++中浮点数的最后有效数字/尾数位

时间:2015-04-02 11:04:41

标签: c++

我想以有效的方式删除C ++中浮点数的最后2或3位有效数字。为了更准确地表达问题 - 我想在浮点数表示中丢弃最后几个尾数位。

一些背景:我可以使用不同的方式到达相同的浮点数。例如,如果我在矩形中使用双线性插值,其角点中的值相等,则由于机器精度限制,结果将在矩形的不同点中的最后几位数中变化。这些偏差的绝对顺序取决于内插值的顺序。例如,如果p [i] ~1e10(i == 1..4,矩形角处的值),则由机器精度引起的插值误差为~1e-4(对于8字节浮点数)。如果p [i] ~1e-10那么误差将是~1e-24。由于插值用于计算一阶或二阶导数,我需要“平滑”这些差异。一个想法是从最终结果中删除最后几位数字。以下是我的看法:

#include <iostream>     // std::cout
#include <limits>       // std::numeric_limits
#include <math.h>       // fabs

template<typename real>
real remove_digits(const real& value, const int num_digits)
{
  //return value;
  if (value == 0.) return 0;
  const real corrector_power =
       (std::numeric_limits<real>::digits10 - num_digits)
                               - round(log10(fabs(value)));

  //the value is too close to the limits of the double minimum value
  //to be corrected -- return 0 instead.
  if (corrector_power > std::numeric_limits<real>::max_exponent10 -
                        num_digits - 1)
  {
    return 0.;
  }
  const real corrector = pow(10, corrector_power);
  const real result =
     (value > 0) ? floor(value * corrector + 0.5) / corrector :
                    ceil(value * corrector - 0.5) / corrector;
  return result;
}//remove_digits

int main() {
  // g++ (Debian 4.7.2-5) 4.7.2 --- Debian GNU/Linux 7.8 (wheezy) 64-bit
  std::cout << remove_digits<float>(12345.1234, 1) << std::endl;  // 12345.1
  std::cout << remove_digits<float>(12345.1234, 2) << std::endl;  // 12345
  std::cout << remove_digits<float>(12345.1234, 3) << std::endl;  // 12350
  std::cout << std::numeric_limits<float>::digits10 << std::endl; // 6
}

它有效,但使用了2个昂贵的操作 - log10和pow。有更聪明的方法吗?如上所述为实现我的目标,我不需要删除实际的十进制数字,而只需将浮点数的尾数表示中的3-4位设置为0.

1 个答案:

答案 0 :(得分:0)

“一些背景:我可以使用不同的方式到达相同的浮点数。”

这不是问题。 x附近的“局部”分辨率约为x-std::next_after(x,0.0),在x范围内,它在很宽的范围内是线性的。 p[i]变化20个数量级的问题无关紧要,因为相对误差与p[i]线性变化,这意味着它也会与第一个表达式线性变化。

更一般地说,如果您想比较ab是否足够相似,只需测试a/b是否约为1.00000000。 (当b正好为零时,你遇到了更大的问题 - 没有任何有意义的方法来说明1E-10是否“几乎等于”0)