精确地返回双精度

时间:2011-09-10 17:02:19

标签: c++ c double

假设我有一个返回double的方法,但我想确定要返回的值的点后的精度。我不知道double可变的价值。

示例:

double i = 3.365737;
return i;

我希望返回值在点

之后精确为3

含义:返回值为3.365

另一个例子:

double i = 4644.322345;
return i;

我希望返回值为:4644.322

7 个答案:

答案 0 :(得分:6)

您想要的是在某个数字后截断十进制数字。您可以使用floor中的<math.h>函数轻松执行此操作(如果您使用的是C ++,则可以std::floor来自<cmath>):

double TruncateNumber(double In, unsigned int Digits)
{
    double f=pow(10, Digits);
    return ((int)(In*f))/f;
}

尽管如此,我认为在某些情况下,由于浮点内部的工作原理,你可能会得到一些奇怪的结果(最后一个数字是一个开/关)。


另一方面,大部分时间你只是按原样传递double并仅在流上输出时截断它,这是通过右流标记自动完成的。

答案 1 :(得分:2)

您需要注意边缘情况。完全基于pow和强制转换或fmod的任何实施都会偶尔产生错误的结果,尤其是基于pow(- PRECISION)的实施。

最安全的做法是实现C和C ++都不提供的东西:定点运算能力。缺乏这一点,您需要找到相关边界情况的表示。这个问题类似于Excel如何舍入的问题。在那里调整我的答案How does Excel successfully Rounds Floating numbers even though they are imprecise?

// Compute 10 to some positive integral power.
// Dealing with overflow (exponent > 308) is an exercise left to the reader.
double pow10 (unsigned int exponent) {
  double result = 1.0;
  double base = 10.0;
  while (exponent > 0) {
    if ((exponent & 1) != 0) result *= base;
    exponent >>= 1;
    base *= base;
  }
  return result;
}

// Truncate number to some precision.
// Dealing with nonsense such as nplaces=400 is an exercise left to the reader.
double truncate (double x, int nplaces) {
  bool is_neg = false;

  // Things will be easier if we only have to deal with positive numbers.
  if (x < 0.0) {
     is_neg = true;
     x = -x;
  }

  // Construct the supposedly truncated value (round down) and the nearest
  // truncated value above it.
  double round_down, round_up;
  if (nplaces < 0) {
    double scale = pow10 (-nplaces);
    round_down   = std::floor (x / scale);
    round_up     = (round_down + 1.0) * scale;
    round_down  *= scale;
  }
  else {
    double scale = pow10 (nplaces);
    round_down   = std::floor (x * scale);
    round_up     = (round_down + 1.0) / scale;
    round_down  /= scale;
  }

  // Usually the round_down value is the desired value.
  // On rare occasions it is the rounded-up value that is.
  // This is one of those cases where you do want to compare doubles by ==.
  if (x != round_up) x = round_down;

  // Correct the sign if needed.
  if (is_neg) x = -x;

  return x;
}

答案 2 :(得分:1)

你无法从双精度中“移除”精度。你可以:4644.322000。这是一个不同的数字,但精度是相同的。

正如@David Heffernan所说,当你把它转换成一个字符串进行显示时就这么做。

答案 3 :(得分:0)

您想要将您的双倍截断到n小数位,然后您可以使用此功能:

#import <cmath>

double truncate_to_places(double d, int n) {
    return d - fmod(d, pow(10.0, -n));
}

答案 4 :(得分:0)

您可以使用fmod函数在所需的精度之后找到数字,然后减去删除它们,而不是像其他答案那样乘以和除以10的幂。

#include <math.h>
#define PRECISION 0.001
double truncate(double x) {
    x -= fmod(x,PRECISION);
    return x;
}

答案 5 :(得分:0)

使用普通double s无法做到这一点,但你可以写一个class或只是struct喜欢

struct lim_prec_float {
  float value;
  int precision;
};

然后有你的功能

lim_prec_float testfn() {
  double i = 3.365737;
  return lim_prec_float{i, 4};
}

(4 = 1之前的点+ 3之后。这使用C ++ 11初始化列表,如果lim_prec_float是具有适当构造函数的class会更好。)

如果您现在想要输出变量,请使用自定义

执行此操作
std::ostream &operator<<(std::ostream &tgt, const lim_prec_float &v) {
  std::stringstream s;
  s << std::setprecision(v.precision) << v.value;
  return (tgt << s.str());
}

现在你可以,例如,

int main() {
  std::cout << testfn() << std::endl
            << lim_prec_float{4644.322345, 7} << std::endl;
  return 0;
}

将输出

3.366
4644.322

这是因为std::setprecision意味着舍入到所需的位数,这可能是你真正想要的。如果您实际上是指 truncate ,则可以使用其他答案给出的截断函数之一修改operator<<

答案 6 :(得分:0)

以同样的方式在显示之前格式化日期,你应该用double做同样的事。

但是,这里有两种用于舍入的方法。

double roundTo3Places(double d) {
    return round(d * 1000) / 1000.0;
}

double roundTo3Places(double d) {
    return (long long) (d * 1000 + (d > 0 ? 0.5 : -0.5)) / 1000.0;
}

后者更快,但数字不能大于9e15