Split double into two integers

E

Eric Lilja

Hi, I need to implement a function that should take a double and split
it into two integers. The decimalpart may be 0 but it may not be
greater than 0.99 and not less than 0.01. In other words, decimalparts
larger than three decimals are invalid input. I know floating point
numbers are not represented exactly in computers so this is tricky
business. This seems to work on my laptop, it's to be used by students
in an assignment to write a monetary class so it mustn't be perfect,
but I would like to hear your input.

#include <cmath>
#include <iostream>
#include <iomanip>
#include <sstream>

using namespace std;

void split_double(const double num, int& integer, int& decimals)
{
const int precision = 2;
double d1, d2;

d1 = modf(num, &d2);

if(d1 != 0.0f && (d1 > 0.99f || d1 < 0.01f))
cout << "d1 = " << d1 << " Error!" << endl; // TODO: Throw
exception

stringstream ss;
ss << fixed << setprecision(precision) << num;
ss >> integer;
ss.ignore();
ss >> decimals;
}

int main()
{
int intpart;
int decpart;

split_double(3.99, intpart, decpart);
cout << intpart << "." << decpart << endl;
split_double(3.991, intpart, decpart);
cout << intpart << "." << decpart << endl;
split_double(3.001, intpart, decpart);
cout << intpart << "." << decpart << endl;
}

$ ./foo.exe
3.99
d1 = 0.991 Error!
3.99
d1 = 0.001 Error!
3.0
 
M

Markus Svilans

Hi Eric,

This function does what you want:

#include <cmath>

void split_double(double d, int precision,
int &integer_part, int &fractional_part)
{
integer_part = (int)d;
fractional_part = (int)( (d - integer_part) * std::pow(10,
precision) + 0.5);
}


The +0.5 in the integer cast is to achieve proper rounding. For
example, if precision is 3, then 0.4567 will round to 0.457 instead of
0.456.

If you need the function to be fast, then the call to pow() should
probably be optimized out somehow (or just hard code the function to a
particular precision).

Regards,
Markus.
 
G

Greg

Markus said:
Hi Eric,

This function does what you want:

#include <cmath>

void split_double(double d, int precision,
int &integer_part, int &fractional_part)
{
integer_part = (int)d;
fractional_part = (int)( (d - integer_part) * std::pow(10,
precision) + 0.5);
}


The +0.5 in the integer cast is to achieve proper rounding. For
example, if precision is 3, then 0.4567 will round to 0.457 instead of
0.456.

There are several rounding behaviors possible (round toward nearest,
round toward zero, round away from zero and so forth). The rounding
behavior that happens to be the "proper" one depends on the
application. And in this case, if the proper behavior to round to the
nearest three-digit decimal fractional representation, then adding 0.5
will not work for all values: for example -0.4567 will round to -0.456
even though -0.467 is closer in value.

A program can readily divide a double into integer and one-thousandth
units (rounding toward nearest represention) like so:

#include <math.h>

// d is the double
int integerUnits = d; // truncate

int fractionalUnits = fmod( round(d * 1000.0), 1000.0);

Greg
 
M

Markus Svilans

Hi Greg,

You're right, the solution I gave only rounds positive numbers
"properly."

Another possible correction is:

fractional_part = (int)( (d - integer_part) * std::pow(10, precision) +
(d > 0 ? 0.5 : -0.5) );

The expression could be simplified by using a constant value (e.g. 100,
1000, or 10000) instead of the slower pow() function.

Thanks,
Markus.
 
G

Greg

Markus said:
Hi Greg,

You're right, the solution I gave only rounds positive numbers
"properly."

Another possible correction is:

fractional_part = (int)( (d - integer_part) * std::pow(10, precision) +
(d > 0 ? 0.5 : -0.5) );

The expression could be simplified by using a constant value (e.g. 100,
1000, or 10000) instead of the slower pow() function.

Another way to avoid the runtime overhead of calling pow() would be to
calculate the value as a compile time constant:

template <int Base, unsigned Exponent>
struct Power
{
const static int value = Base * Power<Base, Exponent-1>::value;
};

template <int Base>
struct Power<Base, 0>
{
const static int value = 1;
};

With the call to pow() replaced with the Power class template
"metafunction", there is no additional overhead in implementing
SplitDouble as a template function (with the desired precision
specified as its non-type parameter):

template <unsigned N>
void SplitDouble(double d, int& outInteger, int& outFraction)
{
int outInteger = d; // truncate

int outFraction = fmod( round(d * Power<10, N>::value),
Power<10, N>::value);
}

To obtain the two digit precision required in the original post, a
program would call SplitDouble like so:

int integerUnits, fractionalUnits;

SplitDouble<2>( d, integerUnits, fractionalUnits );

Greg
 
M

Michiel.Salters

Eric said:
Hi, I need to implement a function that should take a double and split
it into two integers. The decimalpart may be 0 but it may not be
greater than 0.99 and not less than 0.01. In other words, decimalparts
larger than three decimals are invalid input. I know floating point
numbers are not represented exactly in computers so this is tricky
business. This seems to work on my laptop, it's to be used by students
in an assignment to write a monetary class so it mustn't be perfect,

Wrong approach. Monetary amounts are integers, just with a weird I/O
format.

HTH,
Michiel Salters.
 
I

Ivan Vecerina

: Markus Svilans wrote:
: > Hi Eric,
: >
: > This function does what you want:
: >
: > #include <cmath>
: >
: > void split_double(double d, int precision,
: > int &integer_part, int &fractional_part)
: > {
: > integer_part = (int)d;
: > fractional_part = (int)( (d - integer_part) * std::pow(10,
: > precision) + 0.5);
: > }
: >
: >
: > The +0.5 in the integer cast is to achieve proper rounding. For
: > example, if precision is 3, then 0.4567 will round to 0.457 instead
of
: > 0.456.
Please see what happens with d=5.999 and precision=2:
integer_part = 5;
fractional_part = 100;
I don't think that this would be acceptable to the OP.

: There are several rounding behaviors possible (round toward nearest,
: round toward zero, round away from zero and so forth). The rounding
: behavior that happens to be the "proper" one depends on the
: application. And in this case, if the proper behavior to round to the
: nearest three-digit decimal fractional representation, then adding 0.5
: will not work for all values: for example -0.4567 will round to -0.456
: even though -0.467 is closer in value.
:
: A program can readily divide a double into integer and one-thousandth
: units (rounding toward nearest represention) like so:
:
: #include <math.h>
:
: // d is the double
: int integerUnits = d; // truncate
:
: int fractionalUnits = fmod( round(d * 1000.0), 1000.0);
Unfortunately, this doesn't work any better.
Starting with d=5.99999999, you will end up with:
integer_part = 5;
fractional_part = 0;
Neither do the subsequent proposals address the above problem.

One should first round the whole number, then proceed with
the decomposition.
One could use something like:
long long modulus = round( abs(d*1000) );
long integerUnits = modulus / 1000;
long fractionalUnits = modulus % 1000;
// ... then handle the sign as desired if d<0

But what do you want to do with the result?

If it is for display purposes, pass the double value directly
to a printing function from the standard (C or C++) library.

If it is for computation purposes, why not use fixed-point
integer arithmetic in the first place, as Michael pointed out ?


Ivan
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,534
Members
45,008
Latest member
Rahul737

Latest Threads

Top