-1

I apologize for asking yet another rounding question. However, all the search has not yielded a satisfactory solution to my problem. The only possible answer is that what I am looking for may not be possible at all. Just wanted to make sure if the experts think the same.

So, here is my sample code:

double Round(double dbVal, int nPlaces)
{
    const double dbShift = pow(10.0, nPlaces);
    return  floor(dbVal * dbShift + 0.5) / dbShift; 
}    

main()
{
    string sNum = "1.29585";
    double dNum = stod(sNum);
    int iNumDecimals = 5;
    double dRoundedNum = Round(dNum, iNumDecimals);
}

The number sNum is read as a string from a file. For example, the number in the file is 1.29585. I convert it to double using stod. dNum comes out to be 1.295849999999.... I would like to get back 1.29585 in double. Using a Round function as shown above does not help. The round function also returns 1.295849999999....

Is it possible to get back the exact 1.29585 at all? Any other possible solution?

Thanks in advance for any advice.

  • Why convert it to a double then? – David Schwartz Dec 14 '13 at 05:18
  • Oh, I need to do tons of calculations after this. This is just a sample code to explain the problem in a simplified way. And, I read a whole bunch of numbers (zillions) from files and I need to do zillions of calculations with these numbers. That is why I am worried about the accuracy. – user3101198 Dec 14 '13 at 05:24
  • 1
    If you're so worried about accuracy, your first wrong move was deciding to use doubles. :P There will almost always be some rounding error -- even something as simple as `0.1` is really more like `0.099999999999999999523597175` (not the real value; i didn't feel like calculating). Only powers of two, and multiples thereof, can be safely stored without rounding error. – cHao Dec 14 '13 at 05:30
  • 1
    "I know this has been asked a number of times" - then ***why,*** seriously, why do you ask it again instead of searchihg for a duplicate, whereas you know exactly that dupes are forbidden? –  Dec 14 '13 at 06:02
  • Canonical answer: [What Every Computer Scientist Should Know About Floating-Point Arithmetic](http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html). – Jim Buck Dec 14 '13 at 05:14
  • I came across this before I posted. I am not a Computer Science person. I just program to get my work in some other field done. A brief layman's answer would probably help me better. So far, what I seem to gather is that what I am looking for is probably not possible. Just wanted a confirmation from the experts so I can move on to other aspects of my work. – user3101198 Dec 14 '13 at 05:21
  • `s/Computer Scientist/person who expects to mess with floating-point numbers without going insane/` :P – cHao Dec 14 '13 at 05:23

3 Answers3

3

Your number is rounded to the closest representable double to the number you provided ( 1.29585 ). To 17 places, it is: 1.29584999999999995

The next largest representable double-precision number is 1 / 252 larger than that: 1.29585000000000017.

That's roughly 1 part in 5 quadrillion. An error of that magnitude, scaled to the circumference of the entire solar system, would only be about 8 centimeters.

So, in terms of rounding, the double you have is correctly rounded to the nearest representable binary value.

By default, floating point numbers are stored in binary. Just as you can't express 1/3 as an exact decimal fraction (you can approximate it with "0.33333333", extending out the 3s until you get sick of it), you can't express all round decimal values exactly in binary.

If you're curious what the above two values look like in binary: (You can refer to the diagram and description here to understand how to interpret that hexadecimal value if you are interested.)

  • 1.29584999999999995 == 0x3FF4BBCD35A85879
  • 1.29585000000000017 == 0x3FF4BBCD35A8587A

For your zillions of calculations, this approximate representation should cause no problem, unless you're computing a series of values that need to be rounded to an exact number of decimal places. Typically, that's only necessary if you're computing actual bank transactions or the like. Bankers want decimal rounding, so that their computations today match the way computations were done 100 years ago so that they have continuity between the pre- and post-computer eras, not because they're magically more accurate.

Double precision arithmetic carries 16 to 17 decimal positions of precision. The fact that it doesn't print as a nice round number of decimal digits doesn't mean it's inaccurate. If you compare the calculation the computer makes with double precision to the same calculation you'd do by hand (even with the aid of a standard calculator displaying 9 to 12 digits of precision), the computer's double precision arithmetic will generally come out ahead.

What you most likely want to do is to make sure to print out your final calculations to the appropriate number of decimal places. For example, you can use std::setprecision() from <iomanip> to control the precision of values printed via std::cout.

EDIT: If your application actually requires decimal arithmetic and decimal rounding, then you will need to look into decimal floating point support, either built into the compiler or in a 3rd party library. Some recent compilers do have support for this, and some processors even have hardware support for decimal floating point. GCC has decimal floating point extensions, for example, as does Intel's compiler. In contrast, Microsoft suggests finding a BCD library, it seems.

