Rounding double

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Kaz Kylheku

    Re: Rounding double

    On Nov 21, 9:39 pm, md <mojtaba.da...@ gmail.comwrote:
    Hi
    >
    Does any body know, how to round a double value with a specific number
    of digits after the decimal points?
    A double probably doesn't have a decimal point. Floating point values
    are commonly represented in binary, not decimal. So what you are
    asking for is generally impossible.
    A function like this:
    >
    RoundMyDouble (double &value, short numberOfPrecisi ons)
    >
    It then updates the value with numberOfPrecisi ons after the decimal
    point.
    The real number which this function is intended to compute might not
    be representable in the floating point format used by your double
    type.

    At best, you can only write this function such that it produces a
    close approximation of that number. But still, this function is
    silly, and serves no purpose.

    Usually this type of rounding is done in two situations.

    One situation is that you are doing some kind of scientific or
    mathematic computing, and want to display results to a given decimal
    precision. In that case, the rounding and truncation is handed in the
    conversion of floating point values to text in the output routine. You
    do not actually massage your data to do the rounding. The number-to-
    string routine you use, whether it be within of printf or C++ ostreams
    or whatever, will do the job of rendering a printed representation of
    the number in decimal to the specified precision. You never adjust the
    internal representation to achieve this. Internally, you always keep
    the maximum precision afforded to you by the machine. A roudning
    function like the above is of little use to you.

    The second situation is that you are doing financial computing, and
    need internally to have exact decimal-based arithmetic that follows
    certain prescibed rounding rules. All intermediate results in
    financial calculations must obey these rules. Whatever is printed in a
    financial statement matches the internal representation. If your bank
    book says that an account had 1234.53 dollars after a certain
    transaction, it means exactly that. It doesn't mean there were
    actually 1234.5321 dollars, which were printed to the nearest cent. In
    this situation, it is simply inappropriate to be using floating-point
    numbers, and so a routine which simulates decimal rounding over the
    double type is also of no use.

    Comment

    • Kaz Kylheku

      Re: Rounding double

      On Nov 23, 3:26 pm, jacob navia <ja...@nospam.c omwrote:
      I got tired of #including math.h and finding out that atof
      wasn't there but in stdlib.h. So I put it in math.h in lcc-win.
      This is of course not the case with gcc, that has it in stdlib.
      GCC has it in stdlib.h, because, like, this thing called the ISO C
      standard wants it in stdlib.h.

      Comment

      • jkherciueh@gmx.net

        Re: Rounding double

        James Kanze wrote:
        On Nov 23, 12:13 pm, Kai-Uwe Bux <jkherci...@gmx .netwrote:
        >Richard Heathfield wrote:
        jacob navia said:
        >
        <snip>
        >
        >2: As you can see, my solution works in your machine.
        >Either RH is not telling the truth (unlikely) or he is
        >using some minor error in the code to trip about.
        >
        If your code has an error, however minor, then it is broken,
        in which case I suggest you post a fixed version. The
        version you posted *does not work* on my system. Quite apart
        from the fact that you are trying to solve an impossible
        problem (the problem, remember, is that of rounding the
        value of a double to a specified number of decimal places,
        which simply can't be done), you are trying to solve it in a
        way that produces bizarrely incorrect results on at least
        one system.
        >
        >Hm. I wonder if this might be a matter of interpreting the
        >problem.
        >
        >The C standard says about sqrt() that it computed the
        >nonnegative square root. C++ inherits this requirement. If
        >your interpretation of the rounding problem is correct and if
        >we transfer it to the other arithmetic operations, then there
        >can be no conforming implementations . In fact, even elementary
        >school arithmetic (+,-,*,/) cannot be done correctly under
        >that interpretation. However, that interpretation of the specs
        >is not the only possible.
        >
        >A different interpretation of floating point computations is
        >that an operation (say multiplication, addition, sqrt, or
        >rounding to a given number of decimal places) should yield a
        >double (or float or whatever types are topical in the
        >correspondin g newsgroup) that is closest to the exact
        >mathematical result. If I recall correctly, this is by and
        >large the position taken by IEEE754.
        >
        The problem is that neither the C nor the C++ standard require
        such. And that both standards also allow greater precision in
        the intermediate results. A liberty that is, in fact, used by
        most compilers for at least one very common architecture today.
        And which makes "rounding" using just floating point arithmetic
        very, very difficult. (I remember seeing an implementation of
        modf, a very long time ago, which used so trick involving
        multiplication and division in a way to end up with the integral
        part as a result of loss of precision. The person who was
        porting the library to the 8086 was astonished to find that the
        value written through the iptr argument wasn't an integer.)
        >
        >When this (reasonable) interpretation is adopted,
        >
        I'm not too sure about the "reasonable " part.
        a) This interpretation is not the only reasonable alternative
        interpretation. Unfortunately, the OP has not given us much to go on. I
        just wanted to demonstrate that the assumption, the OP wanted the rounding
        to yield _exact_ results (as opposed to all other arithmetic operation) is
        unfounded.

        b) [comp.land.c++ only] Although the C++ does not require floating point
        arithmetic to conform to IEEE754, it recognizes the relevance of IEEE754 in
        that the header <limitsprovid es compile time means to check for
        conformance: std::numeric_li mits<T>::is_iec 559 for a floating point type T
        is supposed to be true if and only if T conforms to IEEE754.
        On an Intel
        machine, a * b, where a and b are both double, does NOT give the
        exact result, rounded to the nearest representable double. And
        although Intel processors are pretty scarce in the milieu where
        I work (the last Intel I actively programmed on was an 80386),
        I've heard that they are still in use. (And other processors,
        such as the AMD 64 bit processor on my home PC, also behave this
        way.)
        Interesting. My laptop uses an Intel processor and my C++ implementation
        claims that my floating point types conform to IEEE754. I guess, the
        compiler does something funny and corrects for the insufficient machine
        instructions.

        >the problem of rounding to a fixed number of decimals is
        >solvable (and it is indeed not different from any other
        >computationa l problem). And if you don't adopt an
        >interpretati on like that, floating point arithmetic in general
        >is "impossible ".
        >
        I'd still leave it up to the implementation to handle the tricky
        parts. Multiply by a power of 10, floor(), ciel() or round(),
        and then divide by the same power, and you should get something
        fairly close to a correct result (and if you're talking about
        "rounding in base 10", close is all you're going to get).
        Actually, without guarantees about the precision of the results, it can be
        very difficult to judge whether approximate rounding is useful for a given
        purpose. Suppose you are computing letter grades and the rules require you
        to take a weighted average, round to one digit after the decimal point, and
        then check whether the result is within, say, [0.7,1.3]. If you do not have
        the guarantee that rounding and compiler interpretation of floating point
        literals is done according to the same precision and floating point
        rounding, you cannot really use floating point arithmetic (unless you
        engage in numerical analysis). In those cases, I would be inclined to use a
        decimal type. (And even if you have the necessary guarantees about
        rounding, you still have to make sure that the excess precision allowed for
        internal computations does not get in the way.)


        The real problem is the OP who fails to provide enough information to get
        meaningful help. As of now, all proposed rounding methods (and also all
        suggestions saying that rounding should be put off until output) in this
        thread are based on guesses as to what the OP needs the rounding for.


        Best

        Kai-Uwe Bux

        Comment

        • Richard Bos

          Re: Rounding double

          jacob navia <jacob@nospam.c omwrote:
          I got tired of #including math.h and finding out that atof
          wasn't there but in stdlib.h. So I put it in math.h in lcc-win.
          Ok, so you've found yet another way for your toy to break ISO C
          compatibility. Well done.

          Richard

          Comment

          • jacob navia

            Re: Rounding double

            Richard Bos wrote:
            jacob navia <jacob@nospam.c omwrote:
            >
            >I got tired of #including math.h and finding out that atof
            >wasn't there but in stdlib.h. So I put it in math.h in lcc-win.
            >
            Ok, so you've found yet another way for your toy to break ISO C
            compatibility. Well done.
            >
            Richard
            It is also in stdlib. It is in BOTH, and nothing
            is written about not putting it in math.h


            --
            jacob navia
            jacob at jacob point remcomp point fr
            logiciels/informatique

            Comment

            • jacob navia

              Re: Rounding double

              James Kuyper wrote:
              jacob navia wrote:
              >James Kanze wrote:
              >>
              >>I'd still leave it up to the implementation to handle the tricky
              >>parts. Multiply by a power of 10, floor(), ciel() or round(),
              >>and then divide by the same power, and you should get something
              >>fairly close to a correct result (and if you're talking about
              >>"rounding in base 10", close is all you're going to get).
              >>>
              >>
              >This is exactly what my function does. It is a close implementation of
              >the rounding, and never claimed to be the *exact* since that is
              >impossible!
              >
              No, your function does not use floor(), ceil(), or round() to calculate
              floating point representations of the intermediate integral value. It
              uses conversion to long long for that purpose. As a result it relies
              upon non-portable assumptions about the relationship between the
              precision of long long and the precision of long double. On
              implementations where that assumption is invalid, your algorithm
              unnecessarily produces results for some argument values that are less
              accurate than they could be with a more appropriate algorithm. Note:
              such an algorithm should actually use floorl(), ceill() or roundl(),
              rather than floor(), ceil() and round(), for precisely the same reason
              that your algorithm uses powl() rather than pow().
              That is the case. I changed the function to clean it up, and
              its last version was:

              #include <stdio.h>
              #include <float.h>
              #include <math.h>

              double roundto(double value, unsigned digits)

              {
              long double v = value;
              long double fv = fabs(value),p = powl(10.0L,digi ts);
              if (fv powl(10.0L,DBL_ DIG) || digits DBL_DIG)
              return value;
              return roundl(p*value)/p;
              }
              --
              jacob navia
              jacob at jacob point remcomp point fr
              logiciels/informatique

              Comment

              • Richard Heathfield

                Re: Rounding double

                jacob navia said:

                <snip>
                I claim that this will deliver the best approximation to the
                rounding to n decimal places, and I have never claimed otherwise.
                >
                If you do not agree, produce a better function.
                As you ought to know already, I don't see any point in trying to solve a
                problem that is inherently impossible for reasons that I have already
                explained.

                We need to know why we're rounding. If we're dealing with, say, currency
                (or some analogous system), the proper solution is to do calculations in
                an integer unit of which all other currency units are a multiple (e.g. for
                Sterling, use pennies; in the USA, use cents; in Europe, use Euros), and
                to establish a protocol for dealing with calculations that don't fit into
                this process (e.g. interest calculations). If we're dealing with
                calculations that simply require a neatening off for display purposes, on
                the other hand, then the proper solution is to round the text
                representation, not the value itself.
                #include <stdio.h>
                #include <float.h>
                #include <math.h>
                >
                double roundto(double value, unsigned digits)
                >
                {
                long double v = value;
                long double fv = fabs(value),p = powl(10.0L,digi ts);
                if (fv powl(10.0L,DBL_ DIG) || digits DBL_DIG)
                return value;
                return roundl(p*value)/p;
                }
                That code is perfectly topical, of course - but it doesn't solve the
                problem. Given this fact, the fact that it doesn't cater for those without
                C99 compilers is of little consequence.

                Data point: on my system, it gives very very very incorrect results (even
                within the context that you've adopted: e.g. if I ask it to round 0.33 to
                1dp I get -1.3543851449227 162220), but then I don't have a C99 compiler,
                merely a gcc implementation that provides non-C99-conforming extensions -
                but clearly this is a separate issue, and one on which the opinions of
                reasonable people are divided.

                (For those who may well be thinking - and indeed have already expressed the
                thought - that I should "get a C99 compiler then - or at least a compiler
                that supports many C99 features", my position is this: Many professional C
                programmers do not get to choose the implementation they are using. In
                many software environments, the decision about which compiler to use was
                made long ago for reasons that do not count "having the latest C99 stuff"
                as being particularly important when measured against more important
                stability criteria. It is therefore unwise to assume that other
                programmers have access to C99 features. But the C99-ness of Mr Navia's
                code is not the reason I think it fails to solve the problem. The reason I
                think Mr Navia's code doesn't solve the problem is that I think the
                problem as stated is insoluble.)

                --
                Richard Heathfield <http://www.cpax.org.uk >
                Email: -http://www. +rjh@
                Google users: <http://www.cpax.org.uk/prg/writings/googly.php>
                "Usenet is a strange place" - dmr 29 July 1999

                Comment

                • Ralf Damaschke

                  Re: Rounding double

                  jacob navia wrote:
                  Richard Bos wrote:
                  >jacob navia <jacob@nospam.c omwrote:
                  >>
                  >>I got tired of #including math.h and finding out that atof
                  >>wasn't there but in stdlib.h. So I put it in math.h in lcc-win.
                  >>
                  >Ok, so you've found yet another way for your toy to break ISO C
                  >compatibilit y. Well done.
                  >
                  It is also in stdlib. It is in BOTH, and nothing
                  is written about not putting it in math.h
                  It depends on the cleverness of such an implementation.
                  If such implementation does not also have the magic to forget the
                  extra declaration of atof when stdlib.h was not included, it
                  violates 7.1.3. The following program is strictly conforming.

                  #include <math.h>
                  static int atof = 4;
                  int main()
                  {
                  return 0;
                  }

                  Ralf

                  Comment

                  • jacob navia

                    Re: Rounding double

                    Ralf Damaschke wrote:
                    jacob navia wrote:
                    >Richard Bos wrote:
                    >>jacob navia <jacob@nospam.c omwrote:
                    >>>
                    >>>I got tired of #including math.h and finding out that atof
                    >>>wasn't there but in stdlib.h. So I put it in math.h in lcc-win.
                    >>Ok, so you've found yet another way for your toy to break ISO C
                    >>compatibility . Well done.
                    >It is also in stdlib. It is in BOTH, and nothing
                    >is written about not putting it in math.h
                    >
                    It depends on the cleverness of such an implementation.
                    If such implementation does not also have the magic to forget the
                    extra declaration of atof when stdlib.h was not included, it
                    violates 7.1.3. The following program is strictly conforming.
                    >
                    #include <math.h>
                    static int atof = 4;
                    int main()
                    {
                    return 0;
                    }
                    >
                    Ralf
                    Use lcc -ansic. In that case the declaration in math.h disappears

                    --
                    jacob navia
                    jacob at jacob point remcomp point fr
                    logiciels/informatique

                    Comment

                    • James Kuyper

                      Re: Rounding double

                      jacob navia wrote:
                      Richard Bos wrote:
                      >jacob navia <jacob@nospam.c omwrote:
                      >>
                      >>I got tired of #including math.h and finding out that atof wasn't
                      >>there but in stdlib.h. So I put it in math.h in lcc-win.
                      >>
                      >Ok, so you've found yet another way for your toy to break ISO C
                      >compatibilit y. Well done.
                      >>
                      >Richard
                      >
                      It is also in stdlib. It is in BOTH, and nothing is written about not
                      putting it in math.h
                      So, if I write the following strictly conforming code:

                      #include <stdlib.h>
                      int atof = 3;
                      int *pAtof(void) { return &atof;}

                      Will it compile correctly under your implementation?

                      7.1.3p1:
                      — Each identifier with file scope listed in any of the following
                      subclauses (including the future library directions) is reserved for
                      use as a macro name and as an identifier with file scope in the same
                      name space if any of its associated headers is included.

                      Comment

                      • jacob navia

                        Re: Rounding double

                        James Kuyper wrote:
                        jacob navia wrote:
                        >Richard Bos wrote:
                        >>jacob navia <jacob@nospam.c omwrote:
                        >>>
                        >>>I got tired of #including math.h and finding out that atof wasn't
                        >>>there but in stdlib.h. So I put it in math.h in lcc-win.
                        >>>
                        >>Ok, so you've found yet another way for your toy to break ISO C
                        >>compatibility . Well done.
                        >>>
                        >>Richard
                        >>
                        >It is also in stdlib. It is in BOTH, and nothing is written about not
                        >putting it in math.h
                        >
                        So, if I write the following strictly conforming code:
                        >
                        #include <stdlib.h>
                        int atof = 3;
                        int *pAtof(void) { return &atof;}
                        >
                        Will it compile correctly under your implementation?
                        >
                        7.1.3p1:
                        >— Each identifier with file scope listed in any of the following
                        >subclauses (including the future library directions) is reserved for
                        >use as a macro name and as an identifier with file scope in the same
                        >name space if any of its associated headers is included.
                        >
                        yes, use
                        lcc -ansic


                        --
                        jacob navia
                        jacob at jacob point remcomp point fr
                        logiciels/informatique

                        Comment

                        • James Kuyper

                          Re: Rounding double

                          jacob navia wrote:
                          James Kuyper wrote:
                          ....
                          >No, your function does not use floor(), ceil(), or round() to
                          >calculate floating point representations of the intermediate integral
                          >value. It uses conversion to long long for that purpose. As a result
                          >it relies upon non-portable assumptions about the relationship between
                          >the precision of long long and the precision of long double. On
                          >implementation s where that assumption is invalid, your algorithm
                          >unnecessaril y produces results for some argument values that are less
                          >accurate than they could be with a more appropriate algorithm. Note:
                          >such an algorithm should actually use floorl(), ceill() or roundl(),
                          >rather than floor(), ceil() and round(), for precisely the same reason
                          >that your algorithm uses powl() rather than pow().
                          >
                          That is the case. I changed the function to clean it up, and
                          its last version was:
                          >
                          #include <stdio.h>
                          #include <float.h>
                          #include <math.h>
                          >
                          double roundto(double value, unsigned digits)
                          >
                          {
                          long double v = value;
                          long double fv = fabs(value),p = powl(10.0L,digi ts);
                          if (fv powl(10.0L,DBL_ DIG) || digits DBL_DIG)
                          return value;
                          return roundl(p*value)/p;
                          }
                          That's better. You've still got potential for overflow on the
                          multiplication, but to be honest that's true of a lot of my code too.
                          However, I write scientific software where the range of possible values
                          is known and validated. Therefore, I only need to provide overflow
                          protection for those operations where overflow is an actual possibility.
                          As this is a utility program, it should prevent overflow for any pair of
                          arguments for which overflow is possible and preventable - it doesn't.

                          Also, providing support for negative values of 'digits' would be
                          trivial, and would significantly improve what little usefulness this
                          routine has (while creating a need to prevent denormalization at the
                          multiplication) .

                          Comment

                          • James Kuyper

                            Re: Rounding double

                            Richard Heathfield wrote:
                            jacob navia said:
                            >
                            <snip>
                            >
                            >I claim that this will deliver the best approximation to the
                            >rounding to n decimal places, and I have never claimed otherwise.
                            >>
                            >If you do not agree, produce a better function.
                            >
                            As you ought to know already, I don't see any point in trying to solve a
                            problem that is inherently impossible for reasons that I have already
                            explained.
                            Are you asserting that the specification Jacob just described is
                            inherently impossible to implement? As far as I can see, the reasons
                            you've already explained do not apply to this specification.

                            I was under the impression that the only thing you could say against
                            this specification is that it doesn't match your own unconventionall y
                            strict interpretation of the OP's specification.

                            Comment

                            • Bart

                              Re: Rounding double

                              "Richard Heathfield" <rjh@see.sig.in validwrote in message
                              news:RIudnfYWYY SRONfanZ2dnUVZ8 t2snZ2d@bt.com. ..
                              We need to know why we're rounding. If we're dealing with, say, currency
                              (or some analogous system), the proper solution is to do calculations in
                              an integer unit of which all other currency units are a multiple (e.g. for
                              Sterling, use pennies; in the USA, use cents; in Europe, use Euros), and
                              to establish a protocol for dealing with calculations that don't fit into
                              this process (e.g. interest calculations). If we're dealing with
                              calculations that simply require a neatening off for display purposes, on
                              the other hand, then the proper solution is to round the text
                              representation, not the value itself.
                              Not many posting here seem to believe there are real and practical
                              reasons for rounding values to so many decimals (or rounding to a
                              nearest fraction, a related problem).

                              Currency seems the most understood, and storing values in floating
                              point as dollars/pounds/euros, and rounding intermediate calculations
                              to the nearest cent/penny (0.01) works perfectly well for a typical
                              shop or business invoice. For large banks adding up accounts for
                              millions of customers, government and so on, I'm sure they have their
                              specialist developers.

                              Another example is CAD (drawing tools) where input is inherently noisy
                              (23.423618182 mm) and the usual practice is to round ('snap') to the
                              nearest aesthetic value, depending on zoom factor and scale and so on,
                              so 23., 23.5, 23.42 and so on. Otherwise you would get all sorts of
                              skewy lines.

                              There is still a noise factor present (the errors we've been
                              discussing) but you would need to zoom in by factor of a billion to
                              see them. In typical printouts things look perfect. In fact it's
                              interesting to zoom in and see these errors come to life on the
                              screen.

                              Also often everything is stored as, say, millimetres, while the user
                              might be using inches, and rounding would need to be as inches (say
                              hundredths of an inch, which would be the nearest multiple of 0.254),
                              again an approximation but works well enough (this allows designs
                              created with different units to be combined).

                              Actually rounding for printing purposes is probably not done much
                              outside printf() and such functions. In fact perhaps it's because
                              printf() does round floating point numbers, and therefore shows a
                              value that is only an approximation, that gives rise to much
                              misunderstandin g. Maybe it should indicate (with a trailing ? perhaps)
                              that the value printed is not quite right unless explicitly told to
                              round.

                              Bart

                              Comment

                              • jacob navia

                                Re: Rounding double

                                James Kuyper wrote:
                                jacob navia wrote:
                                >James Kuyper wrote:
                                ...
                                >>No, your function does not use floor(), ceil(), or round() to
                                >>calculate floating point representations of the intermediate integral
                                >>value. It uses conversion to long long for that purpose. As a result
                                >>it relies upon non-portable assumptions about the relationship
                                >>between the precision of long long and the precision of long double.
                                >>On implementations where that assumption is invalid, your algorithm
                                >>unnecessari ly produces results for some argument values that are less
                                >>accurate than they could be with a more appropriate algorithm. Note:
                                >>such an algorithm should actually use floorl(), ceill() or roundl(),
                                >>rather than floor(), ceil() and round(), for precisely the same
                                >>reason that your algorithm uses powl() rather than pow().
                                >>
                                >That is the case. I changed the function to clean it up, and
                                >its last version was:
                                >>
                                >#include <stdio.h>
                                >#include <float.h>
                                >#include <math.h>
                                >>
                                >double roundto(double value, unsigned digits)
                                >>
                                >{
                                > long double v = value;
                                > long double fv = fabs(value),p = powl(10.0L,digi ts);
                                > if (fv powl(10.0L,DBL_ DIG) || digits DBL_DIG)
                                > return value;
                                > return roundl(p*value)/p;
                                >}
                                >
                                That's better. You've still got potential for overflow on the
                                multiplication, but to be honest that's true of a lot of my code too.
                                However, I write scientific software where the range of possible values
                                is known and validated. Therefore, I only need to provide overflow
                                protection for those operations where overflow is an actual possibility.
                                As this is a utility program, it should prevent overflow for any pair of
                                arguments for which overflow is possible and preventable - it doesn't.
                                >

                                I disagree.

                                The test fv powl(10.0L, DBL_DIG) ensures that the absolute value
                                of "value" is less than 10 ^ 16. If "digits" is 15, the maximum
                                value of the multiplication can be 10 ^ 15 * 10 ^ 15 == 10 ^ 30,
                                a LOT less than the maximum value of double precision that is
                                DBL_MAX 1.7976931348623 157e+308. Note that the calculations are done
                                in long double precision and LDBL_MAX 1.1897314953572 3176505e+4932L
                                so the overflow argument is even less valid in long double precision.
                                Other systems LDBL_MAX are even higher since they use 128 bits and not
                                80 bits as the 80x86. For systems where long double is equal to double
                                precision, the value is well within range anyway.

                                Hence, the multiplication can't overflow.


                                If we accepted negative values, the division of 10 ^ 30 by 10 ^ -15 -->
                                10 ^ 45, still LESS than the value of DBL_MAX. Hence the division can't
                                overflow, and in my function I do not accept negative values so the
                                division result will be always less than 10 ^ 30.

                                The rounding of long double to double can't overflow either since it is
                                always less than 10 ^ 308.
                                Also, providing support for negative values of 'digits' would be
                                trivial, and would significantly improve what little usefulness this
                                routine has (while creating a need to prevent denormalization at the
                                multiplication) .

                                --
                                jacob navia
                                jacob at jacob point remcomp point fr
                                logiciels/informatique

                                Comment

                                Working...