Skip to content

Commit

Permalink
Buitlin NAN
Browse files Browse the repository at this point in the history
Sign of `0.0 / 0.0` is unstable:
  * gcc: `-nan`
  * clang: `nan`
  • Loading branch information
tyfkda committed Apr 17, 2024
1 parent aacea25 commit a3d650c
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 20 deletions.
9 changes: 5 additions & 4 deletions include/math.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@
#define M_LOG10E (0.434294481903252)
#define M_LN2 (0.693147180559945)
#define M_LN10 (2.302585092994046)
#define NAN (0.0 / 0.0)
#define HUGE_VAL (1.0 / 0.0)
#define NAN (__builtin_nan("0x7ff8000000000000"))
#define INFINITY (1.0 / 0.0)
#define HUGE_VAL INFINITY

double sin(double);
double cos(double);
Expand Down Expand Up @@ -77,9 +78,9 @@ double copysign(double x, double f);
})

#define signbit(x) ({ \
union { double d; uint64_t xx; } __u; \
union { double d; uint64_t q; } __u; \
__u.d = (x); \
__u.d != 0 ? __u.d < 0 : __u.xx >> 63; \
__u.q >> 63; \
})

#endif
Expand Down
2 changes: 0 additions & 2 deletions libsrc/math/copysign.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ double copysign(double x, double f) {
S(OP_LOCAL_GET) ",1," // local.get 1
S(OP_F64_COPYSIGN)); // f64.copysign
#else
if (isnan(x))
return x;
union { double d; int64_t q; } u;
u.d = x;
u.q = (u.q & ~SIGN_MASK) | ((uint64_t)signbit(f) << SIGN_POS);
Expand Down
2 changes: 0 additions & 2 deletions libsrc/math/signbit.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
#ifndef __NO_FLONUM
#undef signbit
int signbit(double x) {
if (x != 0)
return x < 0;
return ((*(uint64_t*)&x) >> (EXPO_POS + EXPO_BIT));
}
#endif
21 changes: 12 additions & 9 deletions libsrc/stdio/vfprintf.c
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ static int sprintsign(FILE *fp, bool negative, bool force, int *porder) {
}

#ifndef __NO_FLONUM
#include "math.h" // isfinite
#include "math.h" // isfinite, signbit

