count digit afer decimal point

Pages: 123
If you enter 0.24 as the input, the float will store (if it's a 32 bit float) 0.23999999463558197021484375

Multiply that by 100. You get 23.999999463558197021484375, except that the float can't hold that value. It will hold a value close to it.

What's the closest floating point representation to that? 24

The number 24 CAN be exactly represented.

Nwb, you can start playing around with floating point numbers yourself ( https://www.h-schmidt.net/FloatConverter/IEEE754.html ) and work out what you particular system is doing. Just remember, there are no guarantees on the accuracy of floating point computation in C++.
@Peter round works only for integers, so I am potentially losing an important decimal point if I use the function for round(). (How do I know when to round? and what if the user inputted 2.99!?)

Are we still trying to solve the same problem as at the beginning of this page the previous page (page 2)? If so, I don't think floating point numbers should be used at all.

how is it that when you ran my program you got 23 but when you tried that bit of code you got 24?

I don't know. Did you read the link that Repeater posted about "Floating-Point Determinism"? I don't think it is very useful to try and understand exactly what happens with floating point values. Just accept that they are approximations with rounding errors and write your code accordingly. If you need exact calculations use integers (or types built on top of integers).
Last edited on
Yes, what Peter87 said.

We're riffing on the theme of how might one particular case be working to produce the output we're seeing, but just for the fun of it.

Changing anything at all could produce different outputs. Floating point sacrifices accuracy.

If you NEED the accuracy, if you NEED the user to be able to enter 0.24 and for the value 0.24 to be exactly represented with a datatype, then you NEED to use a datatype that can do that - NOT floating point.

@nwd, trying to understand what's going on inside the processor and the running executable is fun and educational, but the lesson here is NOT that there is some way to make floating point perfectly accurate and reliable. There isn't. That's not what it's for.

But @Peter why did you get two different answers when you did the same computation! Are you saying that it's random in that it could be both 23 or 24?

How float is calculated is by binary, but how it is rounded off is dependont on the compiler? Is that how this works? Or all compilers use the same standard? I still donno how float round works but I guess it doesn't matter at this point..

At this point I'm just curious I'm not trying to use floating point arithmetic to find out how many decimals an inputted number has.. anymore.. lol ;)
why did you get two different answers when you did the same computation!

If I change your program to use double instead of long double I do get the correct result.

Also note that multiplying by 100 once does not necessarily give the same result as multiplying by 10 twice.

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
#include <iomanip>

int main()
{
	long double value;
	std::cin >> value;
	std::cout << std::setprecision(100);
	std::cout << (value * 100) << '\n';
	std::cout << (value * 10 * 10) << '\n';
}

input:
  0.24
output:
  24
  23.99999999999999999826527652402319290558807551860809326171875
Last edited on
As an aside, note that there also exist functions in the standard library to give the next-highest or lowest double.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Example program
#include <iostream>
#include <cmath>
#include <limits>
#include <iomanip>

int main()
{
    long double original = 0.24;
    long double n = original * 10 * 10; // produces more rounding error

    std::cout << std::setprecision(100);
    std::cout << n << '\n';
	
    long double next = std::nextafter(n, std::numeric_limits<decltype(n)>::max());
    while (next <= 24.0)
    {
        std::cout << next << '\n';
        next = std::nextafter(next, std::numeric_limits<decltype(n)>::max());
    }
}


23.99999999999999911182158029987476766109466552734375
23.99999999999999911355630377585157475550659000873565673828125
23.9999999999999991152910272518283818499185144901275634765625
23.99999999999999911702575072780518894433043897151947021484375
23.999999999999999118760474203781996038742363452911376953125
23.99999999999999912049519767975880313315428793430328369140625
23.9999999999999991222299211557356102275662124156951904296875
23.99999999999999912396464463171241732197813689708709716796875
23.99999999999999912569936810768922441639006137847900390625
23.99999999999999912743409158366603151080198585987091064453125
23.9999999999999991291688150596428386052139103412628173828125
23.99999999999999913090353853561964569962583482265472412109375
23.999999999999999132638262011596452794037759304046630859375
23.99999999999999913437298548757325988844968378543853759765625
23.9999999999999991361077089635500669828616082668304443359375
23.99999999999999913784243243952687407727353274822235107421875
23.9999999999999991395771559155036811716854572296142578125
23.99999999999999914131187939148048826609738171100616455078125
23.9999999999999991430466028674572953605093061923980712890625
23.99999999999999914478132634343410245492123067378997802734375
23.999999999999999146516049819410909549333155155181884765625
23.99999999999999914825077329538771664374507963657379150390625
...
23.9999999999999999757138713363247006782330572605133056640625
23.99999999999999997744859481230150777264498174190521240234375
23.999999999999999979183318288278314867056906223297119140625
23.99999999999999998091804176425512196146883070468902587890625
23.9999999999999999826527652402319290558807551860809326171875
23.99999999999999998438748871620873615029267966747283935546875
23.99999999999999998612221219218554324470460414886474609375
23.99999999999999998785693566816235033911652863025665283203125
23.9999999999999999895916591441391574335284531116485595703125
23.99999999999999999132638262011596452794037759304046630859375
23.999999999999999993061106096092771622352302074432373046875
23.99999999999999999479582957206957871676422655582427978515625
23.9999999999999999965305530480463858111761510372161865234375
23.99999999999999999826527652402319290558807551860809326171875
24
Last edited on
Peter you said that if you write 0.24L you would get 24. So just curious, how do I force the compiler to take input as long double and not double and then convert to long double?

@ganado that's cool. Maybe I could use that! Thanks a lot!
Last edited on
how do I force the compiler to take input as long double and not double

When reading input using std::cin you don't need to do anything special. Just use the >> operator like you normally would.
But that's what I did... But you got 23 and not 24?

Temp_floating - truncate(temp_floating) should return 0 if the Value of temp is 24 or - 1 if its 23 with decimal places. You got - 1..

Oh or is it because it is displayed as 24 by really it is 23 with decimals.. Oh right I forgot about that int of 24.0 gave 23.. That makes sense

So floating points are never promoted just demoted..?
Last edited on
I was thinking.. How about we use the previous logic.. And if the value still have floating point after the while loop, we directly multiply the input number by 10^8 (to avoid flooding rounding errors) call nextafter() once, check for floating point and put that in loop to count how many nexafters it took to lose the floating point.

Using this data could estimate how many nextafters it takes for a legit input to get to its integer representation. Remember that all integers can be represented in binary.

> we directly multiply the input number by 10^8 (to avoid flooding rounding errors) call nextafter() once,
> check for floating point and put that in loop to count how many nexafters it took to lose the floating point.

The ULP may already be greater than one when we multiply the input number by 10^8.

Assuming that std::numeric_limits<double>::is_iec559 is true (IEEE 754: true for all current floating-point hardware), the IEEE 754 guarantee is only that a floating point computation is accurate to within half ULP.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <iomanip>
#include <boost/math/special_functions/ulp.hpp>
#include <cmath>

template < typename T > void print_ulps()
{
    T v = T(1.e8) ;
    for( int i = 0 ; i < 16 ; ++i )
    {
        std::cout << "value: " << v
                  << "  ulp: " << std::llround( boost::math::ulp(v) ) << '\n' ;
        v *= 10 ;
    }
}

#define PRINT_ULPS(T) { std::cout << "\n\n" << #T << ":\n----------\n" ; print_ulps<T>() ; }

int main()
{
    PRINT_ULPS(float)
    PRINT_ULPS(double)
    PRINT_ULPS(long double)
}

echo && echo && g++ -std=c++17 -O3 -Wall -Wextra -pedantic-errors main.cpp && ./a.out
echo && echo =========== && echo 
clang++ -std=c++17 -stdlib=libc++ -O3 -Wall -Wextra -pedantic-errors main.cpp -lsupc++ && ./a.out




float:
----------
value: 1e+08  ulp: 8
value: 1e+09  ulp: 64
value: 1e+10  ulp: 1024
value: 1e+11  ulp: 8192
value: 1e+12  ulp: 65536
value: 1e+13  ulp: 1048576
value: 1e+14  ulp: 8388608
value: 1e+15  ulp: 67108864
value: 1e+16  ulp: 1073741824
value: 1e+17  ulp: 8589934592
value: 1e+18  ulp: 68719476736
value: 1e+19  ulp: 1099511627776
value: 1e+20  ulp: 8796093022208
value: 1e+21  ulp: 70368744177664
value: 1e+22  ulp: 1125899906842624
value: 1e+23  ulp: 9007199254740992


double:
----------
value: 1e+08  ulp: 0
value: 1e+09  ulp: 0
value: 1e+10  ulp: 0
value: 1e+11  ulp: 0
value: 1e+12  ulp: 0
value: 1e+13  ulp: 0
value: 1e+14  ulp: 0
value: 1e+15  ulp: 0
value: 1e+16  ulp: 2
value: 1e+17  ulp: 16
value: 1e+18  ulp: 128
value: 1e+19  ulp: 2048
value: 1e+20  ulp: 16384
value: 1e+21  ulp: 131072
value: 1e+22  ulp: 2097152
value: 1e+23  ulp: 16777216


long double:
----------
value: 1e+08  ulp: 0
value: 1e+09  ulp: 0
value: 1e+10  ulp: 0
value: 1e+11  ulp: 0
value: 1e+12  ulp: 0
value: 1e+13  ulp: 0
value: 1e+14  ulp: 0
value: 1e+15  ulp: 0
value: 1e+16  ulp: 0
value: 1e+17  ulp: 0
value: 1e+18  ulp: 0
value: 1e+19  ulp: 1
value: 1e+20  ulp: 8
value: 1e+21  ulp: 64
value: 1e+22  ulp: 1024
value: 1e+23  ulp: 8192

===========



float:
----------
value: 1e+08  ulp: 8
value: 1e+09  ulp: 64
value: 1e+10  ulp: 1024
value: 1e+11  ulp: 8192
value: 1e+12  ulp: 65536
value: 1e+13  ulp: 1048576
value: 1e+14  ulp: 8388608
value: 1e+15  ulp: 67108864
value: 1e+16  ulp: 1073741824
value: 1e+17  ulp: 8589934592
value: 1e+18  ulp: 68719476736
value: 1e+19  ulp: 1099511627776
value: 1e+20  ulp: 8796093022208
value: 1e+21  ulp: 70368744177664
value: 1e+22  ulp: 1125899906842624
value: 1e+23  ulp: 9007199254740992


double:
----------
value: 1e+08  ulp: 0
value: 1e+09  ulp: 0
value: 1e+10  ulp: 0
value: 1e+11  ulp: 0
value: 1e+12  ulp: 0
value: 1e+13  ulp: 0
value: 1e+14  ulp: 0
value: 1e+15  ulp: 0
value: 1e+16  ulp: 2
value: 1e+17  ulp: 16
value: 1e+18  ulp: 128
value: 1e+19  ulp: 2048
value: 1e+20  ulp: 16384
value: 1e+21  ulp: 131072
value: 1e+22  ulp: 2097152
value: 1e+23  ulp: 16777216


long double:
----------
value: 1e+08  ulp: 0
value: 1e+09  ulp: 0
value: 1e+10  ulp: 0
value: 1e+11  ulp: 0
value: 1e+12  ulp: 0
value: 1e+13  ulp: 0
value: 1e+14  ulp: 0
value: 1e+15  ulp: 0
value: 1e+16  ulp: 0
value: 1e+17  ulp: 0
value: 1e+18  ulp: 0
value: 1e+19  ulp: 1
value: 1e+20  ulp: 8
value: 1e+21  ulp: 64
value: 1e+22  ulp: 1024
value: 1e+23  ulp: 8192

http://coliru.stacked-crooked.com/a/f13846f91dbe8af4
Last edited on
Elaborate please? I don't get what ULP is. Or is_iec559

Are you saying that it loses precision by then? What if we multiply only until the number is 8digits (that's what I meant initially but I see that I phrased it wronlgy).

If it still had floating point or if number itself is greater than 8 digits, we prompt that the number is not supported.

Is 8 digits the maximum number I can take without losing precision for long double?
I want to support as many digits as I can as long as I'm accurate.. Also position of the point doesn't affect the precision does it? Only number of digits right?
Last edited on
But that's what I did... But you got 23 and not 24?

In one of your posts you assigned 0.24 to a long double and got 23 because you were mixing double and long double.
http://www.cplusplus.com/forum/beginner/244856/2/#msg1083998


Temp_floating - truncate(temp_floating) should return 0 if the Value of temp is 24 or - 1 if its 23 with decimal places. You got - 1..

Yes, but this had nothing to do with conversion from double to long double. If I changed the type to double it worked correctly. It was when long double was used that it gave me trouble, but only when the number was multiplied with 10 twice instead of 100 once.
http://www.cplusplus.com/forum/beginner/244856/3/#msg1084156
Last edited on
> Are you saying that it loses precision by then?

Yes. In simple terms, the value of the ULP (Unit of Least Precision) is the distance between a representable floating point number and the next higher representable floating point number.

If the ULP is 8 for a floating point number x, the next representable floating point number is x+8
ie. x+1, x+2 etc. can't be represented.

Here is an example, where the ULP is 8:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <iomanip>
#include <boost/math/special_functions/ulp.hpp>

int main()
{
    float f = 100'000'000 ;

    std::cout << std::fixed << std::setprecision(0) ;

    std::cout << "value: " << f << "  ulp: " << boost::math::ulp(f) << '\n' ;

    for( int i = 0 ; i < 32 ; ++i )
        std::cout << f << " + " << std::setw(2) << i << " yields " << f+i << '\n' ;
}

echo && echo && g++ -std=c++17 -O3 -Wall -Wextra -pedantic-errors main.cpp && ./a.out
echo && echo =========== && echo 
clang++ -std=c++17 -stdlib=libc++ -O3 -Wall -Wextra -pedantic-errors main.cpp -lsupc++ && ./a.out


value: 100000000  ulp: 8
100000000 +  0 yields 100000000
100000000 +  1 yields 100000000
100000000 +  2 yields 100000000
100000000 +  3 yields 100000000
100000000 +  4 yields 100000000
100000000 +  5 yields 100000008
100000000 +  6 yields 100000008
100000000 +  7 yields 100000008
100000000 +  8 yields 100000008
100000000 +  9 yields 100000008
100000000 + 10 yields 100000008
100000000 + 11 yields 100000008
100000000 + 12 yields 100000016
100000000 + 13 yields 100000016
100000000 + 14 yields 100000016
100000000 + 15 yields 100000016
100000000 + 16 yields 100000016
100000000 + 17 yields 100000016
100000000 + 18 yields 100000016
100000000 + 19 yields 100000016
100000000 + 20 yields 100000016
100000000 + 21 yields 100000024
100000000 + 22 yields 100000024
100000000 + 23 yields 100000024
100000000 + 24 yields 100000024
100000000 + 25 yields 100000024
100000000 + 26 yields 100000024
100000000 + 27 yields 100000024
100000000 + 28 yields 100000032
100000000 + 29 yields 100000032
100000000 + 30 yields 100000032
100000000 + 31 yields 100000032

===========

value: 100000000  ulp: 8
100000000 +  0 yields 100000000
100000000 +  1 yields 100000000
100000000 +  2 yields 100000000
100000000 +  3 yields 100000000
100000000 +  4 yields 100000000
100000000 +  5 yields 100000008
100000000 +  6 yields 100000008
100000000 +  7 yields 100000008
100000000 +  8 yields 100000008
100000000 +  9 yields 100000008
100000000 + 10 yields 100000008
100000000 + 11 yields 100000008
100000000 + 12 yields 100000016
100000000 + 13 yields 100000016
100000000 + 14 yields 100000016
100000000 + 15 yields 100000016
100000000 + 16 yields 100000016
100000000 + 17 yields 100000016
100000000 + 18 yields 100000016
100000000 + 19 yields 100000016
100000000 + 20 yields 100000016
100000000 + 21 yields 100000024
100000000 + 22 yields 100000024
100000000 + 23 yields 100000024
100000000 + 24 yields 100000024
100000000 + 25 yields 100000024
100000000 + 26 yields 100000024
100000000 + 27 yields 100000024
100000000 + 28 yields 100000032
100000000 + 29 yields 100000032
100000000 + 30 yields 100000032
100000000 + 31 yields 100000032


ULP: https://en.wikipedia.org/wiki/Unit_in_the_last_place

std::numeric_limits<double>::is_iec559
https://en.cppreference.com/w/cpp/types/numeric_limits/is_iec559

IEC 559 aka IEEE 754 is a commonly followed standard for floating point formats, computations and rounding rules.
https://en.wikipedia.org/wiki/IEEE_floating_point
Are you saying that even some whole integers cannot be represented in floating? I don't get it..

We want to use nextafter() to find a floating point that will eventually be a value without a floating point like 2.0 So whatever numbers come before 2.0 don't matter.. I don't think I understand you properly.

@Peter when you ran my demonstration program you got 23 right.. Was that because of continuous 10 multiplication in while loop?
Last edited on
The first intger that cannot be represented in standard 32 bit floating point is 16777217.

Look it up: https://www.h-schmidt.net/FloatConverter/

I think you need to stop guessing and asking disconnected questions and actually take the time to learn how floating point works. Then you'll be able to answer all your questions yourself.

Here's a good explanation; http://fabiensanglard.net/floating_point_visually_explained/index.php
@Peter when you ran my demonstration program you got 23 right..

Yes, I got 23 in the second iteration of the loop. I have posted the output that I got in an earlier post.
http://www.cplusplus.com/forum/beginner/244856/2/#msg1083993

Was that because of continuous 10 multiplication in while loop?

I don't know if you can say that. Sure, if you had done it some other way (e.g. multiplied with 100 in each step) it might have worked for this particular case but that would just be a coincidence. Don't forget that 0.24 when stored as a floating point number is not exactly 0.24 so when multiplied with some number k the "correct" answer is in fact not equal to 0.24 * k. If 0.24 * k can be represented exactly you might get lucky and get exactly this value due to rounding errors but I wouldn't count on it.
Last edited on
Topic archived. No new replies allowed.
Pages: 123