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

How to remove last significant digits/mantissa bits for floating numbers in C++

本文关键字:有效数字 最后一个 浮点数 何删除 删除 C++      更新时间:2023-10-16

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

一些背景:我可以使用不同的方式到达相同的浮点数。例如,如果我在矩形上使用双线性插值,其角值相等,则由于机器精度限制,矩形不同点的最后几位数字的结果将有所不同。这些偏差的绝对顺序取决于插值的顺序。例如,如果 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。

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

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

更一般地说,如果要比较ab是否足够相似,只需测试a/b是否近似1.00000000即可。(当b正好为零时,你就会遇到更大的问题——没有有意义的方法可以说 1E-10 是否"几乎等于"0(

相关文章: