[HN Gopher] Weird compiler bug - Same code, different results
       ___________________________________________________________________
        
       Weird compiler bug - Same code, different results
        
       Author : zaitanz
       Score  : 38 points
       Date   : 2021-01-29 21:24 UTC (1 hours ago)
        
 (HTM) web link (blog.zaita.com)
 (TXT) w3m dump (blog.zaita.com)
        
       | [deleted]
        
       | lysium wrote:
       | I would have never thought that the output of some floating point
       | operations depend on the ,,reset of the floating point package".
       | What gives?
        
         | comex wrote:
         | The processor itself supports configuring some aspects of
         | floating point behavior; on x86 this is the MXCSR register. The
         | most important one is rounding mode: you can choose round-to-
         | nearest, round-down, round-up, or round-towards-zero. Less
         | common options include whether to raise an exception on various
         | out-of-range conditions, and whether to treat denormal numbers
         | as zero (faster but less accurate and not IEEE 754 compliant).
         | 
         | The C standard library has functions to do this configuration;
         | search for fesetenv. _fpreset is not a standard function but,
         | at least in Wine's implementation [1], just resets the relevant
         | configuration registers to an initial state.
         | 
         | [1] https://github.com/wine-
         | mirror/wine/blob/e909986e6ea5ecd49b2...
        
         | brucehoult wrote:
         | At a minimum, when there is a switch between threads the
         | floating point state of the old thread needs to be saved and
         | the floating point state of the incoming thread loaded. This
         | includes such things as rounding mode.
         | 
         | If the thread package was not setting up the "saved" state for
         | a new thread before switching to it then that state could be
         | not what was desired. The best thing to do would probably be to
         | copy the current FP state of the parent at the moment the
         | thread is created.
         | 
         | On a long series of calculations changing the rounding mode
         | could well be enough to cause the difference in results the
         | poster saw.
         | 
         | I would tend to the opinion that redoing the calculation with
         | different rounding modes is in fact a not bad way to figure out
         | how reliable the results are in the first place.
         | 
         | (the rounding mode is not the only thing affected by not saving
         | and restoring the FP state correctly)
        
         | willxinc wrote:
         | Things like rounding mode and denornal behavior could be
         | controlled by CPU flags. Imy not familiar with x86, but this is
         | defiy the case in PPC.
        
         | cperciva wrote:
         | Probably the author is accidentally getting 80-bit "extended
         | double" arithmetic.
        
         | wiml wrote:
         | Some kind of internal state specific to Microsoft's libm? It
         | doesn't look like _fpreset() is present anywhere else.
         | 
         | Though in general, IEEE-754 math does have a small amount of
         | global state, for stuff like rounding modes and subnormals.
         | Perhaps the spawned thread's fenv was unexpected.
         | 
         | I'm not super convinced by this blog post anyway, since it
         | immediately confuses associativity with commutativity.
        
       | brucehoult wrote:
       | If you read the article you find that this is a bug in MinGW64
       | libraries.
       | 
       | It's not a compiler bug, it's not a problem in C/C++, or even in
       | IEEE-754 floating point math. The MinGW64 thread library simply
       | forgot to initialize the FPU correctly.
       | 
       | He'd have had the same problem if he wrote his code in assembly
       | language and then ran it in a MinGW64 thread.
        
       | gus_massa wrote:
       | > _This means that floating point arithmetic is non-associative.
       | In that A + B != B + A._
       | 
       | The equation is the commutative property, not associative. IIRC
       | addition in IEEE-754 is commutative.
       | 
       | The property that fails is (A+B)+C = A+(B+C).
        
       | titzer wrote:
       | Do _not_ use C /C++ for numerical code where accuracy is needed.
       | These languages are not specified to conform to IEEE 754 and you
       | are absolutely asking for trouble.
        
         | parekhnish wrote:
         | So, what _should_ be used?
        
           | cratermoon wrote:
           | COBOL
           | 
           | Seriously? Anything but floating point if you care about
           | precision and accuracy.
        
         | jcranmer wrote:
         | In practice, all of the C compilers will (or can be made to)
         | conform to IEEE 754, although #pragma STDC FENV_ACCESS ON
         | support is very spotty (and consequently non-default rounding-
         | mode support), although Clang/LLVM has been working on this for
         | the past several months.
         | 
         | (That reminds me, I need to harangue the llvm-libc folks to add
         | that pragma to their fpenv code for correctness sake).
        
         | cperciva wrote:
         | My copy of C11 says                  Annex F        (normative)
         | IEC 60559 floating-point arithmetic             F.1
         | Introduction        This annex specifies C language support for
         | the IEC        60559 floating-point standard [...] previously
         | designated        ANSI/IEEE 754-1985.
        
           | titzer wrote:
           | C "supports" whatever the hardware and compiler feel like
           | supporting. It absolutely does not mandate anything. In
           | particular, there are a number of particularly simple
           | compiler optimizations that are not forbidden, though they
           | are not technically correct according to IEEE 754, such as
           | algebraic reassociation and simple commutativity. Moreover, C
           | allows subexpressions to be computed in higher precision
           | (e.g. 80 bit "long double"), which is observable. That last
           | one is primarily due to the x87 FPU coprocessor design that
           | has given us a good 35 years of headaches. Good riddance to
           | that!
        
           | shakna wrote:
           | > Support for Annex F (IEEE-754 / IEC 559) of C99/C11
           | 
           | > The Clang compiler does not support IEC 559 math
           | functionality. Clang also does not control and honor the
           | definition of __STDC_IEC_559__ macro. Under specific options
           | such as -Ofast and -ffast-math, the compiler will enable a
           | range of optimizations that provide faster mathematical
           | operations that may not conform to the IEEE-754
           | specifications. The macro __STDC_IEC_559__ value may be
           | defined but ignored when these faster optimizations are
           | enabled.
           | 
           | If you make use of Clang, you won't find support for Annex F,
           | and in point of fact you can't even macro check to see if it
           | even will support Annex F.
           | 
           | The story with GCC is... More complicated. It guarantees
           | it'll follow Annex F for only some operations [0].
           | 
           | So the upshot is... Most people can't tell when and how Annex
           | F might be followed.
           | 
           | [0] https://gcc.gnu.org/onlinedocs/gcc-7.4.0/gcc/Floating-
           | point-...
        
       ___________________________________________________________________
       (page generated 2021-01-29 23:01 UTC)