diff --git a/docs/modules/math.md b/docs/modules/math.md index 4ac12475..fb9a6609 100644 --- a/docs/modules/math.md +++ b/docs/modules/math.md @@ -3,45 +3,25 @@ icon: package label: math --- -### `math.e` - -2.718281828459045 - ### `math.pi` 3.141592653589793 -### `math.log(x)` +### `math.e` -Return the natural logarithm of `x`. +2.718281828459045 -### `math.log10(x)` +### `math.inf` -Return the base-10 logarithm of `x`. +The `inf`. -### `math.log2(x)` +### `math.nan` -Return the base-2 logarithm of `x`. +The `nan`. -### `math.sin(x)` +### `math.ceil(x)` -Return the sine of `x`. - -### `math.cos(x)` - -Return the cosine of `x`. - -### `math.tan(x)` - -Return the tangent of `x`. - -### `math.isnan(x)` - -Return `True` if `x` is a NaN (not a number), and `False` otherwise. - -### `math.isinf(x)` - -Return `True` if `x` is positive or negative infinity, and `False` otherwise. +Return the ceiling of `x` as a float, the smallest integer value greater than or equal to `x`. ### `math.fabs(x)` @@ -49,16 +29,96 @@ Return the absolute value of `x`. ### `math.floor(x)` -Return the largest integer value less than or equal to `x`. +Return the floor of `x` as a float, the largest integer value less than or equal to `x`. -### `math.ceil(x)` +### `math.fsum(iterable)` -Return the smallest integer value greater than or equal to `x`. +Return an accurate floating point sum of values in the iterable. Avoids loss of precision by tracking multiple intermediate partial sums: + +``` +>>> sum([.1, .1, .1, .1, .1, .1, .1, .1, .1, .1]) +0.9999999999999999 +>>> fsum([.1, .1, .1, .1, .1, .1, .1, .1, .1, .1]) +1.0 +``` + +### `math.gcd(a, b)` + +Return the greatest common divisor of the integers `a` and `b`. + + +### `math.isfinite(x)` + +Return `True` if `x` is neither an infinity nor a NaN, and `False` otherwise. + +### `math.isinf(x)` + +Return `True` if `x` is a positive or negative infinity, and `False` otherwise. + +### `math.isnan(x)` + +Return `True` if `x` is a NaN (not a number), and `False` otherwise. + +### `math.exp(x)` + +Return `e` raised to the power of `x`. + +### `math.log(x)` + +Return the natural logarithm of `x` (to base `e`). + +### `math.log2(x)` + +Return the base-2 logarithm of `x`. This is usually more accurate than `log(x, 2)`. + +### `math.log10(x)` + +Return the base-10 logarithm of `x`. This is usually more accurate than `log(x, 10)`. + +### `math.pow(x, y)` + +Return `x` raised to the power `y`. ### `math.sqrt(x)` Return the square root of `x`. -### `math.gcd(a, b)` +### `math.acos(x)` + +Return the arc cosine of `x`, in radians. + +### `math.asin(x)` + +Return the arc sine of `x`, in radians. + +### `math.atan(x)` + +Return the arc tangent of `x`, in radians. + +### `math.atan2(y, x)` + +Return `atan(y / x)`, in radians. The result is between `-pi` and `pi`. The vector in the plane from the origin to point `(x, y)` makes this angle with the positive X axis. The point of `atan2()` is that the signs of both inputs are known to it, so it can compute the correct quadrant for the angle. For example, `atan(1)` and `atan2(1, 1)` are both `pi/4`, but `atan2(-1, -1)` is `-3*pi/4`. + +### `math.cos(x)` + +Return the cosine of `x` radians. + +### `math.sin(x)` + +Return the sine of `x` radians. + +### `math.tan(x)` + +Return the tangent of `x` radians. + +### `math.degrees(x)` + +Convert angle `x` from radians to degrees. + +### `math.radians(x)` + +Convert angle `x` from degrees to radians. + + + -Return the greatest common divisor of `a` and `b`. \ No newline at end of file diff --git a/src/obj.h b/src/obj.h index b59c0b20..e597de3b 100644 --- a/src/obj.h +++ b/src/obj.h @@ -263,6 +263,8 @@ __T _py_cast(VM* vm, PyObject* obj) { #define CAST(T, x) py_cast(vm, x) #define _CAST(T, x) _py_cast(vm, x) +#define FLOAT(x) vm->num_to_float(x) + /*****************************************************************/ template<> struct Py_ final: PyObject { diff --git a/src/pocketpy.h b/src/pocketpy.h index c050d9c6..65cf3b38 100644 --- a/src/pocketpy.h +++ b/src/pocketpy.h @@ -766,14 +766,14 @@ inline void init_builtins(VM* _vm) { } #ifdef _WIN32 -#define __EXPORT __declspec(dllexport) inline +#define PK_LEGACY_EXPORT __declspec(dllexport) inline #elif __APPLE__ -#define __EXPORT __attribute__((visibility("default"))) __attribute__((used)) inline +#define PK_LEGACY_EXPORT __attribute__((visibility("default"))) __attribute__((used)) inline #elif __EMSCRIPTEN__ #include -#define __EXPORT EMSCRIPTEN_KEEPALIVE inline +#define PK_LEGACY_EXPORT EMSCRIPTEN_KEEPALIVE inline #else -#define __EXPORT inline +#define PK_LEGACY_EXPORT inline #endif inline void add_module_time(VM* vm){ @@ -815,23 +815,31 @@ inline void add_module_json(VM* vm){ vm->bind_func<1>(mod, "dumps", CPP_LAMBDA(vm->call_method(args[0], __json__))); } + +// https://docs.python.org/3.5/library/math.html inline void add_module_math(VM* vm){ PyObject* mod = vm->new_module("math"); mod->attr().set("pi", VAR(3.1415926535897932384)); mod->attr().set("e" , VAR(2.7182818284590452354)); + mod->attr().set("inf", VAR(std::numeric_limits::infinity())); + mod->attr().set("nan", VAR(std::numeric_limits::quiet_NaN())); - vm->bind_func<1>(mod, "log", CPP_LAMBDA(VAR(std::log(vm->num_to_float(args[0]))))); - vm->bind_func<1>(mod, "log10", CPP_LAMBDA(VAR(std::log10(vm->num_to_float(args[0]))))); - vm->bind_func<1>(mod, "log2", CPP_LAMBDA(VAR(std::log2(vm->num_to_float(args[0]))))); - vm->bind_func<1>(mod, "sin", CPP_LAMBDA(VAR(std::sin(vm->num_to_float(args[0]))))); - vm->bind_func<1>(mod, "cos", CPP_LAMBDA(VAR(std::cos(vm->num_to_float(args[0]))))); - vm->bind_func<1>(mod, "tan", CPP_LAMBDA(VAR(std::tan(vm->num_to_float(args[0]))))); - vm->bind_func<1>(mod, "isnan", CPP_LAMBDA(VAR(std::isnan(vm->num_to_float(args[0]))))); - vm->bind_func<1>(mod, "isinf", CPP_LAMBDA(VAR(std::isinf(vm->num_to_float(args[0]))))); + vm->bind_func<1>(mod, "ceil", CPP_LAMBDA(VAR((i64)std::ceil(vm->num_to_float(args[0]))))); vm->bind_func<1>(mod, "fabs", CPP_LAMBDA(VAR(std::fabs(vm->num_to_float(args[0]))))); vm->bind_func<1>(mod, "floor", CPP_LAMBDA(VAR((i64)std::floor(vm->num_to_float(args[0]))))); - vm->bind_func<1>(mod, "ceil", CPP_LAMBDA(VAR((i64)std::ceil(vm->num_to_float(args[0]))))); - vm->bind_func<1>(mod, "sqrt", CPP_LAMBDA(VAR(std::sqrt(vm->num_to_float(args[0]))))); + vm->bind_func<1>(mod, "fsum", [](VM* vm, ArgsView args) { + List& list = CAST(List&, args[0]); + double sum = 0; + double c = 0; + for(PyObject* arg : list){ + double x = vm->num_to_float(arg); + double y = x - c; + double t = sum + y; + c = (t - sum) - y; + sum = t; + } + return VAR(sum); + }); vm->bind_func<2>(mod, "gcd", [](VM* vm, ArgsView args) { i64 a = CAST(i64, args[0]); i64 b = CAST(i64, args[1]); @@ -844,6 +852,30 @@ inline void add_module_math(VM* vm){ } return VAR(a); }); + + vm->bind_func<1>(mod, "isfinite", CPP_LAMBDA(VAR(std::isfinite(vm->num_to_float(args[0]))))); + vm->bind_func<1>(mod, "isinf", CPP_LAMBDA(VAR(std::isinf(vm->num_to_float(args[0]))))); + vm->bind_func<1>(mod, "isnan", CPP_LAMBDA(VAR(std::isnan(vm->num_to_float(args[0]))))); + + vm->bind_func<1>(mod, "exp", CPP_LAMBDA(VAR(std::exp(vm->num_to_float(args[0]))))); + vm->bind_func<1>(mod, "log", CPP_LAMBDA(VAR(std::log(vm->num_to_float(args[0]))))); + vm->bind_func<1>(mod, "log2", CPP_LAMBDA(VAR(std::log2(vm->num_to_float(args[0]))))); + vm->bind_func<1>(mod, "log10", CPP_LAMBDA(VAR(std::log10(vm->num_to_float(args[0]))))); + + vm->bind_func<2>(mod, "pow", CPP_LAMBDA(VAR(std::pow(vm->num_to_float(args[0]), vm->num_to_float(args[1]))))); + vm->bind_func<1>(mod, "sqrt", CPP_LAMBDA(VAR(std::sqrt(vm->num_to_float(args[0]))))); + + vm->bind_func<1>(mod, "acos", CPP_LAMBDA(VAR(std::acos(vm->num_to_float(args[0]))))); + vm->bind_func<1>(mod, "asin", CPP_LAMBDA(VAR(std::asin(vm->num_to_float(args[0]))))); + vm->bind_func<1>(mod, "atan", CPP_LAMBDA(VAR(std::atan(vm->num_to_float(args[0]))))); + vm->bind_func<2>(mod, "atan2", CPP_LAMBDA(VAR(std::atan2(vm->num_to_float(args[0]), vm->num_to_float(args[1]))))); + + vm->bind_func<1>(mod, "cos", CPP_LAMBDA(VAR(std::cos(vm->num_to_float(args[0]))))); + vm->bind_func<1>(mod, "sin", CPP_LAMBDA(VAR(std::sin(vm->num_to_float(args[0]))))); + vm->bind_func<1>(mod, "tan", CPP_LAMBDA(VAR(std::tan(vm->num_to_float(args[0]))))); + + vm->bind_func<1>(mod, "degrees", CPP_LAMBDA(VAR(vm->num_to_float(args[0]) * 180 / 3.1415926535897932384))); + vm->bind_func<1>(mod, "radians", CPP_LAMBDA(VAR(vm->num_to_float(args[0]) * 3.1415926535897932384 / 180))); } inline void add_module_dis(VM* vm){ @@ -1075,7 +1107,7 @@ inline void VM::post_init(){ static std::map _pk_deleter_map; extern "C" { - __EXPORT + PK_LEGACY_EXPORT void pkpy_delete(void* p){ auto it = _pk_deleter_map.find(p); if(it != _pk_deleter_map.end()){ @@ -1085,12 +1117,12 @@ extern "C" { } } - __EXPORT + PK_LEGACY_EXPORT void pkpy_vm_exec(pkpy::VM* vm, const char* source){ vm->exec(source, "main.py", pkpy::EXEC_MODE); } - __EXPORT + PK_LEGACY_EXPORT char* pkpy_vm_get_global(pkpy::VM* vm, const char* name){ pkpy::PyObject* val = vm->_main->attr().try_get(name); if(val == nullptr) return nullptr; @@ -1102,7 +1134,7 @@ extern "C" { } } - __EXPORT + PK_LEGACY_EXPORT char* pkpy_vm_eval(pkpy::VM* vm, const char* source){ pkpy::PyObject* ret = vm->exec(source, "", pkpy::EVAL_MODE); if(ret == nullptr) return nullptr; @@ -1114,24 +1146,24 @@ extern "C" { } } - __EXPORT + PK_LEGACY_EXPORT pkpy::REPL* pkpy_new_repl(pkpy::VM* vm){ pkpy::REPL* p = new pkpy::REPL(vm); _pk_deleter_map[p] = [](void* p){ delete (pkpy::REPL*)p; }; return p; } - __EXPORT + PK_LEGACY_EXPORT bool pkpy_repl_input(pkpy::REPL* r, const char* line){ return r->input(line); } - __EXPORT + PK_LEGACY_EXPORT void pkpy_vm_add_module(pkpy::VM* vm, const char* name, const char* source){ vm->_lazy_modules[name] = source; } - __EXPORT + PK_LEGACY_EXPORT pkpy::VM* pkpy_new_vm(bool enable_os=true){ pkpy::VM* p = new pkpy::VM(enable_os); _pk_deleter_map[p] = [](void* p){ delete (pkpy::VM*)p; };