Joe Z
  • 17,413
  • 3
  • 28
  • 39
  • Thanks Joe Z. Regarding your reference to "...actual bank transactions or the like...", this is actually a part of a financial application, hence the fixation with accuracy. I understand that maybe the number 1.29584999999.... is accurate enough. I will continue to examine and see if it is good enough. – user3101198 Dec 14 '13 at 05:55
  • @user3101198: I guess it really depends on the nature of the financial calculations you're performing. If you're performing a what-if analysis, Black-Sholes analysis, estimating returns on investments, or other forms of comparative or predictive analysis, then binary floating point is just fine. If you're computing an actual payment schedule or interest payout, then you might consider decimal arithmetic. See my edited answer above for some resources. – Joe Z Dec 14 '13 at 06:04
  • Thanks for mentioning about BCD library. I googled - looks like it will need further investment in time and effort. I will look into it later, if the calculations are not accurate enough. – user3101198 Dec 14 '13 at 06:35
  • @user3101198 : It's important to understand that decimal arithmetic isn't more accurate. It's just rounded differently. Decimal arithmetic is appropriate when you need your arithmetic rounded to whole decimal digit boundaries. Your selection between the two should be in terms of what sort of rounding is appropriate for your application. Both formats will round intermediate results, and decimal formats are typically _less_ accurate than the corresponding binary format. The difference is that decimal arithmetic rounds to decimal digit boundaries. – Joe Z Dec 14 '13 at 06:40
  • I don't really know about decimal arithmetic but if binary arithmetic is more accurate then I guess I will just stick to it. Rounding is not my concern. Accuracy after repeated mathematical operations is what I care about. I guess if it is not accurate enough I will just have to come back to the forum with another question. Thanks again. Good night. – user3101198 Dec 14 '13 at 06:53
  • @user3101198 : Hopefully my input has been helpful. If you consider your question answered, marking this answer as "accepted" will take it off the unanswered list. Thanks! – Joe Z Dec 14 '13 at 21:55
  • @JayZ Yes, Thanks very much. Your comments have been very helpful. I also appreciate everybody else's comments. – user3101198 Dec 15 '13 at 23:42
  • I am new to Stackoverflow and I did not know how to close the topic. Also, some people had put the topic "On Hold" so I thought that was the end of it. ... continued ..... – user3101198 Dec 15 '13 at 23:42
  • Although I disagree with their judgement it wasn't worth getting into unnecessary discussion. The very fact that I got so many inputs from wonderful people and my doubt got clarified is justification enough for my asking the question. Also, I did do plenty of research (as mentioned right at the outset) so the negative vote from somebody that I did not do research is also unjustified. I cannot be blamed for having a low IQ - that's what forums are for. I hope all those voters read this note so they are more careful in their judgements (of others) in future. – user3101198 Dec 15 '13 at 23:43
0

The "I whipped it up in Haskell and translated it" answer:

#include <iostream>
#include <cmath>
#include <iomanip>

using namespace std;

int main()
{
    double d = 1.2958499999999;

    cout << setprecision(15) << d << endl;
    cout << setprecision(15) << double(round(d * 1e5) / 1e5) << endl;

    return 0;
}

// outputs:
//   1.2958499999999
//   1.29585

It is hardly a general answer, but it is correct to the letter of the question. I highly recommend understanding the evil that is IEEE floating point using e.g. Jim Buck's reference rather than putting this hack to any great use.

Elliot Robinson
  • 1,384
  • 7
  • 16
  • Well, how do I do this in C++? – user3101198 Dec 14 '13 at 05:27
  • This is C++. Everything I'm aware of uses IEEE floating point, so it's a cross-language concept. I just translated it into running C++. – Elliot Robinson Dec 14 '13 at 05:31
  • Bear in mind that `setprecision(n)` will also round to `n` decimal digits, if you are only interested in printing to the screen. – Elliot Robinson Dec 14 '13 at 05:35
  • 1
    Just so you're warned, the number is still not *exactly* 1.29585. It's just close enough that `cout` doesn't care anymore. – cHao Dec 14 '13 at 05:35
  • where is the round function? I am using MS Visual C++ 2010 Express. I have the include file but it does not recognize the function "round". – user3101198 Dec 14 '13 at 05:39
  • I do not need to print the number. The number will be used inside the program. cout and setprecision won't help. – user3101198 Dec 14 '13 at 05:40
  • It's a C99 function, so you may need to include `math.h` instead. [`round`](http://www.cplusplus.com/reference/cmath/round/) was added to `cmath` in C++11. – Elliot Robinson Dec 14 '13 at 05:41
  • @ElliotRobinson Included "math.h". Still does not identify round. – user3101198 Dec 14 '13 at 05:45
  • Appears that it [isn't supported in the standard Windows libraries](http://stackoverflow.com/questions/485525/round-for-float-in-c). – Elliot Robinson Dec 14 '13 at 05:48
  • I'm not sure what you intend to round _to_. The number is correctly rounded to the nearest representable value. – Joe Z Dec 14 '13 at 05:49
  • Indeed. It's honestly a special case that it even worked out this way, hence my caveat. – Elliot Robinson Dec 14 '13 at 05:51
  • Thanks Everybody. I realize with everybody's help that this is as accurate as it can get. So I will continue checking if this is good enough for the kind of work I am doing. Based on all the explanations above, it might be good enough. Thanks again. I very much appreciate all the prompt and helpful responses. – user3101198 Dec 14 '13 at 06:05
0

Try this way, i don´t remember the format for double right now, i use float.

    float num;
sscanf("1.29585", "%f", &num);

std::cout << num << std::endl;
chfumero
  • 1,137
  • 2
  • 13
  • 26