I have code that converts between a float (representing a second) and an int64 (representing a nanosecond), taking 6
decimal places from the float
int64_t nanos = f * 1000000000LL;
However many decimal values stored in floats cannot be represented exactly in the binary float, so I get results like 14199999488
when my float is 14.2f
. Currently I solve this issue by computing the significant number of digits after the radix point
const float logOfSecs = std::log10(f);
int precommaPlaces = 0;
if(logOfSecs > 0) {
precommaPlaces = std::ceil(logOfSecs);
}
int postcommaPlaces = 7 - precommaPlaces;
if(postcommaPlaces < 0) {
postcommaPlaces = 0;
}
And then printing the float into a string to let Qt round the float correctly. Then I parse the string into a pre and post comma integer and multiple them with integer arithmetic.
const QString valueStr = QString::number(f, 'f', postcommaPlaces);
qint64 nanos = 0;
nanos += valueStr.section(".", 0, 0).toLongLong() * 1000000000LL;
if(postcommaPlaces) {
nanos += valueStr.section(".", 1).toLongLong() *
std::pow(10.0, 9 - postcommaPlaces);
}
This works fine, but I was wondering whether there is a better, perhaps faster way to do this?
The result of multiplying a float and an integer is always going to be a float.
The result of the multiplication of a float and an int is a float . Besides that, it will get promoted to double when passing to printf . You need a %a , %e , %f or %g format. The %d format is used to print int types.
1. Converting float to integer. The multiplication between float and string is not supported in python. But, to avoid the error from being thrown, we can try converting the given integer value to a string value.
By storing the value in a float
the damage has already been done, you've lost the original number whatever it was. You can guess at a value that might have been intended and then round, or if you're simply trying to display a value for the user you can round to a lower number of decimal places.
Instead, you can solve all these problems by using your fixed-point int64_t
representation throughout your entire code base, never converting to/from float
and avoiding throwing away precision during each conversion.
If you want to round to one decimal place for example
#include <iostream>
int main()
{
float f = 14.2f;
long long n = f * 1000000000LL;
std::cout << "float: " << n << '\n';
n = (f + 0.05) * 10;
n *= 100000000LL;
std::cout << "rounded: " << n << '\n';
return 0;
}
With two decimal places it's (f + 0.005) * 100
, ...,
and with six decimal places
n = ((long long)((f + 0.0000005) * 1000000)) * 1000LL;
If you want to consider significant digits (all digits), you must first take log10(f)
and then adjust rounding the decimal places.
But as @MarkB already said, if you use int64_t
in the first place, you don't need this at all.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With