Split double into two integers

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Eric Lilja

    #1

    Split double into two integers

    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(co nst 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(pr ecision) << 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

  • Markus Svilans

    #2
    Re: Split double into two integers

    Hi Eric,

    This function does what you want:

    #include <cmath>

    void split_double(do uble d, int precision,
    int &integer_par t, int &fractional_par t)
    {
    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.



    Eric Lilja wrote:
    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(co nst 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(pr ecision) << 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

    Comment

    • Greg

      #3
      Re: Split double into two integers

      Markus Svilans wrote:
      Hi Eric,
      >
      This function does what you want:
      >
      #include <cmath>
      >
      void split_double(do uble d, int precision,
      int &integer_par t, int &fractional_par t)
      {
      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

      Comment

      • Markus Svilans

        #4
        Re: Split double into two integers

        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.


        Greg wrote:
        Markus Svilans wrote:
        Hi Eric,

        This function does what you want:

        #include <cmath>

        void split_double(do uble d, int precision,
        int &integer_par t, int &fractional_par t)
        {
        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

        Comment

        • Greg

          #5
          Re: Split double into two integers

          Markus Svilans wrote:
          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
          "metafuncti on", 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(dou ble 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

          Comment

          • Michiel.Salters@tomtom.com

            #6
            Re: Split double into two integers

            Eric Lilja wrote:
            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.

            Comment

            • Ivan Vecerina

              #7
              Re: Split double into two integers

              "Greg" <greghe@pacbell .netwrote in message
              news:1160955989 .490271.273400@ f16g2000cwb.goo glegroups.com.. .
              : Markus Svilans wrote:
              : Hi Eric,
              : >
              : This function does what you want:
              : >
              : #include <cmath>
              : >
              : void split_double(do uble d, int precision,
              : int &integer_par t, int &fractional_par t)
              : {
              : 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
              --
              http://ivan.vecerina.com/contact/?subject=NG_POST <- email contact form

              Comment

              Working...