Announcement

Collapse

New Sub-Forum

In an effort to help make sure there are appropriate categories for topics of discussion that are happening, there is now a sub-forum for databases and database programming under Special Interest groups. Please direct questions, etc., about this topic to that sub-forum moving forward. Thank you.
See more
See less

64-bit Precision for Extended Precision Variables Guaranteed?

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • 64-bit Precision for Extended Precision Variables Guaranteed?

    Windows XP SP2, PBCC 4.04
    If I assign a numeric literal that is cast as extended to an extended precision variable (as shown below), the value stored in the EXT variable does not have a precision of 64 bits (as it is supposed to), but instead has 54 bits of precision.

    CODE:
    dim number as EXT
    number = 0.1##

    I have learned -- through one or more posts in this forum -- that this is due to Windows initializing the FPU to a 54-bit mantissa instead of the normal 64 bits. Back in Jan. of 2000, Lance Edmonds provided the following technique to correct this, but it does not work for me:

    CODE:
    #REGISTER NONE
    xyz% = 0
    ASM fstcw xyz%
    ASM or xyz%, &B0000001100000000%
    ASM fldcw xyz%

    Somewhere in this forum, I came across another post that -- through assembler instructions -- claims guaranteed extended precision. Despite searching this forum up and down, I cannot locate that post again. Would some kindhearted soul please show -- or provide a link to -- the official PB method of obtaining 64-bit precision from extended precision variables to once and for all put this issue to bed? I have deliberately created a new thread to, hopefully, make it easier for other people to locate a solution for this problem.
    Last edited by Walter Henn; 7 Dec 2007, 05:13 PM. Reason: Clarification

  • #2
    QUAD integers have 64 bits of precision. You can use those and manage your own exponents. Of course, CURRENCY and EXTENDED CURRENCY are also a 64-bit integers, so they would have 64 bits of precision, too.

    BTW, one tenth (decimal) is irrational in binary so it's probably not the best choice of test data.

    MCM
    Michael Mattias
    Tal Systems Inc. (retired)
    Racine WI USA
    [email protected]
    http://www.talsystems.com

    Comment


    • #3
      Thanks for your comments, Michael.

      Since PB has extended precision, it would certainly be preferable -- and more elegant -- to be able to rely on PB for handling 64-bit precision floating point numbers than to have to use quads and manage my own exponents.

      Actually, the choice of 0.1## for a test number was deliberate. Since it's exact binary equivalent consists of an infinite series of binary digits, it allows you to quickly determine the precision you're getting when you view the extended variable in memory. Choice of a decimal number which can be represented exactly by a binary number with a finite number of places could easily allow you to conclude that you have lost precision when, in fact, you have simply been able to store the test number exactly in less places.
      Last edited by Walter Henn; 7 Dec 2007, 07:12 PM.

      Comment


      • #4
        You know, the doc does not agree with your '64 bits of precision' for EXT variables. What it says is...
        Extended-precision values require 10 bytes of storage each. They have a range of approximately +/- 3.4x10^-4932 to 1.2x10^4932, and offer 18 digits of precision
        Assuming "18 digits" means "18 decimal digits" (note to documentation department implied) , that would only be 56 or 57 bits.

        I'd also be interested in how you determined you are not getting such precision, as both STR$ and FORMAT$ are limited to 18 or 19 decimal digits and 64 bits is about 22 or 23 decimal digits.

        That is, I'm somewhat skeptical as to the basis of your claim, as you have offered no 'failing' code in support thereof.

        Then again, your specific claim is that assignment of a numeric literal is what fails. Maybe you need to specify all 22 or 23 decimal digits to get that when you assign from a literal?

        MCM
        Michael Mattias
        Tal Systems Inc. (retired)
        Racine WI USA
        [email protected]
        http://www.talsystems.com

        Comment


        • #5
          IIRC, the PowerBASIC compilers set the precision to extended.

          Originally posted by Lance Edmonds
          ... your code to set the FPU precision is redundant in PB/CC 3.0 and PB/Win 7.0, as these compilers enforce the highest FPU precision for you anyway.
          http://www.powerbasic.com/support/pb...ead.php?t=7652

          Comment


          • #6
            Walter,
            how do you know it only has 54 bits of precision? Can you post your code that shows this?
            Looks like I get the expected 64 bits.

            Paul.

            Comment


            • #7
              Interesting

              Code:
               
              #COMPILE EXE
              #DIM ALL
              FUNCTION PBMAIN () AS LONG
                  DIM number AS SINGLE
                  number = 0.1!
                  PRINT FORMAT$(number,18)
                  PRINT FORMAT$(1.0!/10!,18)
                  PRINT
                  PRINT "Done."
                  PRINT "Press any key to exit..."
                  WAITKEY$
              END FUNCTION
              Code:
               
              .100000001490116119
              .1
               
              Done.
              Press any key to exit...
              Regards,
              Bob

              Comment


              • #8
                Walter,

                If you were reading this FAQ, notice the compiler versions it applies to. The problem was corrected in later versions.

                Comment


                • #9
                  Thanks for all the responses to my post.

                  Michael --

                  Unfortunately, the documentation must be considered somewhat incomplete since, IMHO, it should show you the memory format for the various floating point types. Nevertheless, it is my understanding that PB utilizes the IEEE standard for extended precision floating point which utilizes 1 bit for sign, 15 points for exponent and 64 bits for mantissa (precision).
                  http://en.wikipedia.org/wiki/Extended_precision
                  http://www.csee.umbc.edu/courses/und...eferences.html

                  Michael et al --

                  I used the following code to verify that casting a literal to extended precision only resulted in 52 bits precision vice 64 bits:

                  Code:
                  #DIM ALL
                  
                  FUNCTION PBMAIN () AS LONG
                  
                  DIM z AS EXT, address AS LONG
                  
                  z = 3.1415926535897932385##
                  PRINT "z = 3.1415926535897932385## = "; STR$(z,18)
                  address = VARPTR(z)
                  PRINT "memory contents: ";TAB(20)
                  FOR address = address + 9 TO address STEP -1
                      PRINT HEX$(PEEK(BYTE, address),2);SPACE$(2);
                  NEXT address
                  PRINT:PRINT "stored dec value: ";TAB(20); "3.141 5926 5358 9793 1160" 
                  'converted from stored binary value to decimal
                  'using Microsoft Power Toy Calculator
                  PRINT "pi:";TAB(20); "3.141 5926 5358 9793 2385"
                  
                  z = 4 * ATN(1)
                  PRINT:PRINT "z = 4 * atn(1)= "; STR$(z,18)
                  address = VARPTR(z)
                  PRINT "memory contents: ";TAB(20)
                  FOR address = address + 9 TO address STEP -1
                      PRINT HEX$(PEEK(BYTE, address),2);SPACE$(2);
                  NEXT address
                  PRINT:PRINT "stored dec value: ";TAB(20); "3.141 5926 5358 9793 2385
                  'converted from stored binary value to decimal
                  'using Microsoft Power Toy Calculator
                  PRINT "pi:";TAB(20); "3.141 5926 5358 9793 2385"
                  
                  WAITKEY$
                  
                  END FUNCTION
                  The program shows the 80-bit (10-byte) memory contents of the extended precision variable z (the first two bytes on the left contain the one-bit sign and 15-bit exponent; and the last 8 bytes contain the mantissa). Observe that the contents of the variable z when containing the extended precision literal has the last 1.5 bytes (12 bits) of the 8-byte mantissa set to zero. However, when z contains the results of the extended precision calculation of 4 * atn(1) [or any other PB extended precision math function, for that matter], all of the 8 bytes of the mantissa are utilized. (Please note that the conversions from stored binary value to decimal were made using the high precision of the Microsoft Power Toy Calculator. If you don't have this amazing calculator (it's free), get it.)

                  Conclusion: If I am correct in my analysis, it appears that calculations performed in extended precision will yield 64-bit precision results, IF they don't involve literals. Since literals cast as extended precision are instead stored with 52 digits, they will somewhat degrade the accuracy of any extended precision calculation involving literals. It is my hope that PB personnel -- since they are the experts -- will join the fray and clarify this issue for everyone. Unfortunately, the documentation doesn't.
                  Last edited by Walter Henn; 9 Dec 2007, 05:51 AM. Reason: Clarification

                  Comment


                  • #10
                    Walter,

                    I see what you're saying. It looks like it has to do with how the EXT literals are parsed.
                    Last edited by Greg Lyon; 9 Dec 2007, 12:35 AM.

                    Comment


                    • #11
                      Robert DeBolt -

                      Both of your literals (1.0! and 10!) can be represented exactly in binary (even with single precision) since they are whole numbers. If you were to choose 1.1! and 11! instead, you would get a result that is different from 0.1 due to the fact that 1.1! cannot be represented exactly in binary.
                      Last edited by Walter Henn; 9 Dec 2007, 12:31 AM.

                      Comment


                      • #12
                        Walter,
                        I get different results with PBCC4.01 but there does appear to be a problem.
                        The constant is only evaluated and stored to 18 decimal digits but the 80-bit format of the FPU can hold more than this (just over 19 digits) so I do see a slight error, but it's not being stored to only 54 bits, more like 59 to 60 bits.


                        The ASM code you gave from Lance will not work. That code will only apply to runtime evaluation. In this case the number (the literal) is being evaluated at compile time.


                        Paul.

                        Comment


                        • #13
                          Originally posted by Walter Henn View Post
                          If I am correct in my analysis, it appears that calculations performed in extended precision will yield 64-bit precision results, IF they don't involve literals. Since literals cast as extended precision are instead stored with 52 digits, they will somewhat degrade the accuracy of any extended precision calculation involving literals. It is my hope that PB personnel -- since they are the experts -- will join the fray and clarify this issue for everyone. Unfortunately, the documentation doesn't.
                          Walter, you might want to look at a 4 years old thread that I started on this subject and at a final answer given by Lance Edmonds ...

                          http://www.powerbasic.com/support/pb...light=Extended
                          Aldo Vitagliano
                          alvitagl at unina it

                          Comment


                          • #14
                            Thanks for your reply, Paul.

                            I should have posted the results of my code before, but I just assumed that everyone would be getting the same results.

                            Using PB 4.04 compiler:

                            z = 3.1415926535897932385## = 3.14159265358979312
                            memory contents: 40 00 C9 0F DA A2 21 68 C0 00
                            *stored dec value: 3.141 5926 5358 9793 1160
                            ************pi: 3.141 5926 5358 9793 2385

                            z = 4 * atn(1)= 3.14159265358979324
                            memory contents: 40 00 C9 0F DA A2 21 68 C2 35
                            *stored dec value: 3.141 5926 5358 9793 2385
                            ************pi: 3.141 5926 5358 9793 2385


                            I had a friend of mine run the same code with PBCC 4.03. Remarkably, the results were different:

                            z = 3.1415926535897932385## = 3.14159265358979323
                            memory contents: 40 00 C9 0F DA A2 21 68 C2 0E
                            *stored dec value: 3.141 5926 5358 9793 2301
                            ************pi: 3.141 5926 5358 9793 2385

                            z = 4 * atn(1)= 3.14159265358979324
                            memory contents: 40 00 C9 0F DA A2 21 68 C2 35
                            *stored dec value: 3.141 5926 5358 9793 2385
                            ************pi: 3.141 5926 5358 9793 2385

                            Results: For PBCC 4.04, the extended precision literal (pi) stored in z has a precision of 53 bits. For PBCC 4.03, the same extended precision literal stored in z has a precision of 57 bits.
                            It is my hope that some of the forum members will try out my code and verify that they are getting the same results using PBCC 4.04 as I am.

                            *Note: All the results above were obtained using Windows XP SP2 and a Pentium 4 processor.
                            Last edited by Walter Henn; 10 Dec 2007, 05:08 PM. Reason: Correction of precision for pi in PBCC 4.04 from 52 bits to 53 bits

                            Comment


                            • #15
                              Aldo --

                              Thanks very much for your post and your inclusion of a link to the thread you started years ago. I would also like to acknowledge, with thanks, that I borrowed 4 * ATN(1) from one of your posts.

                              Folks, I hate to have to point out the obvious, but after all these years writing compilers, would you really think that R&D would not have at least looked at the possibility and rammifications of displaying more than 18 digits? I think not. Therefore, we can conclude that there were specific reasons why an 18-digit limit was imposed.

                              But, as I explained above, if you need to display more than 18 digits, then it's possible to do so with discrete code as you show... problem solved.


                              ------------------
                              Lance
                              PowerBASIC Support
                              Regarding Lance's post, it really doesn't address the problem that a literal constant cast as extended precision does not result in 18 significant digits, as shown by the output obtained from my code using STR$(z, 18). I'm not arguing that the STR$ function doesn't produce 18 digits. It does! However, the storage of the extended precision literal to z is not done with enough precision to yield 18 significant digits from STR$(z,18).

                              I sincerely hope that forum viewers will not jump to the conclusion that this issue is too esoteric. I'm certain that many PBCC users are mathematicians, scientists, etc. to whom accuracy is not just a lofty ideal. Consequently, I'm delighted that posters to my thread have responded so kindly. Also, please be aware that I'm not, in any way, being critical of PB. I've been using it for many years and am delighted with it.
                              Last edited by Walter Henn; 9 Dec 2007, 11:12 PM.

                              Comment


                              • #16
                                Walter,

                                I got the same results you did with your code, I am using PBCC 4.04 and PBWC 8.04. I also modified your code for some further testing. The EXT literal is being parsed and stored identically to what the same literal as a DOUBLE is. I agree with you, I would like to see this problem corrected too. Have you sent it in to [email protected]?

                                Code:
                                #COMPILER PBCC 4.04
                                #DIM ALL
                                #REGISTER NONE
                                
                                FUNCTION PBMAIN () AS LONG
                                
                                    LOCAL ext1, ext2, ext3 AS EXT
                                    LOCAL dbl1, dbl2, dbl3 AS DOUBLE
                                    LOCAL address AS LONG
                                
                                    PRINT "EXTENDED"
                                    ext1 = 3.1415926535897932385##
                                    PRINT " ext1 = 3.1415926535897932385## = "; STR$(ext1,18)
                                    address = VARPTR(ext1)
                                    PRINT " memory contents: ";TAB(20)
                                    FOR address = address + 9 TO address STEP -1
                                        PRINT HEX$(PEEK(BYTE, address),2);SPACE$(2);
                                    NEXT address
                                    PRINT:PRINT
                                    
                                    ext2 = 4 * ATN(1)
                                    PRINT " ext2 = 4 * ATN(1) = "; STR$(ext2,18)
                                    address = VARPTR(ext2)
                                    PRINT " memory contents: ";TAB(20)
                                    FOR address = address + 9 TO address STEP -1
                                        PRINT HEX$(PEEK(BYTE, address),2);SPACE$(2);
                                    NEXT address
                                    PRINT:PRINT
                                    
                                    !finit
                                    !fldpi
                                    !fstp ext3
                                    PRINT " ext3 = !fldpi = "; STR$(ext3,18)
                                    address = VARPTR(ext3)
                                    PRINT " memory contents: ";TAB(20)
                                    FOR address = address + 9 TO address STEP -1
                                        PRINT HEX$(PEEK(BYTE, address),2);SPACE$(2);
                                    NEXT address
                                    PRINT:PRINT
                                    
                                    PRINT "DOUBLE"
                                    dbl1 = 3.1415926535897932385#
                                    PRINT " dbl1 = 3.1415926535897932385# = "; STR$(dbl1,18)
                                    address = VARPTR(dbl1)
                                    PRINT " memory contents: ";TAB(20)
                                    FOR address = address + 7 TO address STEP -1
                                        PRINT HEX$(PEEK(BYTE, address),2);SPACE$(2);
                                    NEXT address
                                    PRINT:PRINT
                                    
                                    dbl2 = 4 * ATN(1)
                                    PRINT " dbl2 = 4 * ATN(1) = "; STR$(dbl2,18)
                                    address = VARPTR(dbl2)
                                    PRINT " memory contents: ";TAB(20)
                                    FOR address = address + 7 TO address STEP -1
                                        PRINT HEX$(PEEK(BYTE, address),2);SPACE$(2);
                                    NEXT address
                                    PRINT:PRINT
                                    
                                    !finit
                                    !fldpi
                                    !fstp dbl3
                                    PRINT " dbl3 = !fldpi = "; STR$(dbl3,18)
                                    address = VARPTR(dbl3)
                                    PRINT " memory contents: ";TAB(20)
                                    FOR address = address + 7 TO address STEP -1
                                        PRINT HEX$(PEEK(BYTE, address),2);SPACE$(2);
                                    NEXT address
                                    PRINT:PRINT
                                        
                                    WAITKEY$
                                
                                END FUNCTION

                                Comment


                                • #17
                                  Hi Greg - Thanks for all of your posts.

                                  Your latest post is particularly helpful in that you verified my results with PBCC 4.04. If there are any forum members who have different results, please let us know. I'd also appreciate it if someone would verify my results using PBCC 4.03 which were different than those obtained with PBCC 4.04 . It would certainly help to know whether the latest patch from 4.03 to 4.04 introduced some degree of inaccuracy.

                                  I also appreciate your posting your code, which I ran. You are quite correct in observing that the memory contents (mantissa) of the literal value of pi cast to double precision and stored in the double variable is identical to the memory contents of the literal value of pi cast to extended precision and stored in the extended variable.

                                  No, I have not sent this issue in to support. I wanted to put it out to the user community and read their comments before I submitted it to PB. It would save me a lot of embarrassment -- in case I've overlooked something obvious or royally screwed up -- and it saves them time, too. I will wait a few more days to see if there are any further posts on this issue.
                                  Last edited by Walter Henn; 10 Dec 2007, 03:00 AM.

                                  Comment


                                  • #18
                                    Walter,
                                    I borrowed 4 * ATN(1) from one of your posts
                                    The FPU has pi built in as a constant so you can get pi a lot quicker this way:
                                    Code:
                                    !fldpi   'put the value of pi on the FPU stack
                                    !fstp z  'store that value in z
                                    Paul.

                                    Comment


                                    • #19
                                      I normally use PB 3.04 rather than 4.03, and here is a small piece of code that runs on both PB 3 and PB 4.
                                      Just to change a bit the soup, I switched from Pi to SQR(2)
                                      Code:
                                      DEFEXT A-Z
                                      FUNCTION PBMAIN()
                                         SQR2$ = " 1.41421356237309504880..."
                                         SQR2=1.41421356237309504880
                                         Num=SQR(2)
                                         PRINT SQR2$; " 'true value'"
                                         PRINT STR$(SQR2,18); "      from literal"
                                         PRINT STR$(Num,18); "      from computation"
                                         PRINT "   ";STR$(Num*100-141,17); "    two more digits internally recovered"
                                         WAITKEY$
                                      END FUNCTION
                                      
                                      To me, it seems that in parsing from a literal the compiler "truncates" the string, in writing the 18 digits string, the executable correctly "rounds" the 18th decimal digits.
                                      Aldo Vitagliano
                                      alvitagl at unina it

                                      Comment


                                      • #20
                                        Originally posted by Paul Dixon View Post
                                        Walter,

                                        The FPU has pi built in as a constant so you can get pi a lot quicker this way:
                                        Code:
                                        !fldpi   'put the value of pi on the FPU stack
                                        !fstp z  'store that value in z
                                        Paul.
                                        Nice. Thanks for the tip, Paul.

                                        Greg & Aldo --

                                        Thanks for the input. Your feeling that the parsing of the extended precision literal is the problem makes sense. I hadn't remembered that literals are converted at compile time.
                                        Last edited by Walter Henn; 10 Dec 2007, 06:19 PM.

                                        Comment

                                        Working...
                                        X