-1

The problem I'm facing is because I'm using a library that when fed a float, it does something inside that is converting the float to double and adding a lot of decimals to the number which is not right. I mean, is right according to how doubles and floats are represented and what naturally happens when you cast, but it is not right for my application. I have 16.3 as float and I need that to be 16.3 as double.

I'm trying to cast the float to double BEFORE passing the value to the library to have control over that conversion but so far I haven't found a straightforward way of doing so. I just want to know if it is possible to cast a 16.3 from float to double and have the same 16.3 as a result.

float f_var = 16.3;            // f_var is 16.3
double d_var = (double)f_var;  // d_var shall be also 16.3, not 16.299999237060547

Is there even a proper way of doing this in C++? Or I would have to do something like:

char float_buf[10];
float f_var = 16.3;
sprintf(float_buf, "%.2f", f_var);
double d_var = atof(float_buf);

Because that works, but I'd like to see if there is a "proper" way of doing this in c++.

There is this response in SO with another proposed solution but it involves multiplication and a division that I would like to avoid as well.

This is not for displaying or any input/output operation or converting to a string, I know how to do that and there is a lot of information about it. Additionally, I'm not trying to understand "why" this happens, there is a lot of information about that as well.

Also, most of the answers in SO that may cause this question to be marked as duplicate are not for C++, they are mostly for C#, Java JS, Objective C, etc. For the few I found for C/C++, most of them are dealing with this issue for IO with printf or cout and the others only explain why this happens which is also not useful. If you have indeed a duplicate for this exactly in C++ then please indicate. I'll be happy for this to be flagged as duplicate if it really is.

Thanks!

m4l490n
  • 1,592
  • 2
  • 25
  • 46
  • 1
    You basically can't. What you need is a fixed point library or an arbitrary precision library. Have a look at this: https://stackoverflow.com/questions/588004/is-floating-point-math-broken – NathanOliver Jul 14 '20 at 15:45
  • 4
    You wrote `float f_var = 16.3;` presumably expecting `f_var` to be exactly `16.3`. It isn't, because it cannot be. There is no floating point value in this here world that can exactly represent the number 16.3, regardless of whether you are using C, C++, PHP or a devil with horns and hooves. – n. m. could be an AI Jul 14 '20 at 15:47
  • Why do you want to avoid the multiplication and division? That's the "right" way to do this. Your way is... explicit... but otherwise probably much, (maybe even *very* much?) slower. Also, duplicates are duplicates, and IEEE floating point is IEEE floating point. Java, C++, C#, C, *doesn't matter* (that much) when it comes to these questions. Notice that the two "right" solutions you've found translate directly to pretty much any language related to C, and more besides. I doubt you're going to get an answer that's specific to C++ that's better than anything you've found in any language. – HTNW Jul 14 '20 at 16:03
  • I would advise you to find a different library that uses doubles instead of floats if this is that important to you. – Mark Ransom Jul 14 '20 at 16:07
  • `float f_var = 16.3; // f_var is 16.3` - No, `f_var` is *not* `16.3` *exactly*, since that value cannot be exactly represented by a `float`. `f_var` holds the closest *aproximation* to `16.3` that a IEEE `float` can represent. – Jesper Juhl Jul 14 '20 at 16:12
  • Using https://www.h-schmidt.net/FloatConverter/IEEE754.html shows that `16.3` as a float is actually `16.299999237060546875` – Alan Birtles Jul 14 '20 at 16:26

1 Answers1

3

This answer assumes IEEE floating point.

A machine data type such as float contains a finite number of bits and therefore can only represent a finite number of values. Alas, 16.3 doesn't happen to be one of those.

float f_var = 16.3;

The value of f_var is now the number that is (1) representable as a single precision floating point number and (2) as close to 16.3 as humanly possible. This number happens to be exactly 16.299999237060546875. It is the closest one to 16.3. The next larger representable number is 16.3000011444091796875, which is quite a bit farther away. The binary representations of these two are 01000001100000100110011001100110 and 01000001100000100110011001100111 if you are curious. They are different only in the last bit.

double d_var = (double)f_var;

Lo and behold, the value of d_var is now the exact same number 16.299999237060546875.

Is this the closest possible double number to 16.3? Hell no!

double can exactly represent 16.300000000000000710542735760100185871124267578125 which is quite a bit closer to 16.3 then 16.299999237060546875.

But why should d_var take this more precise value? It has been shown 16.299999237060546875, and now it is quite faithfully 16.299999237060546875. If you want it to be more precise, assign it a more precise value. It is not a sentient being and doesn't know that you meant that 16.299999237060546875 to be 16.3.

Where could one get such a more precise value from?

Well one could arbitrarily decide all the interesting numbers have no more than one digit after the decimal point, and an infinite number of zeroes after that. (Why would one ever assume that is beyond me, but I'm playing along). If this is the case, then

d_var = round(d_var * 10)/10

would round the number to be very close to the one that has one decimal digit after the decimal point, and an infinite number of zeroes after that. If we do this trick with 16.299999237060546875, it will become 16.300000000000000710542735760100185871124267578125.

If you are interested in numbers with two digits after the decimal point, replace 10 with 100, etc. However, at this point you should be asking ask yourself whether the floating point type is the right tool for the job. Maybe you need fixed-point decimal numbers?

On the other hand, if you are interested in numbers with whatever number of digits there was initially, then you have to supply that number of digits, and possibly remember it along with the number itself. At this point you should ask yourself the above question even harder.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243