diff options
Diffstat (limited to 'src/lib/embryo_float.c')
-rw-r--r-- | src/lib/embryo_float.c | 480 |
1 files changed, 480 insertions, 0 deletions
diff --git a/src/lib/embryo_float.c b/src/lib/embryo_float.c new file mode 100644 index 0000000..ffaa87d --- /dev/null +++ b/src/lib/embryo_float.c @@ -0,0 +1,480 @@ +/* Float arithmetic for the Small AMX engine + * + * Copyright (c) Artran, Inc. 1999 + * Written by Greg Garner (gmg@artran.com) + * Portions Copyright (c) Carsten Haitzler, 2004 <raster@rasterman.com> + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* CHANGES - + * 2002-08-27: Basic conversion of source from C++ to C by Adam D. Moss + * <adam@gimp.org> <aspirin@icculus.org> + * 2003-08-29: Removal of the dynamic memory allocation and replacing two + * type conversion functions by macros, by Thiadmer Riemersma + * 2003-09-22: Moved the type conversion macros to AMX.H, and simplifications + * of some routines, by Thiadmer Riemersma + * 2003-11-24: A few more native functions (geometry), plus minor modifications, + * mostly to be compatible with dynamically loadable extension + * modules, by Thiadmer Riemersma + * 2004-03-20: Cleaned up and reduced size for Embryo, Modified to conform to + * E coding style. Added extra parameter checks. + * Carsten Haitzler, <raster@rasterman.com> + */ + + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdlib.h> +#include <math.h> + +#include "Embryo.h" +#include "embryo_private.h" + +#define PI 3.1415926535897932384626433832795f +#ifndef MAXFLOAT +#define MAXFLOAT 3.40282347e+38f +#endif + +/* internally useful calls */ + +static float +_embryo_fp_degrees_to_radians(float angle, int radix) +{ + switch (radix) + { + case 1: /* degrees, sexagesimal system (technically: degrees/minutes/seconds) */ + return (angle * PI / 180.0f); + case 2: /* grades, centesimal system */ + return (angle * PI / 200.0f); + default: /* assume already radian */ + break; + } + return angle; +} + +/* exported float api */ + +static Embryo_Cell +_embryo_fp(Embryo_Program *ep __UNUSED__, Embryo_Cell *params) +{ + /* params[1] = long value to convert to a float */ + float f; + + if (params[0] != (1 * sizeof(Embryo_Cell))) return 0; + f = (float)params[1]; + return EMBRYO_FLOAT_TO_CELL(f); +} + +static Embryo_Cell +_embryo_fp_str(Embryo_Program *ep, Embryo_Cell *params) +{ + /* params[1] = virtual string address to convert to a float */ + char buf[64]; + Embryo_Cell *str; + float f; + int len; + + if (params[0] != (1 * sizeof(Embryo_Cell))) return 0; + str = embryo_data_address_get(ep, params[1]); + len = embryo_data_string_length_get(ep, str); + if ((len == 0) || (len >= (int)sizeof(buf))) return 0; + embryo_data_string_get(ep, str, buf); + f = (float)atof(buf); + return EMBRYO_FLOAT_TO_CELL(f); +} + +static Embryo_Cell +_embryo_fp_mul(Embryo_Program *ep __UNUSED__, Embryo_Cell *params) +{ + /* params[1] = float operand 1 */ + /* params[2] = float operand 2 */ + float f; + + if (params[0] != (2 * sizeof(Embryo_Cell))) return 0; + f = EMBRYO_CELL_TO_FLOAT(params[1]) * EMBRYO_CELL_TO_FLOAT(params[2]); + return EMBRYO_FLOAT_TO_CELL(f); +} + +static Embryo_Cell +_embryo_fp_div(Embryo_Program *ep __UNUSED__, Embryo_Cell *params) +{ + /* params[1] = float dividend (top) */ + /* params[2] = float divisor (bottom) */ + float f, ff; + + if (params[0] != (2 * sizeof(Embryo_Cell))) return 0; + f = EMBRYO_CELL_TO_FLOAT(params[1]); + ff = EMBRYO_CELL_TO_FLOAT(params[2]); + if (ff == 0.0) + { + if (f == 0.0) + return EMBRYO_FLOAT_TO_CELL(0.0f); + else if (f < 0.0) + return EMBRYO_FLOAT_TO_CELL(-MAXFLOAT); + else + return EMBRYO_FLOAT_TO_CELL(MAXFLOAT); + } + f = f / ff; + return EMBRYO_FLOAT_TO_CELL(f); +} + +static Embryo_Cell +_embryo_fp_add(Embryo_Program *ep __UNUSED__, Embryo_Cell *params) +{ + /* params[1] = float operand 1 */ + /* params[2] = float operand 2 */ + float f; + + if (params[0] != (2 * sizeof(Embryo_Cell))) return 0; + f = EMBRYO_CELL_TO_FLOAT(params[1]) + EMBRYO_CELL_TO_FLOAT(params[2]); + return EMBRYO_FLOAT_TO_CELL(f); +} + +static Embryo_Cell +_embryo_fp_sub(Embryo_Program *ep __UNUSED__, Embryo_Cell *params) +{ + /* params[1] = float operand 1 */ + /* params[2] = float operand 2 */ + float f; + + if (params[0] != (2 * sizeof(Embryo_Cell))) return 0; + f = EMBRYO_CELL_TO_FLOAT(params[1]) - EMBRYO_CELL_TO_FLOAT(params[2]); + return EMBRYO_FLOAT_TO_CELL(f); +} + +/* Return fractional part of float */ +static Embryo_Cell +_embryo_fp_fract(Embryo_Program *ep __UNUSED__, Embryo_Cell *params) +{ + /* params[1] = float operand */ + float f; + + if (params[0] != (1 * sizeof(Embryo_Cell))) return 0; + f = EMBRYO_CELL_TO_FLOAT(params[1]); + f -= (floorf(f)); + return EMBRYO_FLOAT_TO_CELL(f); +} + +/* Return integer part of float, rounded */ +static Embryo_Cell +_embryo_fp_round(Embryo_Program *ep __UNUSED__, Embryo_Cell *params) +{ + /* params[1] = float operand */ + /* params[2] = Type of rounding (cell) */ + float f; + + if (params[0] != (2 * sizeof(Embryo_Cell))) return 0; + f = EMBRYO_CELL_TO_FLOAT(params[1]); + switch (params[2]) + { + case 1: /* round downwards (truncate) */ + f = (floorf(f)); + break; + case 2: /* round upwards */ + f = (ceilf(f)); + break; + case 3: /* round towards zero */ + if (f >= 0.0) f = (floorf(f)); + else f = (ceilf(f)); + break; + default: /* standard, round to nearest */ + f = (floorf(f + 0.5)); + break; + } + return (Embryo_Cell)f; +} + +static Embryo_Cell +_embryo_fp_cmp(Embryo_Program *ep __UNUSED__, Embryo_Cell *params) +{ + /* params[1] = float operand 1 */ + /* params[2] = float operand 2 */ + float f, ff; + + if (params[0] != (2 * sizeof(Embryo_Cell))) return 0; + f = EMBRYO_CELL_TO_FLOAT(params[1]); + ff = EMBRYO_CELL_TO_FLOAT(params[2]); + if (f == ff) return 0; + else if (f > ff) return 1; + return -1; +} + +static Embryo_Cell +_embryo_fp_sqroot(Embryo_Program *ep, Embryo_Cell *params) +{ + /* params[1] = float operand */ + float f; + + if (params[0] != (1 * sizeof(Embryo_Cell))) return 0; + f = EMBRYO_CELL_TO_FLOAT(params[1]); + f = sqrtf(f); + if (f < 0) + { + embryo_program_error_set(ep, EMBRYO_ERROR_DOMAIN); + return 0; + } + return EMBRYO_FLOAT_TO_CELL(f); +} + +static Embryo_Cell +_embryo_fp_power(Embryo_Program *ep __UNUSED__, Embryo_Cell *params) +{ + /* params[1] = float operand 1 */ + /* params[2] = float operand 2 */ + float f, ff; + + if (params[0] != (2 * sizeof(Embryo_Cell))) return 0; + f = EMBRYO_CELL_TO_FLOAT(params[1]); + ff = EMBRYO_CELL_TO_FLOAT(params[2]); + f = powf(f, ff); + return EMBRYO_FLOAT_TO_CELL(f); +} + +static Embryo_Cell +_embryo_fp_log(Embryo_Program *ep, Embryo_Cell *params) +{ + /* params[1] = float operand 1 (value) */ + /* params[2] = float operand 2 (base) */ + float f, ff, tf; + + if (params[0] != (2 * sizeof(Embryo_Cell))) return 0; + f = EMBRYO_CELL_TO_FLOAT(params[1]); + ff = EMBRYO_CELL_TO_FLOAT(params[2]); + if ((f <= 0.0) || (ff <= 0.0)) + { + embryo_program_error_set(ep, EMBRYO_ERROR_DOMAIN); + return 0; + } + if (ff == 10.0) f = log10f(f); + else if (ff == 2.0) f = log2f(f); + else + { + tf = logf(ff); + if (tf == 0.0) f = 0.0; + else f = (logf(f) / tf); + } + return EMBRYO_FLOAT_TO_CELL(f); +} + +static Embryo_Cell +_embryo_fp_sin(Embryo_Program *ep __UNUSED__, Embryo_Cell *params) +{ + /* params[1] = float operand 1 (angle) */ + /* params[2] = float operand 2 (radix) */ + float f; + + if (params[0] != (2 * sizeof(Embryo_Cell))) return 0; + f = EMBRYO_CELL_TO_FLOAT(params[1]); + f = _embryo_fp_degrees_to_radians(f, params[2]); + f = sinf(f); + return EMBRYO_FLOAT_TO_CELL(f); +} + +static Embryo_Cell +_embryo_fp_cos(Embryo_Program *ep __UNUSED__, Embryo_Cell *params) +{ + /* params[1] = float operand 1 (angle) */ + /* params[2] = float operand 2 (radix) */ + float f; + + if (params[0] != (2 * sizeof(Embryo_Cell))) return 0; + f = EMBRYO_CELL_TO_FLOAT(params[1]); + f = _embryo_fp_degrees_to_radians(f, params[2]); + f = cosf(f); + return EMBRYO_FLOAT_TO_CELL(f); +} + +static Embryo_Cell +_embryo_fp_tan(Embryo_Program *ep __UNUSED__, Embryo_Cell *params) +{ + /* params[1] = float operand 1 (angle) */ + /* params[2] = float operand 2 (radix) */ + float f; + + if (params[0] != (2 * sizeof(Embryo_Cell))) return 0; + f = EMBRYO_CELL_TO_FLOAT(params[1]); + f = _embryo_fp_degrees_to_radians(f, params[2]); + f = tanf(f); + return EMBRYO_FLOAT_TO_CELL(f); +} + +static Embryo_Cell +_embryo_fp_abs(Embryo_Program *ep __UNUSED__, Embryo_Cell *params) +{ + /* params[1] = float operand */ + float f; + + if (params[0] != (1 * sizeof(Embryo_Cell))) return 0; + f = EMBRYO_CELL_TO_FLOAT(params[1]); + f = (f >= 0) ? f : -f; + return EMBRYO_FLOAT_TO_CELL(f); +} + +static Embryo_Cell +_embryo_fp_asin(Embryo_Program *ep __UNUSED__, Embryo_Cell *params) +{ + /* params[1] = float operand 1 (angle) */ + /* params[2] = float operand 2 (radix) */ + float f; + + if (params[0] != (2 * sizeof(Embryo_Cell))) return 0; + f = EMBRYO_CELL_TO_FLOAT(params[1]); + f = sinf(f); + f = _embryo_fp_degrees_to_radians(f, params[2]); + return EMBRYO_FLOAT_TO_CELL(f); +} + +static Embryo_Cell +_embryo_fp_acos(Embryo_Program *ep __UNUSED__, Embryo_Cell *params) +{ + /* params[1] = float operand 1 (angle) */ + /* params[2] = float operand 2 (radix) */ + float f; + + if (params[0] != (2 * sizeof(Embryo_Cell))) return 0; + f = EMBRYO_CELL_TO_FLOAT(params[1]); + f = cosf(f); + f = _embryo_fp_degrees_to_radians(f, params[2]); + return EMBRYO_FLOAT_TO_CELL(f); +} + +static Embryo_Cell +_embryo_fp_atan(Embryo_Program *ep __UNUSED__, Embryo_Cell *params) +{ + /* params[1] = float operand 1 (angle) */ + /* params[2] = float operand 2 (radix) */ + float f; + + if (params[0] != (2 * sizeof(Embryo_Cell))) return 0; + f = EMBRYO_CELL_TO_FLOAT(params[1]); + f = tanf(f); + f = _embryo_fp_degrees_to_radians(f, params[2]); + return EMBRYO_FLOAT_TO_CELL(f); +} + +static Embryo_Cell +_embryo_fp_atan2(Embryo_Program *ep __UNUSED__, Embryo_Cell *params) +{ + /* params[1] = float operand 1 (y) */ + /* params[2] = float operand 2 (x) */ + /* params[3] = float operand 3 (radix) */ + float f, ff; + + if (params[0] != (3 * sizeof(Embryo_Cell))) return 0; + f = EMBRYO_CELL_TO_FLOAT(params[1]); + ff = EMBRYO_CELL_TO_FLOAT(params[2]); + f = atan2f(f, ff); + f = _embryo_fp_degrees_to_radians(f, params[3]); + return EMBRYO_FLOAT_TO_CELL(f); +} + +static Embryo_Cell +_embryo_fp_log1p(Embryo_Program *ep __UNUSED__, Embryo_Cell *params) +{ + /* params[1] = float operand */ + float f; + + if (params[0] != (1 * sizeof(Embryo_Cell))) return 0; + f = EMBRYO_CELL_TO_FLOAT(params[1]); + f = log1pf(f); + return EMBRYO_FLOAT_TO_CELL(f); +} + +static Embryo_Cell +_embryo_fp_cbrt(Embryo_Program *ep __UNUSED__, Embryo_Cell *params) +{ + /* params[1] = float operand */ + float f; + + if (params[0] != (1 * sizeof(Embryo_Cell))) return 0; + f = EMBRYO_CELL_TO_FLOAT(params[1]); + f = cbrtf(f); + return EMBRYO_FLOAT_TO_CELL(f); +} + +static Embryo_Cell +_embryo_fp_exp(Embryo_Program *ep __UNUSED__, Embryo_Cell *params) +{ + /* params[1] = float operand */ + float f; + + if (params[0] != (1 * sizeof(Embryo_Cell))) return 0; + f = EMBRYO_CELL_TO_FLOAT(params[1]); + f = expf(f); + return EMBRYO_FLOAT_TO_CELL(f); +} + +static Embryo_Cell +_embryo_fp_exp2(Embryo_Program *ep __UNUSED__, Embryo_Cell *params) +{ + /* params[1] = float operand */ + float f; + + if (params[0] != (1 * sizeof(Embryo_Cell))) return 0; + f = EMBRYO_CELL_TO_FLOAT(params[1]); + f = exp2f(f); + return EMBRYO_FLOAT_TO_CELL(f); +} + +static Embryo_Cell +_embryo_fp_hypot(Embryo_Program *ep __UNUSED__, Embryo_Cell *params) +{ + /* params[1] = float operand */ + float f, ff; + + if (params[0] != (2 * sizeof(Embryo_Cell))) return 0; + f = EMBRYO_CELL_TO_FLOAT(params[1]); + ff = EMBRYO_CELL_TO_FLOAT(params[2]); + f = hypotf(f, ff); + return EMBRYO_FLOAT_TO_CELL(f); +} + +/* functions used by the rest of embryo */ + +void +_embryo_fp_init(Embryo_Program *ep) +{ + embryo_program_native_call_add(ep, "float", _embryo_fp); + embryo_program_native_call_add(ep, "atof", _embryo_fp_str); + embryo_program_native_call_add(ep, "float_mul", _embryo_fp_mul); + embryo_program_native_call_add(ep, "float_div", _embryo_fp_div); + embryo_program_native_call_add(ep, "float_add", _embryo_fp_add); + embryo_program_native_call_add(ep, "float_sub", _embryo_fp_sub); + embryo_program_native_call_add(ep, "fract", _embryo_fp_fract); + embryo_program_native_call_add(ep, "round", _embryo_fp_round); + embryo_program_native_call_add(ep, "float_cmp", _embryo_fp_cmp); + embryo_program_native_call_add(ep, "sqrt", _embryo_fp_sqroot); + embryo_program_native_call_add(ep, "pow", _embryo_fp_power); + embryo_program_native_call_add(ep, "log", _embryo_fp_log); + embryo_program_native_call_add(ep, "sin", _embryo_fp_sin); + embryo_program_native_call_add(ep, "cos", _embryo_fp_cos); + embryo_program_native_call_add(ep, "tan", _embryo_fp_tan); + embryo_program_native_call_add(ep, "abs", _embryo_fp_abs); + /* Added in embryo 1.2 */ + embryo_program_native_call_add(ep, "asin", _embryo_fp_asin); + embryo_program_native_call_add(ep, "acos", _embryo_fp_acos); + embryo_program_native_call_add(ep, "atan", _embryo_fp_atan); + embryo_program_native_call_add(ep, "atan2", _embryo_fp_atan2); + embryo_program_native_call_add(ep, "log1p", _embryo_fp_log1p); + embryo_program_native_call_add(ep, "cbrt", _embryo_fp_cbrt); + embryo_program_native_call_add(ep, "exp", _embryo_fp_exp); + embryo_program_native_call_add(ep, "exp2", _embryo_fp_exp2); + embryo_program_native_call_add(ep, "hypot", _embryo_fp_hypot); +} |