0

I want to calculate the lower limit bound for given price(2.65 * 0.9)

  2.65 * (1 - 0.1) = 2.385

then round it to 2 decimal places, the result 2.385 -> 2.39

  round(2.385) = 2.39

but when I use C++ to implement above:

double pclose = 2.65;
double range = 0.1;
double lmtdown = pclose * (1 - range);
double lmtdownAfterRound= std::round( lmtdown * 100 ) / 100;

printf("pclose=%.2f, range=%.2f, limit down before round=%f,\n", pclose, range, lmtdown);
printf("limit after round=%f\n", lmtdownAfterRound);

but the result printed is 2.38!!!!!! instead of 2.39 I wanted

pclose=2.65, range=0.10, limit down before round=2.385000,
limit after round=2.380000

I have tried anohter round method with std::floor:

lmtdownAfterRound= std::floor(lmtdown * 100 + 0.5) / 100;
printf("limit after round =%f\n", lmtdownAfterRound);

the result is still 2.38:

limit after round with floor=2.380000

is there something wrong in my c++ code, How can I get the correct round value?

Lan
  • 176
  • 2
  • 8
  • 2
    Seems likely to be a binary precision issue.My guess is that the double representation of 0.1 is actually very slightly greater than 1/10th, meaning that lmtdown is actually very very slightly less than 2.385, meaning it gets rounded down to 2.38 not up to 2.39. For instance, if you change pclose to 2.650000001, it correctly rounds it up to 2.39. – Nathan Pierson Aug 15 '20 at 04:04
  • @John3136 thanks, I understand the problem, the 2.385 in computer maybe 2.3849999999994 which cause the problem. I just want to seek an accurate common round double to two decimal places implementation in C++(which can round 0.4999999999 to 1.). otherwize my program will not work. – Lan Aug 15 '20 at 04:36
  • 1
    @Lan -- *I want to calculate the lower limit bound for given price(2.65 * 0.9)* -- *otherwize my program will not work* -- Do not use `double` or floating point for anything to do with monetary calculations. Use either integers or an arbitrary precision library. Many financial institutions prohibit usage of floating point when calculating monetary values, all due to what you're seeing now. – PaulMcKenzie Aug 15 '20 at 05:16
  • 1
    There is no such thing as rounding a `double` to any number of decimal places greater than zero. Floating-point doesn't have decimal places, it has binary places, and the two are incommensurable. If you want decimal digits you have to use a decimal radix, e.g. by printing, or converting to a string. – user207421 Aug 15 '20 at 05:26
  • @PaulMcKenzie , and Marquis of Lorne, Thanks, Got it! ! – Lan Aug 15 '20 at 05:42

1 Answers1

0

Use std::ceil/std::floor

Working code

// Example program
// Example program
#include <iostream>
#include <cmath>
int main() {
double pclose = 2.65;
double range = 0.1;
double lmtdown = pclose * (1.0 - range);
int left = int(lmtdown *1000) %10;

double lmtdownAfterRound;
if (left >=5)
lmtdownAfterRound=std::ceil( lmtdown *100.  )/100.;
else
lmtdownAfterRound=std::floor( lmtdown *100.  )/100.;

printf("pclose=%.2lf, range=%.2lf, limit down before round=%lf,\n", pclose, range, lmtdown);
printf("limit after round=%lf\n", lmtdownAfterRound);
}

Output:

pclose=2.65, range=0.10, limit down before round=2.385000,
limit after round=2.390000

Test it here: http://cppshell.com/

  • 1
    This will in general have different behavior. It will correctly round 2.385 up to 2.39, but it'll also round up 2.381 to 2.39 instead of rounding it down to 2.38. – Nathan Pierson Aug 15 '20 at 04:09
  • @NathanPierson Resolved,added a check to decide whether to use ceil or floor –  Aug 15 '20 at 04:34
  • 1
    I'm sure due to the vagaries of floating point there will be plenty of cases where this workaround fails, if you want precision don't use floating point – Alan Birtles Aug 15 '20 at 05:27
  • @Jets Try your code with `pclose = 1.65` at the same link. You'll get `limit down before round=1.485000`, `limit after round=1.480000`. So no, the code is not working, because it can't possibly work in all cases. – dxiv Aug 15 '20 at 06:03