https://gcc.gnu.org/bugzilla/show_bug.cgi?id=123482

            Bug ID: 123482
           Summary: in -frounding-math, GCC moves floating-point operation
                    across fesetround call
           Product: gcc
           Version: unknown
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c
          Assignee: unassigned at gcc dot gnu.org
          Reporter: pascal_cuoq at hotmail dot com
  Target Milestone: ---

This issue is pretty close to
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=34678 and GCC developers may
consider it a duplicate as the same fix may fix both issues, but it seems just
distinct enough to report separately:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <fenv.h>

#ifdef __TRUSTINSOFT_ANALYZER__
#include <tis_builtin.h>
// display values even if they are not singletons:
#define printf tis_show_each
#endif


#pragma STDC FENV_ACCESS ON

int main(void)
{
    /* IEEE-754 rounding modes required by the C standard */
    const int rounding_modes[] = {
        FE_TONEAREST,
        FE_DOWNWARD,
        FE_UPWARD,
        FE_TOWARDZERO
    };

    const char *rounding_names[] = {
        "FE_TONEAREST",
        "FE_DOWNWARD",
        "FE_UPWARD",
        "FE_TOWARDZERO"
    };

    const size_t num_modes = sizeof(rounding_modes) / sizeof(int);

    /* Pick a random rounding mode */
//    srand((unsigned)time(NULL));
//    size_t idx = (size_t)(rand() % num_modes);
    size_t idx = 2;
    int mode = rounding_modes[idx];

    /* Set rounding mode */
    fesetround(mode);

    /* Operands */
    double a = 0.1;
    double b = 0.3;

    /* Multiplication performed under selected rounding mode */
    double result = a * b;

    /* Set mode back to NE for printing */
    fesetround(FE_TONEAREST);

    printf("Rounding mode: %s\n", rounding_names[idx]);
    printf("a = %.17g\n", a);
    printf("b = %.17g\n", b);
    printf("a * b = %.17g\n", result);
}

CE link: https://godbolt.org/z/TKse9MKs3

GCC 15.2 prints:
Rounding mode: FE_UPWARD
a = 0.10000000000000001
b = 0.29999999999999999
a * b = 0.029999999999999999

I expected:
Rounding mode: FE_UPWARD
a = 0.10000000000000001
b = 0.29999999999999999
a * b = 0.030000000000000002

Looking at the assembly code, the multiplication has been re-ordered after the
second call to fesetround. Unfortunately, in the context of this program, the
second call is necessary, as the rounding mode influences the binary-to-decimal
conversion behavior of printf:

main:
        subq    $8, %rsp
        movl    $2048, %edi
        call    fesetround    ;;;;;;;;;;;;;; HERE
        xorl    %edi, %edi
        call    fesetround    ;;;;;;;;;;;;;; HERE
        movl    $.LC0, %esi
        movl    $.LC1, %edi
        xorl    %eax, %eax
        call    printf
        movl    $.LC3, %edi
        movl    $1, %eax
        movsd   .LC2(%rip), %xmm0
        call    printf
        movl    $.LC5, %edi
        movl    $1, %eax
        movsd   .LC4(%rip), %xmm0
        call    printf
        movl    $.LC6, %edi
        movl    $1, %eax
        movsd   .LC4(%rip), %xmm0
        mulsd   .LC2(%rip), %xmm0    ;;;;;;;;;;;;;; HERE
        call    printf
        xorl    %eax, %eax
        addq    $8, %rsp
        ret

Reply via email to