epsilon-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[epsilon-devel] Tagged floating-point corrections, and SPARC [Was: Re: U


From: Luca Saiu
Subject: [epsilon-devel] Tagged floating-point corrections, and SPARC [Was: Re: Unboxed tagged floating-point data in C: a (new?) efficient and portable solution]
Date: Sat, 13 Apr 2019 22:15:49 +0200
User-agent: Gnus (Gnus v5.13), GNU Emacs 25.1.50.2, x86_64-unknown-linux-gnu

On 2019-04-13 at 13:05 +0200, Luca Saiu wrote:

> On 2019-04-13 at 12:52 +0200, Luca Saiu wrote:
>
>> It compiles and works with every architecture I have played with
>> (x86_64, i386, mips, powerpc, riscv, sh, alpha, where applicable on
>> different word size and endianness).
>
> I had always forgotten to test on arm and aarch64, for some reason.
> [...]

Another architecture I had forgotten to test was SPARC.  Old versions
had the same limitation as i386 but I've found about the new
movstosw/movdtox instructions in recent specifications, and GCC
generates these at least with -mcpu=m8.  Even if I cannot easily test M8
code with QEmu the code looks correct.

About the first message, here are a few obvious corrections: [FIXME: did
the anonymous friend point out these as well?  Give thanks in public if
he did]

#define f2u(a)                   \
  (((union conv) {.f = (a)}).u)

#define u2f(a)                   \
  (((union conv) {.u = (a)}).f)

I'm attaching an updated copy of the testing file, including the fixes
above plus others.

-- 
Luca Saiu
* GNU epsilon:           http://www.gnu.org/software/epsilon
* My personal web site:  http://ageinghacker.net

I support everyone's freedom of mocking any opinion or belief, no
matter how deeply held, with open disrespect and the same unrelented
enthusiasm of a toddler who has just learned the word "poo".
/* Written by Luca Saiu  http://ageinghacker.net
   I, the author, hereby place this code into the public domain
   up to the extent of the applicable law. */

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
#include <fenv.h>
#include <limits.h>
#include <assert.h>
#include <signal.h>

#define FTYPE_IS_FLOAT

#ifdef FTYPE_IS_FLOAT
# define FTYPE float
# define UTYPE uint32_t
#else
# define FTYPE double
# define UTYPE uint64_t
#endif

#define TAG_BIT_NO 4
#define TAGMASK    ((UTYPE) (1 << TAG_BIT_NO) - 1)
#define TAG        TAGMASK // All tag bits set to 1.

union conv
{
  FTYPE f;
  UTYPE u;
};

void
print_bits_u (UTYPE a)
{
  int i;
  for (i = sizeof (UTYPE) * CHAR_BIT - 1; i >= 0; i --)
    {
      UTYPE mask = 1LLU << i;
      if (a & mask)
        printf ("1");
      else
        printf ("0");
    }
}

void
print_bits_f (FTYPE a)
{
  union conv c0;
  c0.f = a;
  print_bits_u (c0.u);
}

__attribute__ ((const))
FTYPE
untag__ (UTYPE a)
{
  union conv c;
  c.u = a;
  /* This is correct. */
  //c.u &= ~ ((1 << TAG_BIT_NO) - 1);

  /* This alternative is equally correct, assuming that the argument is actually
     tagged, as it should be, and faster on MIPS and SH.

     On MIPS the immediate variant of the bitwise-anding instruction takes a
     zero-extended, rather than sign-extended, argument; therefore it cannot be
     used here where the mask would need sign-extension to fit in a 16-bit
     immediate.  GCC solves this the obvious way, by loading a literal into a
     temporary with one separate instruction before and-ing two registers into
     another register.  On the other hand, any sensible architecture, MIPS
     included, has a sum or subtract instruction taking a sign-extended literal.

     SH is even more restricted: it allows and-ing an immediate (again,
     zero-extended) only when the operand (source and destination) is r0.
     Replacing the and with an add (sign-extended immediate, no register
     restriction) solves the problem. */
  c.u -= TAG;
  return c.f;
}

/* This is correct in Standard C99, relying on compound literals, designated
   initializers and non-constant initializers, but not on GNU C expression
   statements. */

#define f2u(a)  \
 (((union conv) {.f = (a)}).u)
#define u2f(a)  \
 (((union conv) {.u = (a)}).f)

#define untag_alt(a)      \
  (((union conv)          \
    {.u = (a) - TAG}).f)

#define untag_stmtexp(a)  \
  ({                      \
    union conv _c;        \
    _c.f = (a);           \
    _c.u &= ~ TAGMASK;    \
    _c.f;                 \
  })

#define untag(a) \
  (u2f (a - TAG))
#define tag(a) \
  ((f2u (a) & ~ TAGMASK) | TAG)

__attribute__ ((const))
UTYPE
tag_ (FTYPE a)
{
  return tag (a);
}

__attribute__ ((const))
FTYPE
untag_ (UTYPE a)
{
  return untag (a);
}

void print_float (FTYPE a)
{
  printf ("%-26.15f ", a);
  print_bits_f (a);
  //feclearexcept (FE_ALL_EXCEPT);
  if (! iscanonical (a))
    printf (" NC");
  if (! isfinite (a) && ! isnan (a))
    printf (" I");
  //if (isnormal (a))
  //  printf (" n");
  if (issignaling (a))
    printf (" S");
  if (issubnormal (a))
    printf (" s");
  if (isnan (a))
    printf (" N");
  if (iszero (a))
    printf (" Z");
fflush (stdout);
}

double
worst_error = 0.0;

__attribute__ ((noinline, noclone))
void
test (FTYPE a)
{
  // assert (untag (tag (a)) == untag (a));
  FTYPE t = untag (tag (a));
  printf ("*   "); print_float (a); printf ("\n");
  printf ("  ->"); print_float (t); printf ("\n");
  if (isfinite (a)
      && isfinite (t)
      && a != 0)
    {
      double error = fabs ((a - t) / a);
      if (error > worst_error)
        worst_error = error;
      if ((a - t) != 0)
        printf ("      (error: %.20f%%)\n", error * 100);
      else
        printf ("      (error: zero)\n");
    }
}

int
main (void)
{
#ifdef FE_SNANS_ALWAYS_SIGNAL
  printf ("FE_SNANS_ALWAYS_SIGNAL is defined.\n");
#endif // #ifdef FE_SNANS_ALWAYS_SIGNAL
  assert (sizeof (FTYPE) == sizeof (UTYPE));

  /*
  // FIXME: with POSIX threads I should use pthread_sigmask instead of 
sigprocmask.
  sigset_t signal_set;
  sigprocmask (0, NULL, & signal_set);
  printf ("SIGNAL MASK BEFORE: %lu\n", * (unsigned long *) &signal_set);
  sigaddset (& signal_set, SIGFPE);
  printf ("SIGNAL MASK AFTER:  %lu\n", * (unsigned long *) &signal_set);
  sigprocmask (SIG_BLOCK, & signal_set, NULL);
  */

  test (0.0);
  test (- 0.0);
  test (1.0);
  test (- 1.0);
  test (M_PI);
  test (- M_PI);
  test (INFINITY);
  test (- INFINITY);
  test (NAN);
  test (SNAN);
  test (1234567890.);
  test (123456789.0);
  test (12345678.90);
  test (1234567.890);
  test (123456.7890);
  test (12345.67890);
  test (1234.567890);
  test (123.4567890);
  test (12.34567890);
  test (1.234567890);
  test (.1234567890);
  test (.01234567890);
  test (.001234567890);
  test (.0001234567890);
  test (.00001234567890);
  test (.000001234567890);
  test (.0000001234567890);
  test (.00000001234567890);
  test (.000000001234567890);
  test ((double) (1LL << 23) + (1 << TAG_BIT_NO) - 1); // worst possible 
relative error on 32-bit float
  test ((double) (1LL << 52) + (1 << TAG_BIT_NO) - 1); // worst possible 
relative error on 64-bit double
  printf ("Worst error: %.20f%%\n", worst_error * 100);
  return 0;
}

Attachment: signature.asc
Description: PGP signature


reply via email to

[Prev in Thread] Current Thread [Next in Thread]