Itzik984
Itzik984

Reputation: 16794

Returning double with precision

Say I have a method returning a double, but I want to determine the precision after the dot of the value to be returned. I don't know the value of the double varaible.

Example:

double i = 3.365737;
return i;

I want the return value to be with precision of 3 number after the dot

Meaning: the return value is 3.365.

Another example:

double i = 4644.322345;
return i;

I want the return value to be: 4644.322

Upvotes: 1

Views: 13460

Answers (7)

David Hammen
David Hammen

Reputation: 33126

You are going to need to take care with the borderline cases. Any implementation based solely on pow and casting or fmod will occasionally give wrong results, particularly so an implementation based on pow(- PRECISION).

The safest bet is to implement something that neither C nor C++ provide: A fixed point arithmetic capability. Lacking that, you will need to find the representations of the pertinent borderline cases. This question is similar to the question on how Excel does rounding. Adapting my answer there, How does Excel successfully Rounds Floating numbers even though they are imprecise? , to this problem,

// 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;
}

Upvotes: 2

Peter Lawrey
Peter Lawrey

Reputation: 533720

In the same way you format a date before displaying it, you should do the same with double.

However, here are two approaches I have used for rounding.

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;
}

The later is faster, however numbers cannot be larger than 9e15

Upvotes: 0

leftaroundabout
leftaroundabout

Reputation: 120741

There is no good way to do this with plain doubles, but you can write a class or simply struct like

struct lim_prec_float {
  float value;
  int precision;
};

then have your function

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

(4 = 1 before point + 3 after. This uses a C++11 initialization list, it would be better if lim_prec_float was a class with proper constructors.)

When you now want to output the variable, do this with a custom

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());
}

Now you can, for instance,

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

which will output

3.366
4644.322

this is because std::setprecision means rounding to the desired number of places, which is likely what you really want. If you actually mean truncate, you can modify the operator<< with one of the truncation functions given by the other answers.

Upvotes: 0

ughoavgfhw
ughoavgfhw

Reputation: 39925

Instead of multiplying and dividing by powers of 10 like the other answers, you can use the fmod function to find the digits after the precision you want, and then subtract to remove them.

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

Upvotes: 0

Matteo Italia
Matteo Italia

Reputation: 126867

What you want is truncation of decimal digits after a certain digit. You can easily do that with the floor function from <math.h> (or std::floor from <cmath> if you're using C++):

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

Still, I think that in some cases you may get some strange results (the last digit being one over/off) due to how floating point internally works.


On the other hand, most of time you just pass around the double as is and truncate it only when outputting it on a stream, which is done automatically with the right stream flags.

Upvotes: 6

orlp
orlp

Reputation: 117771

You want to truncate your double to n decimal places, then you can use this function:

#import <cmath>

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

Upvotes: 0

Steve Wellens
Steve Wellens

Reputation: 20640

You cannot "remove" precision from a double. You could have: 4644.322000. It's a different number but the precision is the same.

As @David Heffernan said do it when you convert it to a string for display.

Upvotes: 1

Related Questions