Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. The Lounge
  3. How hard would it be to have a Rational Number type, so that there would never be floating point errors?

How hard would it be to have a Rational Number type, so that there would never be floating point errors?

Scheduled Pinned Locked Moved The Lounge
c++question
33 Posts 19 Posters 0 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • S swampwiz

    It seems to me that there could be a type (implemented in your favorite language has has classes, including operator overloading) that would store a number as a rational number. This type would be a list of links that would have prime factors and their exponents (e.g., in C++, the prime factor would be type "unsigned long", with the exponent being "long"). This would work because any decimal (or hexadecimal) input would be a rational number itself, and then any operations would be exact, so that, e.g., the result of (213.56 - 45.41) would be 168.15 and not 168.14999999, etc.

    B Offline
    B Offline
    Bruce Patin
    wrote on last edited by
    #24

    They had this in Assembler and FORTRAN a long time ago. In fact, it is a basic number type in System/370 machine language.

    1 Reply Last reply
    0
    • K Kirk 10389821

      Well, it depends... First, rational numbers would slow things down. But if you want accuracy in math, I think the answer is to use integers with IMPLIED Decimals. Some currency types do this with 4 digits of implied decimal accuracy. Personally, I have never seen the need for more than this, unless I was using matlab or something where maintaing 1/3 of important to backing out of the problem properly. Finally, you could always implement your own classes to behave properly. Good luck with that, and the resulting speed. Kirk Out!

      K Offline
      K Offline
      kalberts
      wrote on last edited by
      #25

      Kirk 10389821 wrote:

      First, rational numbers would slow things down.

      First, in quite a few applications, that doesn't matter. Whether the calculations are done in 10 or 20 milliseconds doesn't matter if you prepare a value that is displayed in an interactive dialog. Second, if arithmetic calculations is only a small part of the handling, other formats than the traditional floating point may be faster in the other operations. Take an archetypical Cobol application: It reads in, moves around and displays values in counts and dollars and cents; every now and then doing a multiplication, but mostly just addition and subtraction. The time saved in I/O from a BCD format compared to a float format greatly offsets the slower BCD addition/subtraction. Third: If you use rationals to avoid rounding errors, your float option might be an arbitrary precision float package. Adding 1/3 + 1/6 as rationals is likely to be much faster than calling a library function maintaining a list of mantiass elemnets to add 0.333333.... and 0.16666... for, say, 512 bits.

      1 Reply Last reply
      0
      • G gdemont

        Already done : check Rationals @ http://mathpaqs.sourceforge.net/. It's even generic, so you can use whatever type for a,b in the fractions a/b: integers, big integers, polynomials, ...

        S Offline
        S Offline
        swampwiz
        wrote on last edited by
        #26

        It says this project has no files.

        1 Reply Last reply
        0
        • OriginalGriffO OriginalGriff

          And storage of Pi would be?

          Sent from my Amstrad PC 1640 Bad command or file name. Bad, bad command! Sit! Stay! Staaaay... AntiTwitter: @DalekDave is now a follower!

          O Offline
          O Offline
          obermd
          wrote on last edited by
          #27

          Still wouldn't help electrical engineers whose work is based on e and I. That said there are a lot of good reasons to support some sort of base 10 fractional numbers to avoid the rounding errors that result from converting base 2 fractionals to base 10 fractionals.

          1 Reply Last reply
          0
          • S swampwiz

            It seems to me that there could be a type (implemented in your favorite language has has classes, including operator overloading) that would store a number as a rational number. This type would be a list of links that would have prime factors and their exponents (e.g., in C++, the prime factor would be type "unsigned long", with the exponent being "long"). This would work because any decimal (or hexadecimal) input would be a rational number itself, and then any operations would be exact, so that, e.g., the result of (213.56 - 45.41) would be 168.15 and not 168.14999999, etc.

            D Offline
            D Offline
            Daniel Wilianto
            wrote on last edited by
            #28

            With modern language I think you can implement it yourself. All C# variable types derive from System.Object anyway. And then you can override arithmatic operator for this new type. I can sort of picture it, storing each value as two integer : numerator and denominator.

            K 1 Reply Last reply
            0
            • D Daniel Wilianto

              With modern language I think you can implement it yourself. All C# variable types derive from System.Object anyway. And then you can override arithmatic operator for this new type. I can sort of picture it, storing each value as two integer : numerator and denominator.

              K Offline
              K Offline
              kalberts
              wrote on last edited by
              #29

              I wouldn't hesitate for a second to give that as the exercise of the week to second year programming students, implmenting the four basic arithmetic operations for rationals. There is only a small problem: If the denominators are not identical in addition/subtraction, you can trivially find a common denominator by multiplying the two, but in the genral case, it is not the least common denominator. If you use 64 bit integers for the denominator you might delay handling this until the results are to be used (at least for small student level applications), but at some stage you will have to factorize the numerator and denominator, and remove common factors. In principle this is not difficult as long as the problem is of "student size", but Eratosthenes' sieve is not very efficient (certainly not in space!) - you wouldn't want to employ that for every addition or subtraction! Factors of 2 are easy to get rid of: While both (numerator AND 1) and (denominator ANd 1) are nonzero, shift both right by one bit. This you can do for every basic arithmetic operation (or complex expression). Other factors are worse. If you find it acceptable to do a whole series of division/remainder operations (trying with all prime factors up to the square root of the smaller of numerator and denominator), then the space required is roughly limited to a single read-only table of known primes, but you'll certainly be keeping the division circuitry of the CPU hot :-). My recommendation would be to delay the factorization until it is needed for presentation or conversion to other formats. Viewing/interpreting the values through a debugger would be a next to impossible, and you should implement an exception handler removing common factors if an operation would cause the numerator/denominator to overflow the integer format - maybe doing a 2-factor removal as a fast first try, and going on to a series of division operations only if the values are still above a certain overflow risk threshold. If you use 64 bit ints, you might in the worst case have to do divisions for every prime less than 4G; that is quite some operation...

              1 Reply Last reply
              0
              • S swampwiz

                It seems to me that there could be a type (implemented in your favorite language has has classes, including operator overloading) that would store a number as a rational number. This type would be a list of links that would have prime factors and their exponents (e.g., in C++, the prime factor would be type "unsigned long", with the exponent being "long"). This would work because any decimal (or hexadecimal) input would be a rational number itself, and then any operations would be exact, so that, e.g., the result of (213.56 - 45.41) would be 168.15 and not 168.14999999, etc.

                V Offline
                V Offline
                Vivi Chellappa
                wrote on last edited by
                #30

                Not all that hard. In my MS Computer Science course, All students had to write a compiler for a language that had only integers and rational numbers. This was before the days of C++, classes, etc. Not too much of a sweat.

                K 1 Reply Last reply
                0
                • V Vivi Chellappa

                  Not all that hard. In my MS Computer Science course, All students had to write a compiler for a language that had only integers and rational numbers. This was before the days of C++, classes, etc. Not too much of a sweat.

                  K Offline
                  K Offline
                  kalberts
                  wrote on last edited by
                  #31

                  How did you handle the (least common) denominator problem? Or did you simply ignore it, ignoring the risk that it might overflow? How did you present the results - as if they were floats (through a "(float)numerator/(float)denominator" division) using float output format? Did you do anything to detect common denominators, or did 1/3 + 1/3 end up as 6/9?

                  V 1 Reply Last reply
                  0
                  • K kalberts

                    How did you handle the (least common) denominator problem? Or did you simply ignore it, ignoring the risk that it might overflow? How did you present the results - as if they were floats (through a "(float)numerator/(float)denominator" division) using float output format? Did you do anything to detect common denominators, or did 1/3 + 1/3 end up as 6/9?

                    V Offline
                    V Offline
                    Vivi Chellappa
                    wrote on last edited by
                    #32

                    I don't remember the details because it was such a long time ago but I distinctly remember that I did determine the least common denominator for fractions and ensured that all rational numbers were stored in their reduced form.

                    1 Reply Last reply
                    0
                    • S swampwiz

                      It seems to me that there could be a type (implemented in your favorite language has has classes, including operator overloading) that would store a number as a rational number. This type would be a list of links that would have prime factors and their exponents (e.g., in C++, the prime factor would be type "unsigned long", with the exponent being "long"). This would work because any decimal (or hexadecimal) input would be a rational number itself, and then any operations would be exact, so that, e.g., the result of (213.56 - 45.41) would be 168.15 and not 168.14999999, etc.

                      B Offline
                      B Offline
                      bjongejan
                      wrote on last edited by
                      #33

                      While a rational number type has both advantages and disadvantages and we can discuss that forever, it is an undeniable truth that JSON should have had a rational number type, simply because some languages or libraries already support rational numbers and there should exist a portable way for applications using rational numbers to serialize and exchange these with other applications. A rational number that is within the range of a floating point number type can always be "downgraded" to that floating point number type, but the converse is problematic. For example, is the JSON expression [0.67,0.33] an array with the numbers 2/3 and 1/3 ? If not, then what about [0.6666666666666667,0.3333333333333333]? It would have been clear what was meant if we were allowed to write [2/3,1/3].

                      1 Reply Last reply
                      0
                      Reply
                      • Reply as topic
                      Log in to reply
                      • Oldest to Newest
                      • Newest to Oldest
                      • Most Votes


                      • Login

                      • Don't have an account? Register

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • World
                      • Users
                      • Groups