aboutsummaryrefslogtreecommitdiff
path: root/src/stdlib
diff options
context:
space:
mode:
Diffstat (limited to 'src/stdlib')
-rw-r--r--src/stdlib/datatypes.h3
-rw-r--r--src/stdlib/decimals.c293
-rw-r--r--src/stdlib/decimals.h58
-rw-r--r--src/stdlib/print.c93
-rw-r--r--src/stdlib/print.h4
-rw-r--r--src/stdlib/stdlib.c29
-rw-r--r--src/stdlib/tomo.h1
7 files changed, 474 insertions, 7 deletions
diff --git a/src/stdlib/datatypes.h b/src/stdlib/datatypes.h
index fce1ea74..8ca2876d 100644
--- a/src/stdlib/datatypes.h
+++ b/src/stdlib/datatypes.h
@@ -22,6 +22,9 @@
#define Num_t double
#define Num32_t float
+#define Dec_t _Decimal64
+#define OptionalDec_t _Decimal64
+
#define Int64_t int64_t
#define Int32_t int32_t
#define Int16_t int16_t
diff --git a/src/stdlib/decimals.c b/src/stdlib/decimals.c
new file mode 100644
index 00000000..4a3f0c08
--- /dev/null
+++ b/src/stdlib/decimals.c
@@ -0,0 +1,293 @@
+// Integer type infos and methods
+#include <stdio.h> // Must be before gmp.h
+
+#include <ctype.h>
+#include <gc.h>
+#include <math.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "bytes.h"
+#include "datatypes.h"
+#include "decimals.h"
+#include "integers.h"
+#include "lists.h"
+#include "nums.h"
+#include "optionals.h"
+#include "print.h"
+#include "text.h"
+#include "types.h"
+
+public int Dec$print(FILE *f, Dec_t d) {
+ return fprint(f, d);
+}
+
+public Text_t Dec$value_as_text(Dec_t d) {
+ return Text$from_str(String(d));
+}
+
+public Text_t Dec$as_text(const void *d, bool colorize, const TypeInfo_t *info) {
+ (void)info;
+ if (!d) return Text("Dec");
+ Text_t text = Text$from_str(String(*(Dec_t*)d));
+ if (colorize) text = Text$concat(Text("\x1b[35m"), text, Text("\x1b[m"));
+ return text;
+}
+
+static bool Dec$is_none(const void *d, const TypeInfo_t *info)
+{
+ (void)info;
+ return *(int64_t*)d == -1;
+}
+
+public CONSTFUNC int32_t Dec$compare_value(const Dec_t x, const Dec_t y) {
+ return (x > y) - (x < y);
+}
+
+public CONSTFUNC int32_t Dec$compare(const void *x, const void *y, const TypeInfo_t *info) {
+ (void)info;
+ return Dec$compare_value(*(Dec_t*)x, *(Dec_t*)y);
+}
+
+public CONSTFUNC bool Dec$equal_value(const Dec_t x, const Dec_t y) {
+ return x == y;
+}
+
+public CONSTFUNC bool Dec$equal(const void *x, const void *y, const TypeInfo_t *info) {
+ (void)info;
+ return *(_Decimal64*)x == *(_Decimal64*)y;
+}
+
+public CONSTFUNC Dec_t Dec$plus(Dec_t x, Dec_t y) {
+ return x + y;
+}
+
+public CONSTFUNC Dec_t Dec$negative(Dec_t x) {
+ return -x;
+}
+
+public CONSTFUNC Dec_t Dec$minus(Dec_t x, Dec_t y) {
+ return x - y;
+}
+
+public CONSTFUNC Dec_t Dec$times(Dec_t x, Dec_t y) {
+ return x * y;
+}
+
+public CONSTFUNC Dec_t Dec$divided_by(Dec_t x, Dec_t y) {
+ return x / y;
+}
+
+public CONSTFUNC Dec_t Dec$modulo(Dec_t x, Dec_t modulus) {
+ // TODO: improve the accuracy of this approach:
+ return (Dec_t)Num$mod((double)x, (double)modulus);
+}
+
+public CONSTFUNC Dec_t Dec$modulo1(Dec_t x, Dec_t modulus) {
+ // TODO: improve the accuracy of this approach:
+ return (Dec_t)Num$mod1((double)x, (double)modulus);
+}
+
+public PUREFUNC OptionalDec_t Dec$from_str(const char *str) {
+ _Decimal64 n = 0.0DD;
+ const char *p = str;
+ bool negative = (*p == '-');
+ if (negative)
+ p += 1;
+ for (; *p; p++) {
+ if (*p == '.') break;
+ if (*p == '_') continue;
+ if (!isdigit(*p)) return NONE_DEC;
+ n = 10.0DD * n + (_Decimal64)(*p - '0');
+ }
+ _Decimal64 denominator = 1.0DD;
+ for (; *p; p++) {
+ if (*p == '_') continue;
+ if (!isdigit(*p)) return NONE_DEC;
+ n = 10.0DD * n + (_Decimal64)(*p - '0');
+ denominator *= 0.1DD;
+ }
+ return n * denominator;
+}
+
+public CONSTFUNC Dec_t Dec$from_int64(int64_t i) {
+ return (_Decimal64)i;
+}
+
+public Dec_t Dec$from_int(Int_t i) {
+ if likely (i.small & 1L) {
+ return Dec$from_int64(i.small >> 2L);
+ }
+ Text_t text = Int$value_as_text(i);
+ const char *str = Text$as_c_string(text);
+ return Dec$from_str(str);
+}
+
+CONSTFUNC public Dec_t Dec$from_num(double n) {
+ return (_Decimal64)n;
+}
+
+public Int_t Dec$as_int(Dec_t d, bool truncate) {
+ char *str = String(d);
+ char *decimal = strchr(str, '.');
+ if (!truncate && decimal)
+ fail("Could not convert to an integer without truncation: ", str);
+ *decimal = '\0';
+ return Int$from_str(str);
+}
+
+public int64_t Dec$as_int64(Dec_t d, bool truncate) {
+ return Int64$from_int(Dec$as_int(d, truncate), truncate);
+}
+
+public int32_t Dec$as_int32(Dec_t d, bool truncate) {
+ return Int32$from_int(Dec$as_int(d, truncate), truncate);
+}
+
+public int16_t Dec$as_int16(Dec_t d, bool truncate) {
+ return Int16$from_int(Dec$as_int(d, truncate), truncate);
+}
+
+public int8_t Dec$as_int8(Dec_t d, bool truncate) {
+ return Int8$from_int(Dec$as_int(d, truncate), truncate);
+}
+
+public Byte_t Dec$as_byte(Dec_t d, bool truncate) {
+ return Byte$from_int(Dec$as_int(d, truncate), truncate);
+}
+
+CONSTFUNC public bool Dec$as_bool(Dec_t d) {
+ return d != 0.0DD;
+}
+
+public double Dec$as_num(Dec_t d) {
+ const char *str = String(d);
+ return strtod(str, NULL);
+}
+
+#define NAN_MASK 0x7C00000000000000UL
+#define INF_MASK 0x7800000000000000UL
+#define DEC_BITS(n) ((union { uint64_t bits; _Decimal64 d; }){.d=n}).bits
+
+static bool Dec$isfinite(Dec_t d) {
+ uint64_t bits = DEC_BITS(d);
+ return (((bits & NAN_MASK) != NAN_MASK) &&
+ ((bits & INF_MASK) != INF_MASK));
+}
+
+static bool Dec$isnan(Dec_t d) {
+ uint64_t bits = DEC_BITS(d);
+ return ((bits & NAN_MASK) == NAN_MASK);
+}
+
+CONSTFUNC static Dec_t Dec$int_power(Dec_t x, int64_t exponent)
+{
+ if (exponent == 0) {
+ return 1.DD;
+ } else if (exponent == 1) {
+ return x;
+ } else if (exponent % 2 == 0) {
+ Dec_t y = Dec$int_power(x, exponent/2);
+ return y*y;
+ } else {
+ return x * Dec$int_power(x, exponent - 1);
+ }
+}
+
+public Dec_t Dec$power(Dec_t x, Dec_t y) {
+ if (x == 0.DD && y < 0.DD)
+ fail("The following math operation is not supported: ", x, "^", y);
+
+ /* For any y, including a NaN. */
+ if (x == 1.DD)
+ return x;
+
+ if (Dec$isnan(x) || Dec$isnan(y))
+ return NONE_DEC;
+
+ if (y == 0.DD)
+ return 1.DD;
+
+ if (x < 0.DD && y < 0.DD) {
+ return NONE_DEC;
+ } else if (x == 0.DD) {
+ return y < 0.DD ? NONE_DEC : 0.DD;
+ } else if (!Dec$isfinite(x)) {
+ return y < 0.DD ? 0.DD : x;
+ }
+
+ int64_t int_y = (int64_t)y;
+ if ((Dec_t)int_y == y)
+ return Dec$int_power(x, int_y);
+
+ // TODO: improve the accuracy of this approach:
+ return (Dec_t)powl((long double)x, (long double)y);
+}
+
+public Dec_t Dec$round(Dec_t d, Int_t digits) {
+ int64_t digits64 = Int64$from_int(digits, false);
+ if (digits.small != 1L) {
+ for (int64_t i = digits64; i > 0; i--)
+ d *= 10.0DD;
+ for (int64_t i = digits64; i < 0; i++)
+ d *= 0.1DD;
+ }
+ _Decimal64 truncated = (_Decimal64)(int64_t)d;
+ _Decimal64 difference = (d - truncated);
+ _Decimal64 rounded;
+ if (difference < 0.0DD) {
+ rounded = (difference < -0.5DD) ? truncated - 1.0DD : truncated;
+ } else {
+ rounded = (difference >= 0.5DD) ? truncated + 1.0DD : truncated;
+ }
+ for (int64_t i = digits64; i > 0; i--)
+ rounded *= 0.1DD;
+ for (int64_t i = digits64; i < 0; i++)
+ rounded *= 10.0DD;
+ return rounded;
+}
+
+public OptionalDec_t Dec$parse(Text_t text) {
+ return Dec$from_str(Text$as_c_string(text));
+}
+
+static void Dec$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *info)
+{
+ (void)info;
+ Dec_t d = *(Dec_t*)obj;
+ char *str = String(d);
+ int64_t len = (int64_t)strlen(str);
+ Int64$serialize(&len, out, pointers, &Int64$info);
+ if (fwrite(str, sizeof(char), (size_t)len, out) != (size_t)len)
+ fail("Could not serialize Dec value!");
+}
+
+static void Dec$deserialize(FILE *in, void *obj, List_t *pointers, const TypeInfo_t *info)
+{
+ (void)info;
+ int64_t len = 0;
+ Int64$deserialize(in, &len, pointers, &Int64$info);
+ assert(len >= 0);
+ char buf[len];
+ if (fread(buf, sizeof(char), (size_t)len, in) != (size_t)len)
+ fail("Could not deserialize Dec value!");
+ Dec_t d = Dec$from_str(buf);
+ memcpy(obj, &d, sizeof(d));
+}
+
+public const TypeInfo_t Dec$info = {
+ .size=sizeof(Dec_t),
+ .align=__alignof__(Dec_t),
+ .metamethods={
+ .compare=Dec$compare,
+ .equal=Dec$equal,
+ .as_text=Dec$as_text,
+ .is_none=Dec$is_none,
+ .serialize=Dec$serialize,
+ .deserialize=Dec$deserialize,
+ },
+};
+
+// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
diff --git a/src/stdlib/decimals.h b/src/stdlib/decimals.h
new file mode 100644
index 00000000..d6eeb332
--- /dev/null
+++ b/src/stdlib/decimals.h
@@ -0,0 +1,58 @@
+#pragma once
+
+// Integer type infos and methods
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "print.h"
+#include "datatypes.h"
+#include "stdlib.h"
+#include "types.h"
+#include "util.h"
+
+#define NONE_DEC (((union { int64_t i; _Decimal64 d; }){.i=-1}).d)
+
+int Dec$print(FILE *f, Dec_t d);
+Text_t Dec$value_as_text(Dec_t d);
+Text_t Dec$as_text(const void *d, bool colorize, const TypeInfo_t *info);
+CONSTFUNC int32_t Dec$compare_value(const Dec_t x, const Dec_t y);
+CONSTFUNC int32_t Dec$compare(const void *x, const void *y, const TypeInfo_t *info);
+CONSTFUNC bool Dec$equal_value(const Dec_t x, const Dec_t y);
+CONSTFUNC bool Dec$equal(const void *x, const void *y, const TypeInfo_t *info);
+CONSTFUNC Dec_t Dec$round(Dec_t d, Int_t digits);
+CONSTFUNC Dec_t Dec$power(Dec_t base, Dec_t exponent);
+CONSTFUNC Dec_t Dec$plus(Dec_t x, Dec_t y);
+CONSTFUNC Dec_t Dec$negative(Dec_t x);
+CONSTFUNC Dec_t Dec$minus(Dec_t x, Dec_t y);
+CONSTFUNC Dec_t Dec$times(Dec_t x, Dec_t y);
+CONSTFUNC Dec_t Dec$divided_by(Dec_t x, Dec_t y);
+CONSTFUNC Dec_t Dec$modulo(Dec_t x, Dec_t modulus);
+CONSTFUNC Dec_t Dec$modulo1(Dec_t x, Dec_t modulus);
+PUREFUNC Dec_t Dec$from_str(const char *str);
+OptionalDec_t Dec$parse(Text_t text);
+
+CONSTFUNC Dec_t Dec$from_int64(int64_t i);
+Dec_t Dec$from_int(Int_t i);
+CONSTFUNC Dec_t Dec$from_num(double n);
+#define Dec$from_num32(n) Dec$from_num((double)n)
+#define Dec$from_int32(i) Dec$from_int64((int64_t)i)
+#define Dec$from_int16(i) Dec$from_int64((int64_t)i)
+#define Dec$from_int8(i) Dec$from_int64((int64_t)i)
+#define Dec$from_byte(i) Dec$from_int64((int64_t)i)
+#define Dec$from_bool(i) Dec$from_int64((int64_t)i)
+
+Int_t Dec$as_int(Dec_t d, bool truncate);
+int64_t Dec$as_int64(Dec_t d, bool truncate);
+int32_t Dec$as_int32(Dec_t d, bool truncate);
+int16_t Dec$as_int16(Dec_t d, bool truncate);
+int8_t Dec$as_int8(Dec_t d, bool truncate);
+Byte_t Dec$as_byte(Dec_t d, bool truncate);
+CONSTFUNC bool Dec$as_bool(Dec_t d);
+double Dec$as_num(Dec_t d);
+#define Dec$as_num32(d) ((float)Dec$as_num(d))
+
+extern const TypeInfo_t Dec$info;
+
+// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
diff --git a/src/stdlib/print.c b/src/stdlib/print.c
index 3270c765..a2ef7d4e 100644
--- a/src/stdlib/print.c
+++ b/src/stdlib/print.c
@@ -22,7 +22,7 @@ public int _print_int(FILE *f, int64_t n)
if (negative)
*(p--) = '-';
- return fwrite(p + 1, sizeof(char), (size_t)(&buf[19] - p), f);
+ return (int)fwrite(p + 1, sizeof(char), (size_t)(&buf[19] - p), f);
}
public int _print_uint(FILE *f, uint64_t n)
@@ -35,7 +35,7 @@ public int _print_uint(FILE *f, uint64_t n)
n /= 10;
} while (n > 0);
- return fwrite(p + 1, sizeof(char), (size_t)(&buf[19] - p), f);
+ return (int)fwrite(p + 1, sizeof(char), (size_t)(&buf[19] - p), f);
}
public int _print_hex(FILE *f, hex_format_t hex)
@@ -178,6 +178,95 @@ public int _print_char(FILE *f, char c)
#undef ESC
}
+public int _print_decimal64(FILE *f , _Decimal64 x)
+{
+ union {
+ _Decimal64 decimal;
+ struct {
+ uint64_t mantissa:52;
+ uint64_t exponent:11;
+ bool negative:1;
+ };
+ uint64_t bits;
+ } info = {.decimal = x};
+
+ if ((info.bits >> 58 & 0x1F) == 0x1E)
+ return fputs(info.negative ? "-INF" : "INF", f);
+ else if ((info.bits >> 58 & 0x1F) == 0x1F)
+ return fputs("NAN", f);
+
+ // determine exponent e, and mantissa m
+ // where e and m are depend on the bits in m2
+ uint64_t e;
+ uint64_t m;
+ uint64_t m2 = info.bits >> 61 & 0x3;
+ if (m2 == 0x3) {
+ e = info.bits >> 51 & 0x3FF;
+ m = 0x20000000000000 | (info.bits & 0x7FFFFFFFFFFFF);
+ } else {
+ e = info.bits >> 53 & 0x3FF;
+ m = info.bits & 0x1FFFFFFFFFFFFF;
+ }
+
+ if (m == 0) return fputs("0", f);
+
+ char buf[64] = {[63]=0};
+ char *p = &buf[62];
+ int64_t exponent = (int64_t)e - 398;
+
+ uint64_t n = m;
+ do {
+ *(p--) = '0' + (n % 10);
+ n /= 10;
+ } while (n > 0);
+
+ const char *digit_str = p + 1;
+
+ int printed = 0;
+ int64_t digits = (int64_t)(&buf[63] - digit_str);
+
+ if (info.negative)
+ printed += fputc('-', f);
+
+ while (exponent < 0 && digits > 1 && digit_str[digits-1] == '0') {
+ digits -= 1;
+ exponent += 1;
+ }
+
+ if (exponent >= 0) {
+ printed += (int)fwrite(digit_str, sizeof(char), (size_t)digits, f);
+ for (int64_t i = 0; i < exponent; i++)
+ printed += (int)fwrite("0", sizeof(char), 1, f);
+ } else {
+ int64_t digits_above_zero = MAX(digits + exponent, 0);
+ if (digits_above_zero > 0) {
+ printed += (int)fwrite(digit_str, sizeof(char), (size_t)digits_above_zero, f);
+ for (int64_t i = -digits; i > exponent; i--)
+ printed += fputc('0', f);
+ } else {
+ printed += fputc('0', f);
+ }
+
+ int64_t digits_below_zero = digits - digits_above_zero;
+ if (digits_below_zero > 0) {
+ const char *rest = digit_str + digits_above_zero;
+ if (*rest) {
+ printed += fputc('.', f);
+ for (int64_t i = digits_below_zero; i < -exponent; i++)
+ printed += fputc('0', f);
+ printed += fputs(digit_str+digits_above_zero, f);
+ }
+ }
+ }
+
+ return printed;
+}
+
+public int _print_decimal32(FILE *f, _Decimal32 x)
+{
+ return _print_decimal64(f, (_Decimal64)x);
+}
+
public int _print_quoted(FILE *f, quoted_t quoted)
{
#define ESC(e) "\\" e
diff --git a/src/stdlib/print.h b/src/stdlib/print.h
index 5ef5b6ed..5a4a56f2 100644
--- a/src/stdlib/print.h
+++ b/src/stdlib/print.h
@@ -83,6 +83,8 @@ int _print_double(FILE *f, double x);
int _print_hex(FILE *f, hex_format_t hex);
int _print_hex_double(FILE *f, hex_double_t hex);
int _print_oct(FILE *f, oct_format_t oct);
+int _print_decimal32(FILE *f, _Decimal32 d);
+int _print_decimal64(FILE *f, _Decimal64 d);
PRINT_FN _print_float(FILE *f, float x) { return _print_double(f, (double)x); }
PRINT_FN _print_pointer(FILE *f, void *p) { return _print_hex(f, hex((uint64_t)p)); }
PRINT_FN _print_bool(FILE *f, bool b) { return fputs(b ? "yes" : "no", f); }
@@ -116,6 +118,8 @@ extern int Int$print(FILE *f, Int_t i);
uint8_t: _print_uint, \
float: _print_float, \
double: _print_double, \
+ _Decimal32: _print_decimal32, \
+ _Decimal64: _print_decimal64, \
hex_format_t: _print_hex, \
hex_double_t: _print_hex_double, \
oct_format_t: _print_oct, \
diff --git a/src/stdlib/stdlib.c b/src/stdlib/stdlib.c
index fa41cda6..82470c41 100644
--- a/src/stdlib/stdlib.c
+++ b/src/stdlib/stdlib.c
@@ -57,6 +57,19 @@ static _Noreturn void signal_handler(int sig, siginfo_t *info, void *userdata)
_exit(1);
}
+static _Noreturn void fpe_handler(int sig, siginfo_t *info, void *userdata)
+{
+ (void)info, (void)userdata;
+ assert(sig == SIGFPE);
+ fflush(stdout);
+ if (USE_COLOR) fputs("\x1b[31;7m ===== MATH EXCEPTION ===== \n\n\x1b[m", stderr);
+ else fputs("===== MATH EXCEPTION =====\n\n", stderr);
+ print_stacktrace(stderr, 3);
+ fflush(stderr);
+ raise(SIGABRT);
+ _exit(1);
+}
+
public void tomo_init(void)
{
GC_INIT();
@@ -67,11 +80,17 @@ public void tomo_init(void)
setlocale(LC_ALL, "");
assert(getrandom(TOMO_HASH_KEY, sizeof(TOMO_HASH_KEY), 0) == sizeof(TOMO_HASH_KEY));
- struct sigaction sigact;
- sigact.sa_sigaction = signal_handler;
- sigemptyset(&sigact.sa_mask);
- sigact.sa_flags = 0;
- sigaction(SIGILL, &sigact, (struct sigaction *)NULL);
+ struct sigaction ill_sigaction;
+ ill_sigaction.sa_sigaction = signal_handler;
+ sigemptyset(&ill_sigaction.sa_mask);
+ ill_sigaction.sa_flags = 0;
+ sigaction(SIGILL, &ill_sigaction, (struct sigaction *)NULL);
+
+ struct sigaction fpe_sigaction;
+ fpe_sigaction.sa_sigaction = fpe_handler;
+ sigemptyset(&fpe_sigaction.sa_mask);
+ fpe_sigaction.sa_flags = 0;
+ sigaction(SIGFPE, &fpe_sigaction, (struct sigaction *)NULL);
}
static bool parse_single_arg(const TypeInfo_t *info, char *arg, void *dest)
diff --git a/src/stdlib/tomo.h b/src/stdlib/tomo.h
index 63abd2d6..62139ea4 100644
--- a/src/stdlib/tomo.h
+++ b/src/stdlib/tomo.h
@@ -11,6 +11,7 @@
#include "bytes.h"
#include "c_strings.h"
#include "datatypes.h"
+#include "decimals.h"
#include "enums.h"
#include "functiontype.h"
#include "integers.h"