diff options
Diffstat (limited to 'src/stdlib')
| -rw-r--r-- | src/stdlib/datatypes.h | 3 | ||||
| -rw-r--r-- | src/stdlib/decimals.c | 293 | ||||
| -rw-r--r-- | src/stdlib/decimals.h | 58 | ||||
| -rw-r--r-- | src/stdlib/print.c | 93 | ||||
| -rw-r--r-- | src/stdlib/print.h | 4 | ||||
| -rw-r--r-- | src/stdlib/stdlib.c | 29 | ||||
| -rw-r--r-- | src/stdlib/tomo.h | 1 |
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" |
