diff --git a/lib/std/math/math.c3 b/lib/std/math/math.c3 index ee170921a..84ba831a3 100644 --- a/lib/std/math/math.c3 +++ b/lib/std/math/math.c3 @@ -6,7 +6,6 @@ import std::math::complex; import std::math::matrix; import std::math::quaternion; -// TODO Define these using quad precision. const E = 2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178525166427427466; const LOG2E = 1.44269504088896340735992468100189214; // log2(e) const LOG10E = 0.434294481903251827651128918916605082; // log10(e) @@ -60,6 +59,7 @@ const DOUBLE_EPSILON = 2.22044604925031308085e-16; const QUAD_MANT_DIG = 113; /* +Currently unsupported float128 constants const QUAD_MAX = 1.18973149535723176508575932662800702e+4932; const QUAD_MIN = 3.36210314311209350626267781732175260e-4932; const QUAD_DENORM_MIN = 6.47517511943802511092443895822764655e-4966; @@ -129,7 +129,7 @@ macro is_approx(x, y, eps) { if (x == y) return true; if (is_nan(x) || is_nan(y)) return false; - return abs(x-y) <= eps; + return abs(x - y) <= eps; } <* @@ -140,7 +140,7 @@ macro is_approx_rel(x, y, eps) { if (x == y) return true; if (is_nan(x) || is_nan(y)) return false; - return abs(x-y) <= eps * max(abs(x), abs(y)); + return abs(x - y) <= eps * max(abs(x), abs(y)); } <* @@ -149,7 +149,7 @@ macro is_approx_rel(x, y, eps) macro sign(x) { var $Type = $typeof(x); - $if $Type.kindof == TypeKind.UNSIGNED_INT: + $if $Type.kindof == UNSIGNED_INT: return ($Type)(x > 0); $else return ($Type)(x > 0) - ($Type)(x < 0); @@ -177,7 +177,7 @@ macro atan2(x, y) *> macro sincos_ref(x, sinp, cosp) { - $if @typeid(*sinp) == float.typeid: + $if @typeis(sinp, float*.typeid): return _sincosf(x, sinp, cosp); $else return _sincos(x, sinp, cosp); @@ -192,7 +192,7 @@ macro sincos_ref(x, sinp, cosp) *> macro sincos(x) { - $if @typeid(x) == float.typeid: + $if @typeis(x, float): float[<2>] v @noinit; _sincosf(x, &v[0], &v[1]); $else diff --git a/lib/std/math/math_complex.c3 b/lib/std/math/math_complex.c3 index 849ae6b64..2f9ee0aaf 100644 --- a/lib/std/math/math_complex.c3 +++ b/lib/std/math/math_complex.c3 @@ -19,12 +19,12 @@ macro Complex Complex.add_real(self, Real r) @operator_s(+) => { .v = self.v + ( macro Complex Complex.add_each(self, Real b) => { .v = self.v + b }; macro Complex Complex.sub(self, Complex b) @operator(-) => { .v = self.v - b.v }; macro Complex Complex.sub_real(self, Real r) @operator(-) => { .v = self.v - (Real[<2>]) { r, 0 } }; -macro Complex Complex.sub_from_real(self, Real r) @operator_r(-) => { .v = (Real[<2>]) { r, 0 } - self.v }; +macro Complex Complex.sub_real_inverse(self, Real r) @operator_r(-) => { .v = (Real[<2>]) { r, 0 } - self.v }; macro Complex Complex.sub_each(self, Real b) => { .v = self.v - b }; macro Complex Complex.scale(self, Real r) @operator_s(*) => { .v = self.v * r }; macro Complex Complex.mul(self, Complex b)@operator(*) => { self.r * b.r - self.c * b.c, self.r * b.c + b.r * self.c }; macro Complex Complex.div_real(self, Real r) @operator(/) => { .v = self.v / r }; -macro Complex Complex.real_div(Complex c, Real r) @operator_r(/) => ((Complex) { .r = self }).div(c); +macro Complex Complex.div_real_inverse(Complex c, Real r) @operator_r(/) => ((Complex) { .r = self }).div(c); macro Complex Complex.div(self, Complex b) @operator(/) { Real div = b.v.dot(b.v); diff --git a/lib/std/math/math_quaternion.c3 b/lib/std/math/math_quaternion.c3 index d07f260d3..86d7d4c11 100644 --- a/lib/std/math/math_quaternion.c3 +++ b/lib/std/math/math_quaternion.c3 @@ -16,7 +16,7 @@ macro Quaternion Quaternion.add_each(self, Real b) => { .v = self.v + b }; macro Quaternion Quaternion.sub(self, Quaternion b) @operator(-) => { .v = self.v - b.v }; macro Quaternion Quaternion.negate(self) @operator(-) => { .v = -self.v }; macro Quaternion Quaternion.sub_each(self, Real b) => { .v = self.v - b }; -macro Quaternion Quaternion.scale(self, Real s) @operator(*) => { .v = self.v * s }; +macro Quaternion Quaternion.scale(self, Real s) @operator_s(*) => { .v = self.v * s }; macro Quaternion Quaternion.normalize(self) => { .v = self.v.normalize() }; macro Real Quaternion.length(self) => self.v.length(); macro Quaternion Quaternion.lerp(self, Quaternion q2, Real amount) => { .v = self.v.lerp(q2.v, amount) }; @@ -61,7 +61,7 @@ fn Quaternion Quaternion.slerp(self, Quaternion q2, Real amount) return { .v = q1v * ratio_a + q2v * ratio_b }; } -fn Quaternion Quaternion.mul(self, Quaternion b) @operator(+) +fn Quaternion Quaternion.mul(self, Quaternion b) @operator(*) { return { self.i * b.l + self.l * b.i + self.j * b.k - self.k * b.j, self.j * b.l + self.l * b.j + self.k * b.i - self.i * b.k, diff --git a/releasenotes.md b/releasenotes.md index db7dbef3f..3378b874f 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -9,11 +9,13 @@ - Improved error message when narrowing isn't allowed. - Operator overloading for `+ - * / % & | ^ << >> ~ == !=` - Add `@operator_r` and `@operator_s` attributes. +- More stdlib tests: `sincos`. ### Fixes - Trying to cast an enum to int and back caused the compiler to crash. - Incorrect rounding at compile time going from double to int. - Regression with invalid setup of the WASM temp allocator. +- Correctly detect multiple overloads of the same type. ### Stdlib changes - Hash functions for integer vectors and arrays. diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index ebe772cbb..5c56ce95a 100755 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -21,7 +21,7 @@ static bool sema_analyse_operator_common(SemaContext *context, Decl *method, Typ uint32_t parameters); static inline Decl *operator_in_module_typed(SemaContext *c, Module *module, OperatorOverload operator_overload, OverloadType overload_type, Type *method_type, Expr *binary_arg, Type *binary_type, Decl **candidate_ref, Decl **ambiguous_ref); -static inline Decl *operator_in_module_exact_typed(SemaContext *c, Module *module, OperatorOverload operator_overload, OverloadType overload_type, Type *method_type, Type *param_type); +static inline Decl *operator_in_module_exact_typed(Module *module, OperatorOverload operator_overload, OverloadType overload_type, Type *method_type, Type *param_type, Decl *skipped); static inline bool sema_analyse_operator_element_at(SemaContext *context, Decl *method); static inline bool sema_analyse_operator_element_set(SemaContext *context, Decl *method); static inline bool sema_analyse_operator_len(SemaContext *context, Decl *method); @@ -49,7 +49,7 @@ static bool sema_analyse_attributes_for_var(SemaContext *context, Decl *decl, bo static bool sema_check_section(SemaContext *context, Attr *attr); static inline bool sema_analyse_attribute_decl(SemaContext *context, SemaContext *c, Decl *decl, bool *erase_decl); static Decl *sema_find_typed_operator_in_list(SemaContext *context, Decl **methods, OperatorOverload operator_overload, OverloadType overload_type, Type *parent_type, Expr *binary_arg, Type *binary_type, Decl **candidate_ref, Decl **ambiguous_ref); -static Decl *sema_find_exact_typed_operator_in_list(SemaContext *context, Decl **methods, OperatorOverload operator_overload, OverloadType overload_type, Type *parent_type, Type *binary_type); +static Decl *sema_find_exact_typed_operator_in_list(Decl **methods, OperatorOverload operator_overload, OverloadType overload_type, Type *parent_type, Type *binary_type, Decl *skipped); static inline bool sema_analyse_typedef(SemaContext *context, Decl *decl, bool *erase_decl); static bool sema_analyse_variable_type(SemaContext *context, Type *type, SourceSpan span); @@ -1718,37 +1718,37 @@ static inline Decl *operator_in_module_typed(SemaContext *c, Module *module, Ope return NULL; } -static inline Decl *operator_in_module_exact_typed(SemaContext *c, Module *module, OperatorOverload operator_overload, OverloadType overload_type, Type *method_type, Type *param_type) +static inline Decl *operator_in_module_exact_typed(Module *module, OperatorOverload operator_overload, OverloadType overload_type, Type *method_type, Type *param_type, Decl *skipped) { if (module->is_generic) return NULL; - Decl *found = sema_find_exact_typed_operator_in_list(c, module->private_method_extensions, operator_overload, overload_type, method_type, param_type); + Decl *found = sema_find_exact_typed_operator_in_list(module->private_method_extensions, operator_overload, overload_type, method_type, param_type, skipped); if (found) return found; FOREACH(Module *, sub_module, module->sub_modules) { - return operator_in_module_exact_typed(c, sub_module, operator_overload, overload_type, method_type, param_type); + return operator_in_module_exact_typed(sub_module, operator_overload, overload_type, method_type, param_type, skipped); } return NULL; } -static inline Decl *operator_in_module_untyped(SemaContext *c, Module *module, Type *type, OperatorOverload operator_overload) +static inline Decl *operator_in_module_untyped(Module *module, Type *type, OperatorOverload operator_overload, Decl *skipped) { if (module->is_generic) return NULL; FOREACH(Decl *, extension, module->private_method_extensions) { + if (extension == skipped) continue; if (decl_matches_overload(extension, type, operator_overload)) { - unit_register_external_symbol(c, extension); return extension; } } FOREACH(Module *, sub_module, module->sub_modules) { - return operator_in_module_untyped(c, sub_module, type, operator_overload); + return operator_in_module_untyped(sub_module, type, operator_overload, skipped); } return NULL; } -Decl *sema_find_untyped_operator(SemaContext *context, Type *type, OperatorOverload operator_overload) +Decl *sema_find_untyped_operator(SemaContext *context, Type *type, OperatorOverload operator_overload, Decl *skipped) { type = type->canonical; assert(operator_overload < OVERLOAD_TYPED_START); @@ -1756,31 +1756,29 @@ Decl *sema_find_untyped_operator(SemaContext *context, Type *type, OperatorOverl Decl *def = type->decl; FOREACH(Decl *, func, def->methods) { - if (func->func_decl.operator == operator_overload) - { - unit_register_external_symbol(context, func); - return func; - } + if (skipped == func) continue; + if (func->func_decl.operator == operator_overload) return func; } FOREACH(Decl *, extension, context->unit->local_method_extensions) { + if (skipped == extension) continue; if (decl_matches_overload(extension, type, operator_overload)) return extension; } - Decl *extension = operator_in_module_untyped(context, context->compilation_unit->module, type, operator_overload); + Decl *extension = operator_in_module_untyped(context->compilation_unit->module, type, operator_overload, skipped); if (extension) return extension; - FOREACH(Decl *, import, context->unit->imports) + FOREACH(Decl *, import, context->unit->public_imports) { - if (!import->import.import_private_as_public) continue; - extension = operator_in_module_untyped(context, import->import.module, type, operator_overload); + extension = operator_in_module_untyped(import->import.module, type, operator_overload, skipped); if (extension) return extension; } return NULL; } -static Decl *sema_find_exact_typed_operator_in_list(SemaContext *context, Decl **methods, OperatorOverload operator_overload, OverloadType overload_type, Type *parent_type, Type *binary_type) +static Decl *sema_find_exact_typed_operator_in_list(Decl **methods, OperatorOverload operator_overload, OverloadType overload_type, Type *parent_type, Type *binary_type, Decl *skipped) { FOREACH(Decl *, func, methods) { + if (func == skipped) continue; if (func->func_decl.operator != operator_overload) continue; if (parent_type && parent_type != typeget(func->func_decl.type_parent)) continue; if ((overload_type & func->func_decl.overload_type) == 0) continue; @@ -1819,26 +1817,28 @@ static Decl *sema_find_typed_operator_in_list(SemaContext *context, Decl **metho return NULL; } -static Decl *sema_find_exact_typed_operator(SemaContext *context, Type *type, OperatorOverload operator_overload, OverloadType overload_type, Type *param_type) +static Decl *sema_find_exact_typed_operator(SemaContext *context, Type *type, OperatorOverload operator_overload, OverloadType overload_type, Type *param_type, Decl *skipped) { assert(operator_overload >= OVERLOAD_TYPED_START); type = type->canonical; - Decl *func = sema_find_exact_typed_operator_in_list(context, type->decl->methods, operator_overload, overload_type, type, param_type); + Decl *func = sema_find_exact_typed_operator_in_list(type->decl->methods, operator_overload, overload_type, type, + param_type, skipped); if (func) return func; - Decl *extension = sema_find_exact_typed_operator_in_list(context, context->unit->local_method_extensions, operator_overload, overload_type, type, param_type); + Decl *extension = sema_find_exact_typed_operator_in_list(context->unit->local_method_extensions, + operator_overload, overload_type, type, param_type, skipped); if (extension) return extension; - extension = operator_in_module_exact_typed(context, context->compilation_unit->module, operator_overload, overload_type, - type, param_type); + extension = operator_in_module_exact_typed(context->compilation_unit->module, operator_overload, overload_type, + type, param_type, skipped); if (extension) return extension; FOREACH(Decl *, import, context->unit->imports) { if (!import->import.import_private_as_public) continue; - extension = operator_in_module_exact_typed(context, import->import.module, operator_overload, overload_type, - type, param_type); + extension = operator_in_module_exact_typed(import->import.module, operator_overload, overload_type, + type, param_type, skipped); if (extension) return extension; } return NULL; @@ -2155,13 +2155,13 @@ INLINE bool sema_analyse_operator_method(SemaContext *context, Type *parent_type Decl *other = NULL; if (operator >= OVERLOAD_TYPED_START) { - other = sema_find_exact_typed_operator(context, parent_type, operator, method->func_decl.overload_type, second_param); + other = sema_find_exact_typed_operator(context, parent_type, operator, method->func_decl.overload_type, second_param, method); } else { - other = sema_find_untyped_operator(context, parent_type, operator); + other = sema_find_untyped_operator(context, parent_type, operator, method); } - if (other && other != method) + if (other) { SourceSpan span = method_find_overload_span(method); sema_error_at(context, span, "This operator is already defined for '%s'.", parent_type->name); @@ -2179,14 +2179,14 @@ INLINE bool sema_analyse_operator_method(SemaContext *context, Type *parent_type return true; case OVERLOAD_ELEMENT_AT: // [] compares &[] - other = sema_find_untyped_operator(context, parent_type, OVERLOAD_ELEMENT_REF); + other = sema_find_untyped_operator(context, parent_type, OVERLOAD_ELEMENT_REF, method); if (other && decl_ok(other)) { sema_get_overload_arguments(other, &value, &index_type); break; } // And []= - other = sema_find_untyped_operator(context, parent_type, OVERLOAD_ELEMENT_SET); + other = sema_find_untyped_operator(context, parent_type, OVERLOAD_ELEMENT_SET, method); if (other && decl_ok(other)) { sema_get_overload_arguments(other, &value, &index_type); @@ -2195,14 +2195,14 @@ INLINE bool sema_analyse_operator_method(SemaContext *context, Type *parent_type return true; case OVERLOAD_ELEMENT_REF: // &[] compares [] - other = sema_find_untyped_operator(context, parent_type, OVERLOAD_ELEMENT_AT); + other = sema_find_untyped_operator(context, parent_type, OVERLOAD_ELEMENT_AT, method); if (other && decl_ok(other)) { sema_get_overload_arguments(other, &value, &index_type); break; } // And []= - other = sema_find_untyped_operator(context, parent_type, OVERLOAD_ELEMENT_SET); + other = sema_find_untyped_operator(context, parent_type, OVERLOAD_ELEMENT_SET, method); if (other && decl_ok(other)) { sema_get_overload_arguments(other, &value, &index_type); @@ -2211,14 +2211,14 @@ INLINE bool sema_analyse_operator_method(SemaContext *context, Type *parent_type return true; case OVERLOAD_ELEMENT_SET: // []= compares &[] - other = sema_find_untyped_operator(context, parent_type, OVERLOAD_ELEMENT_REF); + other = sema_find_untyped_operator(context, parent_type, OVERLOAD_ELEMENT_REF, method); if (other && decl_ok(other)) { sema_get_overload_arguments(other, &value, &index_type); break; } // And [] - other = sema_find_untyped_operator(context, parent_type, OVERLOAD_ELEMENT_AT); + other = sema_find_untyped_operator(context, parent_type, OVERLOAD_ELEMENT_AT, method); if (other && decl_ok(other)) { sema_get_overload_arguments(other, &value, &index_type); diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 884884521..8a589ff75 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -3153,7 +3153,7 @@ static Expr *sema_expr_find_subscript_type_or_overload_for_subscript(SemaContext Decl **overload_ptr) { Decl *overload = NULL; - overload = sema_find_untyped_operator(context, current_expr->type, overload_type); + overload = sema_find_untyped_operator(context, current_expr->type, overload_type, NULL); if (overload) { // Overload for []= @@ -3340,7 +3340,7 @@ static inline bool sema_expr_analyse_subscript_lvalue(SemaContext *context, Expr { if (start_from_end) { - Decl *len = sema_find_untyped_operator(context, current_expr->type, OVERLOAD_LEN); + Decl *len = sema_find_untyped_operator(context, current_expr->type, OVERLOAD_LEN, NULL); if (!len) { if (check_valid) goto VALID_FAIL_POISON; @@ -3459,7 +3459,7 @@ static inline bool sema_expr_analyse_subscript(SemaContext *context, Expr *expr, { if (start_from_end) { - Decl *len = sema_find_untyped_operator(context, current_expr->type, OVERLOAD_LEN); + Decl *len = sema_find_untyped_operator(context, current_expr->type, OVERLOAD_LEN, NULL); if (!len) { if (check_valid) goto VALID_FAIL_POISON; @@ -7646,7 +7646,7 @@ static inline bool sema_expr_analyse_neg_plus(SemaContext *context, Expr *expr) // Check for overload if (type_is_user_defined(canonical)) { - Decl *overload = sema_find_untyped_operator(context, canonical, OVERLOAD_UNARY_MINUS); + Decl *overload = sema_find_untyped_operator(context, canonical, OVERLOAD_UNARY_MINUS, NULL); if (overload) { // Plus just returns inner @@ -7722,7 +7722,7 @@ static inline bool sema_expr_analyse_bit_not(SemaContext *context, Expr *expr) if (type_is_user_defined(canonical) && canonical->type_kind != TYPE_BITSTRUCT) { - Decl *overload = sema_find_untyped_operator(context, canonical, OVERLOAD_NEGATE); + Decl *overload = sema_find_untyped_operator(context, canonical, OVERLOAD_NEGATE, NULL); if (overload) return sema_insert_method_call(context, expr, overload, inner, NULL, false); } @@ -7898,7 +7898,7 @@ static bool sema_analyse_assign_mutate_overloaded_subscript(SemaContext *context Expr *increased = exprptr(subscript_expr->subscript_assign_expr.expr); Type *type_check = increased->type->canonical; Expr *index = exprptr(subscript_expr->subscript_assign_expr.index); - Decl *operator = sema_find_untyped_operator(context, type_check, OVERLOAD_ELEMENT_REF); + Decl *operator = sema_find_untyped_operator(context, type_check, OVERLOAD_ELEMENT_REF, NULL); Expr **args = NULL; if (operator) { @@ -7908,7 +7908,7 @@ static bool sema_analyse_assign_mutate_overloaded_subscript(SemaContext *context main->type = subscript_expr->type; return true; } - operator = sema_find_untyped_operator(context, type_check, OVERLOAD_ELEMENT_AT); + operator = sema_find_untyped_operator(context, type_check, OVERLOAD_ELEMENT_AT, NULL); if (!operator) { RETURN_SEMA_ERROR(main, "There is no overload for [] for %s.", type_quoted_error_string(increased->type)); diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index 6276bdbf3..e4a21d5ad 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -95,7 +95,7 @@ bool sema_analyse_expr_value(SemaContext *context, Expr *expr); Expr *expr_access_inline_member(Expr *parent, Decl *parent_decl); bool sema_analyse_ct_expr(SemaContext *context, Expr *expr); Decl *sema_find_typed_operator(SemaContext *context, OperatorOverload operator_overload, Expr *rhs, Expr *lhs, Decl **ambiguous_ref, bool *reverse); -Decl *sema_find_untyped_operator(SemaContext *context, Type *type, OperatorOverload operator_overload); +Decl *sema_find_untyped_operator(SemaContext *context, Type *type, OperatorOverload operator_overload, Decl *skipped); bool sema_insert_method_call(SemaContext *context, Expr *method_call, Decl *method_decl, Expr *parent, Expr **arguments, bool reverse_overload); bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *expr); diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 2a3830f99..010e2895c 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -1477,9 +1477,9 @@ static inline bool sema_analyse_foreach_stmt(SemaContext *context, Ast *statemen if (!value_type || canonical->type_kind == TYPE_DISTINCT) { - len = sema_find_untyped_operator(context, enumerator->type, OVERLOAD_LEN); - Decl *by_val = sema_find_untyped_operator(context, enumerator->type, OVERLOAD_ELEMENT_AT); - Decl *by_ref = sema_find_untyped_operator(context, enumerator->type, OVERLOAD_ELEMENT_REF); + len = sema_find_untyped_operator(context, enumerator->type, OVERLOAD_LEN, NULL); + Decl *by_val = sema_find_untyped_operator(context, enumerator->type, OVERLOAD_ELEMENT_AT, NULL); + Decl *by_ref = sema_find_untyped_operator(context, enumerator->type, OVERLOAD_ELEMENT_REF, NULL); if (!len || (!by_val && !by_ref)) { if (value_type) goto SKIP_OVERLOAD; diff --git a/test/unit/stdlib/math/math.c3 b/test/unit/stdlib/math/math.c3 index 3e1a7f1fd..885b7aef1 100644 --- a/test/unit/stdlib/math/math.c3 +++ b/test/unit/stdlib/math/math.c3 @@ -1,8 +1,58 @@ module math_tests; import std::math; +import std::core::test; + + +fn void test_sincos() @test +{ + $assert(@typeid(math::sincos(1.0)) == double[<2>].typeid); + test::eq_approx(math::sincos(math::PI / 6).x, 0.5, 4); + test::eq_approx(math::sincos(math::PI / 6).y, 0.866025, 5); + test::eq_approx(math::sincos(math::PI / 4).x, 0.707107, 5); + test::eq_approx(math::sincos(math::PI / 4).y, 0.707107, 5); + test::eq_approx(math::sincos(math::PI / 3).x, 0.866025, 5); + test::eq_approx(math::sincos(math::PI / 3).y, 0.5, 5); + test::eq_approx(math::sincos(math::PI / 2).x, 1, 5); + test::eq_approx(math::sincos(math::PI / 2).y, 0, 5); + test::eq_approx(math::sincos(math::PI).x, 0, 5); + test::eq_approx(math::sincos(math::PI).y, -1, 5); + test::eq_approx(math::sincos(3 * math::PI / 2).x, -1, 5); + test::eq_approx(math::sincos(3 * math::PI / 2).y, 0, 5); + test::eq_approx(math::sincos(math::PI * 2).x, 0, 5); + test::eq_approx(math::sincos(math::PI * 2).y, 1, 5); +} +fn void test_sincosf() @test +{ + $assert(@typeid(math::sincos(1.0f)) == float[<2>].typeid); + test::eq_approx(math::sincos((float)(math::PI / 6)).x, 0.5, 4); + test::eq_approx(math::sincos((float)(math::PI / 6)).y, 0.866025, 5); + test::eq_approx(math::sincos((float)(math::PI / 4)).x, 0.707107, 5); + test::eq_approx(math::sincos((float)(math::PI / 4)).y, 0.707107, 5); + test::eq_approx(math::sincos((float)(math::PI / 3)).x, 0.866025, 5); + test::eq_approx(math::sincos((float)(math::PI / 3)).y, 0.5, 5); + test::eq_approx(math::sincos((float)(math::PI / 2)).x, 1, 5); + test::eq_approx(math::sincos((float)(math::PI / 2)).y, 0, 5); + test::eq_approx(math::sincos((float)(math::PI)).x, 0, 5); + test::eq_approx(math::sincos((float)(math::PI)).y, -1, 5); + test::eq_approx(math::sincos((float)(3 * math::PI / 2)).x, -1, 5); + test::eq_approx(math::sincos((float)(3 * math::PI / 2)).y, 0, 5); + test::eq_approx(math::sincos((float)(math::PI * 2)).x, 0, 5); + test::eq_approx(math::sincos((float)(math::PI * 2)).y, 1, 5); +} fn void test_abs() @test { + test::eq_approx(math::abs(-2.0), 2.0, 6); + test::eq_approx(math::abs(-2.0f), 2.0, 6); + test::eq_approx(math::abs(2.0), 2.0, 6); + test::eq_approx(math::abs(2.0f), 2.0, 6); + test::eq(math::abs(-2), 2); + test::eq(math::abs(2), 2); + test::eq(math::abs((int[<2>]){ -1, 2 }), (int[<2>]) { 1, 2 }); + test::eq(math::abs((int128)-1), (int128)1); + test::eq_approx(math::abs((float[<2>]) { 1, -3 }).x, 1.0, 6); + test::eq_approx(math::abs((float[<2>]) { 1, -3 }).y, 3.0, 6); + int x = -21; assert(math::abs(x) == 21); double y = -123.0; @@ -19,7 +69,7 @@ fn void test_abs() @test fn void test_acos() @test { int [<5>] in = { 231, -231, 1, 0, -1 }; - double [<3>] out = { 0., math::PI_2, math::PI }; + double [<3>] out = { 0.0, math::PI_2, math::PI }; double [<6>] in2 = { 0.9, 0.6, 0.1, -0.1, -0.6, -0.9 }; double [<6>] out2 = { 0.45102681179626236, 0.9272952180016123, 1.4706289056333368, 1.6709637479564565, 2.214297435588181, 2.6905658417935308 }; assert(@typeis(math::acos(in[0]), double)); @@ -57,7 +107,7 @@ fn void test_acos() @test fn void test_acosh() @test { int [<5>] in = { 0, -1, 1, 2, 231 }; - double [<3>] out = { 0., 1.3169578969248166, 6.135560205979194 }; + double [<3>] out = { 0.0, 1.3169578969248166, 6.135560205979194 }; assert(@typeis(math::acosh(in[0]), double)); assert(@typeis(math::acosh((float)in[0]), float)); assert(@typeis(math::acosh((double)in[0]), double)); @@ -82,7 +132,7 @@ fn void test_acosh() @test fn void test_asin() @test { int [<5>] in = { 231, -231, 1, 0, -1 }; - double [<3>] out = { math::PI_2, 0., -math::PI_2 }; + double [<3>] out = { math::PI_2, 0.0, -math::PI_2 }; double [<6>] in2 = { 0.98, 0.6, 0.1, -0.1, -0.6, -0.98 }; double [<6>] out2 = { 1.3704614844717768, 0.6435011087932844, 0.1001674211615598, -0.1001674211615598, -0.6435011087932844, -1.3704614844717768 }; assert(@typeis(math::asin(in[0]), double)); @@ -116,7 +166,7 @@ fn void test_asin() @test fn void test_asinh() @test { int [<5>] in = { 231, 1, 0, -1, -231 }; - double [<5>] out = { 6.135569576118435, 0.881373587019543, 0., -0.881373587019543, -6.135569576118435 }; + double [<5>] out = { 6.135569576118435, 0.881373587019543, 0.0, -0.881373587019543, -6.135569576118435 }; assert(@typeis(math::asinh(in[0]), double)); assert(@typeis(math::asinh((float)in[0]), float)); assert(@typeis(math::asinh((double)in[0]), double)); @@ -134,7 +184,7 @@ fn void test_asinh() @test fn void test_atan() @test { int [<9>] in = { 231, 3, 2, 1, 0, -1, -2, -3, -231 }; - double [<9>] out = { 1.5664673495078372, 1.2490457723982544, 1.1071487177940904, math::PI_4, 0., -math::PI_4, -1.1071487177940904, -1.2490457723982544, -1.5664673495078372 }; + double [<9>] out = { 1.5664673495078372, 1.2490457723982544, 1.1071487177940904, math::PI_4, 0.0, -math::PI_4, -1.1071487177940904, -1.2490457723982544, -1.5664673495078372 }; double [<6>] in2 = { 0.6, 0.4, 0.1, -0.1, -0.4, -0.6 }; double [<6>] out2 = { 0.5404195002705842, 0.3805063771123649, 0.09966865249116204, -0.09966865249116204, -0.3805063771123649, -0.5404195002705842 }; assert(@typeis(math::atan(in[0]), double)); @@ -178,9 +228,9 @@ fn void test_atanh() @test assert(math::is_inf(math::atanh((float)in[i])), "atanh(%f) is not inf", in[i]); assert(math::is_inf(math::atanh((double)in[i])), "atanh(%f) is not inf", in[i]); } - assert(math::atanh(0) == 0., "atanh(%d) is not equal to %f", 0, 0.); - assert(math::atanh(0.f) == 0.f, "atanh(%f) is not equal to %f", 0.f, 0.f); - assert(math::atanh(0.) == 0., "atanh(%f) is not equal to %f", 0., 0.); + assert(math::atanh(0) == 0.0, "atanh(%d) is not equal to %f", 0, 0.0); + assert(math::atanh(0.0f) == 0.0f, "atanh(%f) is not equal to %f", 0.0f, 0.0f); + assert(math::atanh(0.0) == 0.0, "atanh(%f) is not equal to %f", 0.0, 0.0); for (int i = 0; i < 6; i++) { float f = math::atanh((float)in2[i]); @@ -192,24 +242,24 @@ fn void test_atanh() @test fn void test_floating_point_word() @test { - float f = 1.f; + float f = 1.0f; assert(f.word() == 0x3f800000); // xor swap - float f1 = 2.f; - float f2 = 3.f; + float f1 = 2.0f; + float f2 = 3.0f; uint u1 = f1.word(); uint u2 = f2.word(); u1 ^= u2; u2 ^= u1; u1 ^= u2; f1.set_word(u1); f2.set_word(u2); - assert((f1 == 3.f) && (f2 == 2.f)); + assert((f1 == 3.0f) && (f2 == 2.0f)); // sign bit trick - f = -1.f; + f = -1.0f; assert((f.word() >> 31) == 1); - f = 1.f; + f = 1.0f; assert((f.word() >> 31) == 0); // absolute value bit trick - float[<4>] fvals = { 1.f, -1.f, 91.5f, -91.5f }; + float[<4>] fvals = { 1.0f, -1.0f, 91.5f, -91.5f }; for (int i = 0; i < 4; i++) { f = fvals[i]; @@ -217,7 +267,7 @@ fn void test_floating_point_word() @test assert(f == math::abs(fvals[i])); } - double d = 1.; + double d = 1.0; assert((d.high_word() - 0x3ff00000 | d.low_word()) == 0); // xor swap double d1 = 2.0; @@ -232,14 +282,14 @@ fn void test_floating_point_word() @test d1.set_high_word(u1_high); d2.set_low_word(u2_low); d2.set_high_word(u2_high); - assert((d1 == 3.) && (d2 == 2.)); + assert((d1 == 3.0) && (d2 == 2.0)); // sign bit trick - d = -1.; + d = -1.0; assert((d.high_word() >> 31) == 1); - d = 1.; + d = 1.0; assert((d.high_word() >> 31) == 0); // absolute value bit trick - double[<4>] vals = { 1., -1., 91.5, -91.5 }; + double[<4>] vals = { 1.0, -1.0, 91.5, -91.5 }; for (int i = 0; i < 4; i++) { d = vals[i]; @@ -275,11 +325,11 @@ fn void test_ceil() @test fn void test_cos() @test { int [<5>] in = { 231, 1, 0, -1, -231 }; - double [<5>] out = { 0.09280621889587707, 0.54030230586813972 , 1., 0.54030230586813972, 0.09280621889587707 }; - float [<2>] in2 = { math::PI, 0.f }; - float [<2>] out2 = { -1.f, 1.f }; - double [<2>] in3 = { math::PI, 0. }; - double [<2>] out3 = { -1., 1. }; + double [<5>] out = { 0.09280621889587707, 0.54030230586813972 , 1.0, 0.54030230586813972, 0.09280621889587707 }; + float [<2>] in2 = { math::PI, 0.0f }; + float [<2>] out2 = { -1.0f, 1.0f }; + double [<2>] in3 = { math::PI, 0.0 }; + double [<2>] out3 = { -1.0, 1.0 }; assert(@typeis(math::cos(in[0]), double)); assert(@typeis(math::cos((float)in[0]), float)); assert(@typeis(math::cos((double)in[0]), double)); @@ -304,7 +354,7 @@ fn void test_cos() @test fn void test_exp() @test { int[<5>] in = { 2, 1, 0, -1, -2 }; - double[<5>] out = { 7.38905609893065, math::E , 1., 0.36787944117144233, 0.1353352832366127 }; + double[<5>] out = { 7.38905609893065, math::E , 1.0, 0.36787944117144233, 0.1353352832366127 }; float[<6>] in2 = { 1.8f, 0.6f, 0.4f, -0.4f, -0.8f, -1.8f }; float[<6>] out2 = {6.049647464412946f, 1.8221188003905089f, 1.4918246976412703f, 0.6703200460356393f, 0.44932896411722156f, 0.16529888822158656f }; double[<6>] in3 = { 1.8, 0.6, 0.4, -0.4, -0.8, -1.8 }; @@ -357,13 +407,13 @@ fn void test_floor() @test fn void test_log() @test { int[<8>] in = { 1, 10, 100, 1000, 1, 4, 8, 16 }; - double[<8>] out = { 0., 1., 2., 3., 0., 2. / 3., 1., 4. / 3. }; - float[<4>] bf = { 1.f / math::E, 1.f / (math::E * math::E), 1.f / (math::E * math::E), 1.f / math::E }; - float[<4>] in2 = { math::E * math::E, math::E, 1.f / math::E, 1.f / (math::E * math::E) }; - float[<4>] out2 = { -2.f, -0.5f, 0.5f, 2.f }; - double[<4>] bx = { 1. / math::E, 1. / (math::E * math::E), 1. / (math::E * math::E), 1. / math::E }; - double[<4>] in3 = { math::E * math::E, math::E, 1. / math::E, 1. / (math::E * math::E) }; - double[<4>] out3 = { -2., -0.5, 0.5, 2. }; + double[<8>] out = { 0.0, 1.0, 2.0, 3.0, 0.0, 2.0 / 3.0, 1.0, 4.0 / 3.0 }; + float[<4>] bf = { 1.0f / math::E, 1.0f / (math::E * math::E), 1.0f / (math::E * math::E), 1.0f / math::E }; + float[<4>] in2 = { math::E * math::E, math::E, 1.0f / math::E, 1.0f / (math::E * math::E) }; + float[<4>] out2 = { -2.0f, -0.5f, 0.5f, 2.0f }; + double[<4>] bx = { 1.0 / math::E, 1.0 / (math::E * math::E), 1.0 / (math::E * math::E), 1.0 / math::E }; + double[<4>] in3 = { math::E * math::E, math::E, 1.0 / math::E, 1.0 / (math::E * math::E) }; + double[<4>] out3 = { -2.0, -0.5, 0.5, 2.0 }; assert(@typeis(math::log(in[0], in[0]), double)); assert(@typeis(math::log(in[0], (float)in[0]), float)); assert(@typeis(math::log((float)in[0], in[0]), float)); @@ -398,11 +448,11 @@ fn void test_log() @test fn void test_pow() @test { int[<10>] e = { 2, 1, 0, -1, -2, 2, 1, 0, -1, -2 }; - double[<10>] out = { 100., 10., 1., 0.1, 0.01, 4., 2., 1., 0.5, 0.25 }; - float[<2>] base2 = { -1.f / math::E, 1.f / math::E }; - float[<5>] out2 = { 1.f / (math::E * math::E), 1.f / math::E, 1.f, math::E, math::E * math::E }; - double[<2>] base3 = { -1. / math::E, 1. / math::E }; - double[<5>] out3 = { 1. / (math::E * math::E), 1. / math::E, 1., math::E, math::E * math::E }; + double[<10>] out = { 100.0, 10.0, 1.0, 0.1, 0.01, 4.0, 2.0, 1.0, 0.5, 0.25 }; + float[<2>] base2 = { -1.0f / math::E, 1.0f / math::E }; + float[<5>] out2 = { 1.0f / (math::E * math::E), 1.0f / math::E, 1.0f, math::E, math::E * math::E }; + double[<2>] base3 = { -1.0 / math::E, 1.0 / math::E }; + double[<5>] out3 = { 1.0 / (math::E * math::E), 1.0 / math::E, 1.0, math::E, math::E * math::E }; assert(@typeis(math::pow(e[1], e[1]), double)); assert(@typeis(math::pow((float)e[1], e[1]), float)); assert(@typeis(math::pow((double)e[1], e[1]), double)); @@ -460,11 +510,11 @@ fn void test_sign() @test fn void test_sin() @test { int [<5>] in = { 231, 1, 0, -1, -231 }; - double [<5>] out = { -0.99568418975810324, 0.84147098480789651 , 0., -0.84147098480789651, 0.99568418975810324 }; + double [<5>] out = { -0.99568418975810324, 0.84147098480789651 , 0.0, -0.84147098480789651, 0.99568418975810324 }; float [<2>] in2 = { math::PI_2, -math::PI_2 }; - float [<2>] out2 = { 1.f, -1.f }; + float [<2>] out2 = { 1.0f, -1.0f }; double [<2>] in3 = { math::PI_2, -math::PI_2 }; - double [<2>] out3 = { 1., -1. }; + double [<2>] out3 = { 1.0, -1.0 }; assert(@typeis(math::sin(in[0]), double)); assert(@typeis(math::sin((float)in[0]), float)); assert(@typeis(math::sin((double)in[0]), double)); @@ -489,11 +539,11 @@ fn void test_sin() @test fn void test_tan() @test { int [<5>] in = { 231, 1, 0, -1, -231 }; - double [<5>] out = { -10.7286365246191129, 1.5574077246549022 , 0., -1.5574077246549022, 10.7286365246191129 }; + double [<5>] out = { -10.7286365246191129, 1.5574077246549022 , 0.0, -1.5574077246549022, 10.7286365246191129 }; float [<2>] in2 = { math::PI_4, -math::PI_4 }; - float [<2>] out2 = { 1.f, -1.f }; + float [<2>] out2 = { 1.0f, -1.0f }; double [<2>] in3 = { math::PI_4, -math::PI_4 }; - double [<2>] out3 = { 1., -1. }; + double [<2>] out3 = { 1.0, -1.0 }; assert(@typeis(math::tan(in[0]), double)); assert(@typeis(math::tan((float)in[0]), float)); assert(@typeis(math::tan((double)in[0]), double));