3

I need a function that reduces the precision (number of digits) of my doubles.

I need it for calculation, not for output on the screen.

What I have so far, is:

double setDigits(double _number, int _digits)
{
    double tenth = pow((double)10,_digits);
    _number *= tenth;
    _number = floor(_number);
    _number /= tenth;

    return _number;
}

calling setDigits(sqrt(2),3) gives 1.4139999999999999, and not 1.414 as I want.

What can I do?

bjar-bjar
  • 737
  • 1
  • 8
  • 21
  • 1
    Is the difference of 1e-16 actually significant with your calculation? 1.414 doesn't have an exact binary representation, so it's never going to actually be 1.414 when you use it. So if an error of 1e-16 is unacceptable, you should go to quad precision. – tpg2114 Oct 26 '12 at 10:35
  • 2
    You should seriously consider how floating-point works, and whether this will work for you. There is no guarantee that you can actually represent the number you expect in binary floating-point. If you are working with money, for example, consider using only integers and storing cents instead of dollars. – tenfour Oct 26 '12 at 10:36
  • Where does `_decimals` come from? – Sergey Kalinichenko Oct 26 '12 at 10:42
  • The intention was to use it when comparing values, so instead of knowing the exact digits (some of my numbers defer at the 1E-7, and that kind of precision is not relevant), I would round them, for easier comparison.
    Using Floats solves my problem.
    – bjar-bjar Oct 26 '12 at 10:54

6 Answers6

5

What can I do?

Unfortunately, nothing for the root problem: in your platform, 1.414 has no exact double representation. You can't run a calculation with "1.414" because you can't place "1.414" anywhere in your double.

See for example http://www3.ntu.edu.sg/home/ehchua/programming/java/DataRepresentation.html .

What you can do is to keep your number with the maximum precision, and display it with reduced precision. You need to calculate machine precision and keep track of error during computation.

So you'll use 1.413999999999997 and at the end get an answer of, say, 41.99999137; which you'll display with

printf("The answer is %.3f\n", theAnswer);

Or you can change platform (compiler, or math library, or floating point representation, e.g. use long double where supported), but remember that you can then get 1.414 right at the price of getting, say, 1.873 wrong (have it as 1.87299999999 or 1.87300000001), and the calculation will have more or less the same errors.

You can work in integer arithmetic, multiplying the initial numbers by 1,000,000 (and getting 1414000) or another suitable scale, and then dividing at the end. Integers have a maximum bound, though.

There are also Arbitrary Precision Libraries that use a different internal representation and allow you to specify precision the way you want, for example GMP ( http://gmplib.org/ ). Of course, working with that is more difficult than specifying

op1 = 6.0;
op2 = 7.0;
theAnswer = op1 * op2;

and processing is slower as well, but results are good - or as good as you tell them to be.

LSerni
  • 55,617
  • 10
  • 65
  • 107
  • Very helpful, you made me realize that I didn't need the precision of a double. Changing all of my doubles to floats corrected the weird values. – bjar-bjar Oct 26 '12 at 11:18
  • 1
    OK, but remember that you have chosen the *change the platform* option; the effect you noticed *is still there*, just not with the same numbers. – LSerni Oct 26 '12 at 11:44
3

The following lines are invalid.

double tenth = pow((double)10,_decimals); //_decimals is not declared
_number = floor(_digits); //should be floor(_number)

The corrected function is

double setDigits(double _number, int _digits)
{
    double tenth = pow((double)10,_digits);
    _number *= tenth;
    _number = floor(_number);
    _number /= tenth;

    return _number;
}

Here is a demo.

abhshkdz
  • 6,335
  • 1
  • 21
  • 31
0
double setDigits(double _number, int _digits)
{
       double tenth = pow((double)10,_digits);
       int result = (_number * tenth) + 0.5; 
       double aux = result / tenth;

       return aux;
}

Try the following with tenth = 1000;

result = 1413,9999999999999 * 1000 +0,5
result = 1414,4......
result = 1414
dreamcrash
  • 47,137
  • 25
  • 94
  • 117
0

i don't know you may not check this answer but others do i am new in c++ but i found a way to do this

double Point::norm()

{

return (double)floor((sqrt(pow(px,2)+pow(py,2)+pow(pz,2))*1000))*0.001;

}

i use this but you can write your own math class to do this for you

roohi
  • 1
-1

Try this:

double setDigits(double _number, int _digits)
{
    double tenth = pow((double)10,_digits);
    return floor(_number * tenth + 0.5)/tenth;
}

std::cout<<setDigits(sqrt(2),3);

Output: 1.414

Denis Ermolin
  • 5,530
  • 6
  • 27
  • 44
  • @user1595420: it's just how floating point works. Numbers that look pretty do not store pretty and vice-versa. It's a problem of reading in decimal and storing in binary. They are just not that compatible for things like this. – tenfour Oct 26 '12 at 10:40
-1

You can create an intermediate class that manages data internally as int, but inputs and outputs as double:

class TruncatedDouble
{
private:
  int value_;

public:
  TruncatedDouble(double d) : value_((int)(double * 1000)) {}
  void SetValue(double d) {value_ = (int)(double * 1000);}
  double GetValue() {return (double)value_ / 1000;}
};

You have to overload all the usual operators too, but that's easy. Example:

TruncatedDouble& operator+=(const TruncatedDouble& rh) {value_ += rh.value_;}

And so on. This class will actually be quite fast, since you are operating with int instead of double, and you will never lose precision.

Gorpik
  • 10,940
  • 4
  • 36
  • 56
  • 1
    Um, yes, you'll lose precision. Any value with more than four digits to the right of the decimal point will be truncated, as the name of this class suggests. – Pete Becker Oct 26 '12 at 14:10
  • @PeteBecker: I mean that you will not lose precision due to rounding in operations, because you will be operating with `int`s. Losing whatever is more than four digits to the right of the decimal points is not a shortcoming but a requirement, according to the question. Of course, you can round instead of truncate; I have truncated for simplicity, but rounding is not much more difficult. – Gorpik Oct 28 '12 at 15:13
  • Yes, I was being a bit obtuse. Sorry. – Pete Becker Oct 28 '12 at 16:33