static long double pow10(int order) {
long double a = 1, x = 10;
Expand All @@ -114,12 +114,17 @@ static long double pow10(int order) {
return a;
}

static int printnonfinite(FILE *fp, long double x,
int order, int suborder, bool sign, bool leftalign, char padding) {
static const char *ss[] = { "inf", "-inf", "nan", "-nan" };
int index = (isnan(x) ? 2 : 0) | (signbit(x) ? 1 : 0);
return snprintstr(fp, ss[index], order, suborder, leftalign, ' ');
}

static int printfloat(FILE *fp, long double x,
int order, int suborder, bool sign, bool leftalign, char padding) {
if (!isfinite(x)) {
const char *s = isnan(x) ? "nan" : x > 0 ? "inf" : "-inf";
return snprintstr(fp, s, order, suborder, leftalign, ' ');
}
if (!isfinite(x))
return printnonfinite(fp, x, order, suborder, sign, leftalign, padding);

bool neg = signbit(x);
if (neg)
Expand Down Expand Up @@ -161,10 +166,8 @@ static int normalize_float(long double *px) {

static int printscientific(FILE *fp, long double x, int order, int suborder, bool sign,
bool leftalign, char padding) {
if (!isfinite(x)) {
const char *s = isnan(x) ? "nan" : x > 0 ? "inf" : "-inf";
return snprintstr(fp, s, order, suborder, leftalign, ' ');
}
if (!isfinite(x))
return printnonfinite(fp, x, order, suborder, sign, leftalign, padding);

bool neg = signbit(x);
if (neg)
Expand Down
16 changes: 16 additions & 0 deletions libsrc/tests/math_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,26 @@ TEST(isinf) {

TEST(isnan) {
EXPECT_TRUE(isnan(NAN));
EXPECT_TRUE(isnan(copysign(NAN, -1)));
EXPECT_FALSE(isnan(1.23));
EXPECT_FALSE(isnan(0.0));
EXPECT_FALSE(isnan(HUGE_VAL));
} END_TEST()

TEST(copysign_signbit) {
double pzero = 0.0;
double nzero = -0.0;
EXPECT_DEQ(123, copysign(-123, pzero));
EXPECT_DEQ(-456, copysign(+456, nzero));
EXPECT_EQ(1, signbit(copysign(pzero, -1)));
EXPECT_EQ(0, signbit(copysign(nzero, +1)));

EXPECT_EQ(0, signbit(NAN));
double nnan = copysign(NAN, -1);
EXPECT_EQ(1, signbit(nnan));
EXPECT_EQ(0, signbit(copysign(nnan, +1)));
} END_TEST()

TEST(negative_zero) {
double nzero = -0.0;
EXPECT_TRUE(nzero == 0.0);
Expand All @@ -157,6 +172,7 @@ int main() {
test_frexp,
test_isinf,
test_isnan,
test_copysign_signbit,
test_negative_zero,
);
}
Expand Down
41 changes: 39 additions & 2 deletions src/cc/builtin.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
#include "../config.h"
#include <assert.h>
#include <stdlib.h>

#ifndef __NO_FLONUM
#include <math.h>
#endif

#include "arch_config.h"
#include "ast.h"
Expand All @@ -21,6 +26,33 @@ static Expr *proc_builtin_type_kind(const Token *ident) {
return new_expr_fixlit(&tySize, ident, type->kind);
}

#ifndef __NO_FLONUM
static Expr *proc_builtin_nan(const Token *ident) {
consume(TK_LPAR, "`(' expected");
Expr *fmt = parse_expr();
consume(TK_RPAR, "`)' expected");

uint64_t significand = 0;
if (fmt->kind == EX_STR) {
const char *p = fmt->str.buf;
int base = 10;
if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
p += 2;
base = 16;
}
significand = strtoull(p, NULL, base);
} else {
parse_error(PE_NOFATAL, fmt->token, "String literal expected");
}

const uint64_t MASK = (1UL << 52) - 1UL;
union { double d; uint64_t q; } u;
u.d = NAN;
u.q = (u.q & ~MASK) | (significand & MASK);
return new_expr_flolit(&tyDouble, ident, u.d);
}
#endif

#if VAARG_ON_STACK
static VReg *gen_builtin_va_start(Expr *expr) {
assert(expr->kind == EX_FUNCALL);
Expand Down Expand Up @@ -266,8 +298,13 @@ static VReg *gen_alloca(Expr *expr) {
}

void install_builtins(void) {
static BuiltinExprProc p_reg_class = &proc_builtin_type_kind;
add_builtin_expr_ident("__builtin_type_kind", &p_reg_class);
static BuiltinExprProc p_type_kind = &proc_builtin_type_kind;
add_builtin_expr_ident("__builtin_type_kind", &p_type_kind);

#ifndef __NO_FLONUM
static BuiltinExprProc p_nan = &proc_builtin_nan;
add_builtin_expr_ident("__builtin_nan", &p_nan);
#endif

{
#if VAARG_ON_STACK || XCC_TARGET_ARCH == XCC_ARCH_RISCV64
Expand Down
38 changes: 37 additions & 1 deletion src/wcc/gen_wasm.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@
#include <assert.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h> // free
#include <stdlib.h> // free, strtoull
#include <string.h>

#ifndef __NO_FLONUM
#include <math.h>
#endif

#include "ast.h"
#include "fe_misc.h" // curfunc
#include "parser.h"
Expand Down Expand Up @@ -1853,6 +1857,33 @@ static void gen_builtin_try_catch_longjmp(Expr *expr, enum BuiltinFunctionPhase
} ADD_CODE(OP_END);
}

#ifndef __NO_FLONUM
static Expr *proc_builtin_nan(const Token *ident) {
consume(TK_LPAR, "`(' expected");
Expr *fmt = parse_expr();
consume(TK_RPAR, "`)' expected");

uint64_t significand = 0;
if (fmt->kind == EX_STR) {
const char *p = fmt->str.buf;
int base = 10;
if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
p += 2;
base = 16;
}
significand = strtoull(p, NULL, base);
} else {
parse_error(PE_NOFATAL, fmt->token, "String literal expected");
}

const uint64_t MASK = (1UL << 52) - 1UL;
union { double d; uint64_t q; } u;
u.d = NAN;
u.q = (u.q & ~MASK) | (significand & MASK);
return new_expr_flolit(&tyDouble, ident, u.d);
}
#endif

static Expr *proc_builtin_va_start(const Token *ident) {
if (curfunc == NULL || !curfunc->type->func.vaargs) {
parse_error(PE_FATAL, ident, "`va_start' can only be used in a variadic function");
Expand Down Expand Up @@ -1998,6 +2029,11 @@ void install_builtins(void) {
add_typedef(global_scope, name, type);
}

#ifndef __NO_FLONUM
static BuiltinExprProc p_nan = &proc_builtin_nan;
add_builtin_expr_ident("__builtin_nan", &p_nan);
#endif

static BuiltinExprProc p_va_start = &proc_builtin_va_start;
static BuiltinExprProc p_va_end = &proc_builtin_va_end;
static BuiltinExprProc p_va_arg = &proc_builtin_va_arg;
Expand Down

0 comments on commit a3d650c

Please sign in to comment.