Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to correctly multiply a float with an int and get a result only influenced by significant digits?

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?

like image 739
Johannes Schaub - litb Avatar asked Jan 10 '13 15:01

Johannes Schaub - litb


People also ask

Can you multiply a float and an int?

The result of multiplying a float and an integer is always going to be a float.

What happens if you multiply a float by an int?

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.

Can you multiply float and int in Python?

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.


2 Answers

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.

like image 119
Mark B Avatar answered Sep 28 '22 09:09

Mark B


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.

like image 27
Olaf Dietsche Avatar answered Sep 28 '22 10:09

Olaf Dietsche