### Truncating a float to a specified number of decimal digits

Pages: 12 The ios.precision(n) function rounds a floating-point value rather than truncating it.

To truncate a float to an int, we can, of course, cast it to an int.
We can also use the trunc() function in the [itex] library for this purpose.

However, if we want to truncate (rather than round) a float to a specified number of digits, how can we do so?

eg: the float is 1234.5678. I would like to output it truncated to 2 decimal digits, which should give 1234.56.

Using ios.precision(6) would round it as 1234.57.
Last edited on Can a floating-point value truly be "truncated"? - judge for yourself:

 ``12345678910111213`` ``````#include #include using namespace std; int main() { double x = 1234.5678; x = (int)( 100 * x ) / 100.0; cout.setf( ios::fixed ); for ( int i = 2; i <= 16; i++ ) cout << setprecision( i ) << x << endl; }``````

 ```1234.56 1234.560 1234.5600 1234.56000 1234.560000 1234.5600000 1234.56000000 1234.560000000 1234.5600000000 1234.56000000000 1234.560000000000 1234.5599999999999 1234.55999999999995 1234.559999999999945 1234.5599999999999454``` ``12345678910111213141516171819202122232425262728293031323334353637383940`` ``````#include #include #include #include #include #include // return { value after truncation, true } if truncation was successful // return { approximate value after truncation, false } if precision is insufficient std::pair< double, bool > trunc_n( double value, std::size_t digits_after_decimal = 0 ) { static constexpr std::intmax_t maxv = std::numeric_limits::max() ; static constexpr std::intmax_t minv = std::numeric_limits::min() ; unsigned long long multiplier = 1 ; for( std::size_t i = 0 ; i < digits_after_decimal ; ++i ) multiplier *= 10 ; const auto scaled_value = value * multiplier ; const bool did_trunc = scaled_value != scaled_value+0.5 && scaled_value != scaled_value-0.5 ; if( scaled_value >= minv && scaled_value <= maxv ) return { double( std::intmax_t(scaled_value) ) / multiplier, did_trunc } ; else return { std::trunc(scaled_value) / multiplier, did_trunc } ; } int main() { for( double value : { -1234567890123.555555, -1234567890.555555, 1234567890.555555, 1234567890123.555555 } ) { std::cout << '\n' << std::fixed << std::setprecision(6) << value << '\n' ; for( int ndigits = 2 ; ndigits <= std::cin.precision() ; ++ndigits ) { const auto result = trunc_n(value,ndigits) ; std::cout << std::setprecision(ndigits) << result.first ; if( !result.second ) std::cout << " *** insuffiicient floating point precision ***" ; std::cout << '\n' ; } } }``````

http://coliru.stacked-crooked.com/a/ccd091946576f69a Can a floating-point value truly be "truncated"?

I already provided an example of what I want to do:

If a float holds 1234.5678, truncation to 2 decimal digits would be 1234.56. That's what I want the OP to be.

What I wanted to know is if there is a library function for this purpose. I get the message - there isn't.

So to do this, we would need to write a custom function, which can be done. > If a float holds 1234.5678, truncation to 2 decimal digits would be 1234.56.

