2

It will print 88.89 to the console because, 88.888 with a hundredths place rounding precision equals 88.89.

How do I make "b" equal to the value of "a" rounded to the hundredths place. (e.g. 88.89)

How do I make it so its like, | float b = "a" rounded to nearest hundredths place etc.

Basically how do I make a float equal to another float, but with lower precision

EXAMPLE:

a = 88.888
b = 88.89
c = 88.9

I don't want it to print to console, I just want these rounded values given to a variable because I require a rounded number in my program, and all the other numbers are 2 decimal places out. It would throw off the program if it was more than the hundredths place (banking software, we don't have a denomination past cents. We just need hundredths place basically).

#include<iostream>
#include<iomanip>
using namespace std;
        
int main(){
        
    float a = 88.888;
        
    cout << fixed << setprecision(2) << a << endl; 
    
    float b;
        
    return 0;
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • 5
    You can't set precision of a `float` or `double` variable. Only for the input/output of such a variable. But you shouldn't be using `float` or `double` for monetary values to begin with, due to the fact that floating-point values/math is inprecise. Use a non-floating data type instead, like `unsigned int`, to represent whole cents, not fractions of a dollar. Or, there are plenty of libraries that offer fixed-precision types for use with money values. – Remy Lebeau Dec 03 '21 at 22:22
  • 2
    So if I understood you correctly you always want exactly two decimal places, in that case I'd recommend using an integer and always `/ 100.0` when needing to output it. – Salvage Dec 03 '21 at 22:24
  • 2
    Related/dupe: [Best way to store currency values in C++](https://stackoverflow.com/questions/149033/) – Remy Lebeau Dec 03 '21 at 22:28
  • Depending on how serious you want to get with the solution here, checkout [C++ decimal data types](https://stackoverflow.com/a/17253823/2602718) – scohe001 Dec 03 '21 at 22:29
  • 2
    _"How do I make "b" ... rounded to the hundredths place"_ Taking that question literally, you can not. `float` and `double` types do not have a "hundredths place", as they use powers of 2 as their places, and no combination of powers of two can produce "a hundredth". It can only be approximated. – Drew Dormann Dec 03 '21 at 22:32
  • Possibly with `b = std::round(a * 100.0) / 100.0;`, although you'll still have the imprecision that comes with floating point types. – 1201ProgramAlarm Dec 03 '21 at 22:33
  • @DrewDormann: Whether `float` and `double` use powers of two is implementation-defined. Binary is common, but decimal formats exist and conform to the C and C++ standards. – Eric Postpischil Dec 04 '21 at 00:20
  • bilberto, Poor Stackoverflow etiquette to drastically change the question, especially after a good answer. Post rolled back. Post a new question for a different question. – chux - Reinstate Monica Jan 17 '22 at 02:20
  • Thanks to all for providing such indepth answers –  May 07 '22 at 01:16

3 Answers3

3

How do I make "b" equal to the value of "a" rounded to the hundredths place. (e.g. 88.89)

Depending on the particular target value, you cannot. For example, the number 88.89 is not representable in 32 bit IEEE-754 floating point format, so you simply cannot have a float with that value in that representation.

What you can have instead is a value that is very very close to it, such as 88.8899993896484375. If this is what you want, then it is achievable.

A simple solution is to use the string formatting facilities. The character streams have manipulator called std::setprecision, as you have shown. Simply convert the float to a string with desired precision, and then convert the string back to float.

Another solution is to use a bit of math. An intuitive and seemingly trivial solution would be:

std::round(a * 100.f) / 100.f;

There is a problem however. When the input value is near the threshold where direction of rounding changes, the lack of floating point precision can cause the input value to be on the wrong side for the purpose of the rounding direction.

For example, the closest representable value to 0.005 is actually 0.004999999888241291046142578125 which rounds down instead of up that we would have hoped from 0.005.

We can work around this by using one more decimal of precision and do an intermediate rounding:

std::round(
    std::round(a * 1000.f) / 10.f
) / 100.f;

banking software, we don't have a denomination past cents

I recommend to not use finite precision floating point for banking software. Use integers to represent cents when you don't want more precision. And when you do need more precision, use arbitrary precision arithmetic.

eerorika
  • 232,697
  • 12
  • 197
  • 326
1

It is perhaps not entirely clear what you require, but I assume:

float precision( float f, int places )
{
    float n = std::pow(10.0f, places ) ;
    return std::round(f * n) / n ;
}

Then given:

float a = 8.888 ;

The following:

float b = precision( a, 2 ) ;  // 8.89
float c = precision( a, 1 ) ;  // 8.9

However be clear that while the mathematical value of b for example is 8.89 and default standard output methods will print it as such, the stored value will be as near to 8.89 as the binary floating point representation allows. So for example while:

std::cout << a << '\n' ;
std::cout << b << '\n' ;
std::cout << c << '\n' ;

Outputs:

8.888
8.89
8.9

Setting the output precision reveals the limitation of representing decimal real values in binary floating point:

std::cout << std::setprecision(8) ;
std::cout << a << '\n' ;
std::cout << b << '\n' ;
std::cout << c << '\n' ;

outputs:

8.8879995
8.8900003
8.8999996

Which is fine and probably meets your requirements, but you may need to be aware to avoid errors. For example do not expect c == 8.9 to be true - Expect:

std::cout << ((c == 8.9) ? "equal\n" : "not equal\n") ;

to output not equal. In my test:

std::cout << ((c == 8.9f) ? "equal\n" : "not equal\n") ;

did outout equal; but I would not rely on that. In the first instance the converted c is implicitly converted to double, and in that conversion its value differs form the literal double 8.9.

That of course is a general rule not to compare a floating point value for exact equality. Instead you should:

std::cout << ((c - 8.9f < 0.01f) ? "equal\n" : "not equal\n") ;

For this reason, you would not generally use binary floating point for banking software. You would use decimal floating point. For that you would need library support or a different language. C# (and any .Net supported language) for example has a decimal type with at least 28 digits of precision.

Financial apps in general should not in any event round to the nearest cent in many cases - share prices and currency exchange rates for example are expressed in fractions of a cent. You would only want to round at the final result.

Suffice it to say that there are issues to resolve beside the simple rounding of the stored value (as opposed to rounding the presentation).

Clifford
  • 88,407
  • 13
  • 85
  • 165
  • 1
    `std::round(f * n) / n` is not a correct rounding method because `f * n` introduces an additional rounding error. – Eric Postpischil Dec 04 '21 at 00:19
  • @EricPostpischil : As I say; there are issues to resolve. Whether it matters may depend on the application. If it does matter it is likely that this is the wrong solution in any case. I'd be interested in your solution if you have one. I looked at it, but have spent more time on this than it is worth. As ever with floating point you fix one issue and another arises. – Clifford Dec 04 '21 at 00:34
  • 2
    Fixed point arithmetic is beyond easy to do in C and does not require any third-party library. Also, financial data is typically maintained in 100ᵗʰ of a cent → that is, one cent is the integer value 100 — one dollar is the value 10000. – Dúthomhas Jan 17 '22 at 02:54
  • @Dúthomhas - agreed (although this is a C++ question) - that is how I'd do it - probably with 64 bit integers. Post an answer perhaps. Here C++ is actually an advantage since you can have a "currency" class with operator overloading to do the atithmentic, and perhaps an `ostream::operator<<` overload to present it. – Clifford Jan 18 '22 at 13:58
0

Banking software should never store money using floating point.

I will not go into details here, but you can read more than you would think there is to know in:

Use fixed-point arithmetic

The idea is as simple as it gets: instead of using floating-point, use integers and simply multiply by some fixed value.

For banking, compute at 100ths of a cent, or 10,000ths of a dollar. Thus:

long one_dollar = 10000;  // == 100 cents == 10000 100ths of a cent

To display that dollar, you can simply divide using floating-point arithmetic:

std::cout << std::fixed << std::setprecision(2) << (one_dollar / 10000.0);

If you need to be specific (for example, not letting 0.006 round up to display as 0.01 to your clients, making them believe they have a whole cent when they do not), make sure to get rid of the sub-cent values first:

std::cout << std::fixed << std::setprecision(2) << ((one_dollar / 100) / 100.0);

All other useful operations (addition, subtraction, multiplication, division) work the same as ever.

For your needs

There is no requirement that fixed-point values be stored in multiples of 10000, or even 100. Any multiplier will do. You want to store as pennies? Do that. Easy, peasy, done.

Since you are using C++, you can even write yourself a simple class to make a fixed-point number act like any other numeric type, but that is probably much more than you need for the purposes of your assignment.

If I were you, I’d just use an integer, and create a couple of functions to convert between cents and dollars.

double to_dollars( long cents );
long from_dollars( double dollars );

Thereafter treat every monetary amount as the number of cents you have.

Dúthomhas
  • 8,200
  • 2
  • 17
  • 39