I think that what lastchance was trying to point out is that there may not be a floating point representation for the precise mathematical value 1234.56 Here's my function for this purpose. It's a Quick and dirty job, and error checking hasn't been built-in, but I've tested it and it seems OK:

 ``123456789101112131415161718192021222324252627282930`` ``````template F trunc_decs(const F& f, int decs) { int i1 = floor(f); F rmnd = f - i1; int i2 = static_cast (rmnd * pow(10, decs)); F f1 = i2 / pow(10, decs); return i1 + f1; } int main() { cout << "trunc_decs(1234.5678, 3) = " << setprecision(7) /// else will round << trunc_decs(1234.5678, 3) << endl; cout << "trunc_decs(1234.5678, 2) = " << trunc_decs(1234.5678, 2) << endl; cout << "trunc_decs(1234.5678, 1) = " << trunc_decs(1234.5678, 1) << endl; cout << "trunc_decs(1234.5678, 0) = " << trunc_decs(1234.5678, 0) << endl; return 0; }`````` I would like to output it truncated

If it's just for outputting:

 ``1234567891011121314151617181920212223242526`` ``````#include #include using namespace std; string truncate(float val, int numDigits) { std::string output = std::to_string(val).substr(0, numDigits+1); if (output.find('.') == string::npos || output.back() == '.') { output.pop_back(); } return output; } int main() { float value = 1234.5678; cout << truncate(value, 6) << '\n'; cout << truncate(value, 5) << '\n'; cout << truncate(value, 4) << '\n'; cout << truncate(value, 3) << '\n'; cout << truncate(value, 2) << '\n'; }``````

or as a float:
 ``1234567891011121314151617181920212223242526`` ``````#include #include using namespace std; float truncate(float val, int numDigits) { std::string output = std::to_string(val).substr(0, numDigits+1); if (output.find('.') == string::npos || output.back() == '.') { output.pop_back(); } return stof(output); } int main() { float value = 1234.5678; cout << truncate(value, 6) << '\n'; cout << truncate(value, 5) << '\n'; cout << truncate(value, 4) << '\n'; cout << truncate(value, 3) << '\n'; cout << truncate(value, 2) << '\n'; }``````
Last edited on Very good. Now, for extra credit, change (in the post two above this) your line
` << setprecision(7) /// else will round `
to
` << fixed << setprecision(16) /// else will round `
and observe what happens (once you've included all the headers).

 ```trunc_decs(1234.5678, 3) = 1234.5670000000000073 trunc_decs(1234.5678, 2) = 1234.5599999999999454 trunc_decs(1234.5678, 1) = 1234.5000000000000000 trunc_decs(1234.5678, 0) = 1234.0000000000000000```

When you have done that, change the number to 1234.4321 and used fixed and precision(16) manipulators.
Last edited on Typical values around 1234.56 which can be represented as a double
(typical: std::numeric_limits<double>::is_iec559 == true)

 ``1234567891011121314151617181920`` ``````#include #include #include #include int main() { const double value = 1234.56 ; constexpr auto N = std::numeric_limits::digits10 + 1 ; std::cout << std::boolalpha << "iec559? " << std::numeric_limits::is_iec559 << '\n' << std::fixed << std::setprecision(N) << "representable numbers around 1234.56\nto about " << N << " digits after the decimal point\n" ; const double a_lower_value = std::nexttoward( std::nexttoward( value, value-1 ), value-1 ) ; const double a_higher_value = std::nexttoward( std::nexttoward( value, value+1 ), value+1 ) ; for( double v = a_lower_value ; v <= a_higher_value ; v = std::nexttoward( v, v+1 ) ) std::cout << v << '\n' ; }``````

 ```iec559? true representable numbers around 1234.56 to about 16 digits after the decimal point 1234.5599999999994907 1234.5599999999997181 1234.5599999999999454 1234.5600000000001728 1234.5600000000004002```

http://coliru.stacked-crooked.com/a/fce759083e73a209
http://rextester.com/VDD84140 trunc_decs(1234.5678, 3) = 1234.5670000000000073 trunc_decs(1234.5678, 2) = 1234.5599999999999454 trunc_decs(1234.5678, 1) = 1234.5000000000000000 trunc_decs(1234.5678, 0) = 1234.0000000000000000

And does this happen when rounding (rather than truncation) is done as well?

I think the behavior you have recorded has nothing to do with truncation per se. The point is that a floating-point number like 1234.56 cannot be stored precisely. Consequently, "truncation" is meaningless.

As @JLBorges points out, there are actually several distinct numbers which would be printed out as 1234.56 to a moderate precision. Which particular one of those numbers would you like any trunc() function to return?

Try this program - absolutely no truncation needed. x is already assigned the value that you want. You can't even guarantee which side of 1234.56 the higher-precision values would fall, so writing to strings won't help you.

 ``12345678910`` ``````#include #include using namespace std; int main() { double x = 1234.56; cout.setf( ios::fixed ); for ( int i = 2; i <= 16; i++ ) cout << setprecision( i ) << x << endl; }``````
Last edited on > The point is that a floating-point number like 1234.56 cannot be stored precisely.
> Consequently, "truncation" is meaningless.

Truncation of a floating point value is definitely not meaningless.

Most floating point values are approximations of a real number. The result of a truncation is both meaningful and useful if it is as close as possible (actually as close as needed) to the mathematical ideal of the truncated real number. This is true of the result of any floating point computation: std::sin() or 1.0/3.0 are not meaningless just because the result may not be precise representation of the actual value with infinite precision. Absolute mathematical precision to N/finity digits is often both meaningless and misleading. It is by applying a value of precision by which measurements gain value. Whenever scientists do any math the precision is very much a part of the value of any computation. Adding precision is just sloppy.

The issue is that radix=10 and radix=2 are not digit-for-digit compatible, especially when you start playing with fractional parts. So knowing how it makes a difference at all is another level of care you must have when dealing with computers.

What Every Computer Scientist Should Know About Floating-Point Arithmetic
http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html closed account (48T7M4Gy)
Quite a good article.
Last edited on > Exactly as @lastchance wrote x = (int)( 100 * x ) / 100.0;

Try this:
 ``12345678910111213`` ``````#include #include #include #include int main() { double x = std::numeric_limits::max() * 2.0 ; ; std::cout << std::fixed << std::setprecision(2) << x << '\n' ; x = (int)( 100 * x ) / 100.0 ; // undefined behaviour std::cout << std::fixed << std::setprecision(2) << x << '\n' ; }``````

http://coliru.stacked-crooked.com/a/b45777673d4d4ae8
Last edited on closed account (48T7M4Gy)
the difficulty is representing decimals given computers are fundamentally binary.
Last edited on closed account (48T7M4Gy)
BCD is essentially a part of any problem here.
Last edited on > because of the 13-14 digit limitation

Try this (just 8 digits):
 ``123456789101112`` ``````#include #include #include int main() { double x = 90'000'000.0 ; // a mere 8 significant digits std::cout << std::fixed << std::setprecision(2) << x << '\n' ; x = (int)( 100 * x ) / 100.0 ; // undefined behaviour on this implementation std::cout << std::fixed << std::setprecision(2) << x << '\n' ; }``````

http://coliru.stacked-crooked.com/a/c0c88d61ef46b7ce

> the algorithm @lastchance has presented would be 'unreliable' or at least prone to error in the last few digits

The "algorithm", very reliably, engenders undefined behaviour.
Last edited on closed account (48T7M4Gy)
.
Last edited on closed account (48T7M4Gy)
.
Last edited on
Pages: 12