mirror of
https://github.com/reactos/reactos.git
synced 2024-11-02 21:09:15 +00:00
295eaf4e32
- Added support for average of squares and mean of squares into statistical functions. - pop() and push() functions in RPN engines now work with nodes instead of stack units. - Moved the POW and SQR operations near the operators. CORE-12766 - Fix number of digits for IEEE-754 constants. - Show all available digits in exp notation. CORE-14871 - Update help correspondingly.
608 lines
11 KiB
C
608 lines
11 KiB
C
/*
|
|
* ReactOS Calc (Math functions, IEEE-754 engine)
|
|
*
|
|
* Copyright 2007-2017, Carlo Bramini
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "calc.h"
|
|
|
|
static double validate_rad2angle(double a);
|
|
static double validate_angle2rad(calc_number_t *c);
|
|
|
|
void apply_int_mask(calc_number_t *r)
|
|
{
|
|
unsigned __int64 mask;
|
|
|
|
switch (calc.size) {
|
|
case IDC_RADIO_QWORD:
|
|
mask = _UI64_MAX;
|
|
break;
|
|
case IDC_RADIO_DWORD:
|
|
mask = ULONG_MAX;
|
|
break;
|
|
case IDC_RADIO_WORD:
|
|
mask = USHRT_MAX;
|
|
break;
|
|
case IDC_RADIO_BYTE:
|
|
mask = UCHAR_MAX;
|
|
break;
|
|
default:
|
|
mask = (unsigned __int64)-1;
|
|
}
|
|
r->i &= mask;
|
|
}
|
|
|
|
double asinh(double x)
|
|
{
|
|
return log(x+sqrt(x*x+1));
|
|
}
|
|
|
|
double acosh(double x)
|
|
{
|
|
// must be x>=1, if not return Nan (Not a Number)
|
|
if(!(x>=1.0)) return sqrt(-1.0);
|
|
|
|
// return only the positive result (as sqrt does).
|
|
return log(x+sqrt(x*x-1.0));
|
|
}
|
|
|
|
double atanh(double x)
|
|
{
|
|
// must be x>-1, x<1, if not return Nan (Not a Number)
|
|
if(!(x>-1.0 && x<1.0)) return sqrt(-1.0);
|
|
|
|
return log((1.0+x)/(1.0-x))/2.0;
|
|
}
|
|
|
|
static double validate_rad2angle(double a)
|
|
{
|
|
switch (calc.degr) {
|
|
case IDC_RADIO_DEG:
|
|
a = a * (180.0/CALC_PI);
|
|
break;
|
|
case IDC_RADIO_RAD:
|
|
break;
|
|
case IDC_RADIO_GRAD:
|
|
a = a * (200.0/CALC_PI);
|
|
break;
|
|
}
|
|
return a;
|
|
}
|
|
|
|
static double validate_angle2rad(calc_number_t *c)
|
|
{
|
|
switch (calc.degr) {
|
|
case IDC_RADIO_DEG:
|
|
c->f = c->f * (CALC_PI/180.0);
|
|
break;
|
|
case IDC_RADIO_RAD:
|
|
break;
|
|
case IDC_RADIO_GRAD:
|
|
c->f = c->f * (CALC_PI/200.0);
|
|
break;
|
|
}
|
|
return c->f;
|
|
}
|
|
|
|
void rpn_sin(calc_number_t *c)
|
|
{
|
|
double angle = validate_angle2rad(c);
|
|
|
|
if (angle == 0 || angle == CALC_PI)
|
|
c->f = 0;
|
|
else
|
|
if (angle == CALC_3_PI_2)
|
|
c->f = -1;
|
|
else
|
|
if (angle == CALC_2_PI)
|
|
c->f = 1;
|
|
else
|
|
c->f = sin(angle);
|
|
}
|
|
void rpn_cos(calc_number_t *c)
|
|
{
|
|
double angle = validate_angle2rad(c);
|
|
|
|
if (angle == CALC_PI_2 || angle == CALC_3_PI_2)
|
|
c->f = 0;
|
|
else
|
|
if (angle == CALC_PI)
|
|
c->f = -1;
|
|
else
|
|
if (angle == CALC_2_PI)
|
|
c->f = 1;
|
|
else
|
|
c->f = cos(angle);
|
|
}
|
|
void rpn_tan(calc_number_t *c)
|
|
{
|
|
double angle = validate_angle2rad(c);
|
|
|
|
if (angle == CALC_PI_2 || angle == CALC_3_PI_2)
|
|
calc.is_nan = TRUE;
|
|
else
|
|
if (angle == CALC_PI || angle == CALC_2_PI)
|
|
c->f = 0;
|
|
else
|
|
c->f = tan(angle);
|
|
}
|
|
|
|
void rpn_asin(calc_number_t *c)
|
|
{
|
|
c->f = validate_rad2angle(asin(c->f));
|
|
if (_isnan(c->f))
|
|
calc.is_nan = TRUE;
|
|
}
|
|
void rpn_acos(calc_number_t *c)
|
|
{
|
|
c->f = validate_rad2angle(acos(c->f));
|
|
if (_isnan(c->f))
|
|
calc.is_nan = TRUE;
|
|
}
|
|
void rpn_atan(calc_number_t *c)
|
|
{
|
|
c->f = validate_rad2angle(atan(c->f));
|
|
if (_isnan(c->f))
|
|
calc.is_nan = TRUE;
|
|
}
|
|
|
|
void rpn_sinh(calc_number_t *c)
|
|
{
|
|
c->f = sinh(c->f);
|
|
if (_isnan(c->f))
|
|
calc.is_nan = TRUE;
|
|
}
|
|
void rpn_cosh(calc_number_t *c)
|
|
{
|
|
c->f = cosh(c->f);
|
|
if (_isnan(c->f))
|
|
calc.is_nan = TRUE;
|
|
}
|
|
void rpn_tanh(calc_number_t *c)
|
|
{
|
|
c->f = tanh(c->f);
|
|
if (_isnan(c->f))
|
|
calc.is_nan = TRUE;
|
|
}
|
|
|
|
void rpn_asinh(calc_number_t *c)
|
|
{
|
|
c->f = asinh(c->f);
|
|
if (_isnan(c->f))
|
|
calc.is_nan = TRUE;
|
|
}
|
|
void rpn_acosh(calc_number_t *c)
|
|
{
|
|
c->f = acosh(c->f);
|
|
if (_isnan(c->f))
|
|
calc.is_nan = TRUE;
|
|
}
|
|
void rpn_atanh(calc_number_t *c)
|
|
{
|
|
c->f = atanh(c->f);
|
|
if (_isnan(c->f))
|
|
calc.is_nan = TRUE;
|
|
}
|
|
|
|
void rpn_int(calc_number_t *c)
|
|
{
|
|
double int_part;
|
|
|
|
modf(calc.code.f, &int_part);
|
|
c->f = int_part;
|
|
}
|
|
|
|
void rpn_frac(calc_number_t *c)
|
|
{
|
|
double int_part;
|
|
|
|
c->f = modf(calc.code.f, &int_part);
|
|
}
|
|
|
|
void rpn_reci(calc_number_t *c)
|
|
{
|
|
if (c->f == 0)
|
|
calc.is_nan = TRUE;
|
|
else
|
|
c->f = 1./c->f;
|
|
}
|
|
|
|
void rpn_fact(calc_number_t *c)
|
|
{
|
|
double fact, mult, num;
|
|
|
|
if (calc.base == IDC_RADIO_DEC)
|
|
num = c->f;
|
|
else
|
|
num = (double)c->i;
|
|
if (num > 1000) {
|
|
calc.is_nan = TRUE;
|
|
return;
|
|
}
|
|
if (num < 0) {
|
|
calc.is_nan = TRUE;
|
|
return;
|
|
} else
|
|
if (num == 0)
|
|
fact = 1;
|
|
else {
|
|
rpn_int(c);
|
|
fact = 1;
|
|
mult = 2;
|
|
while (mult <= num) {
|
|
fact *= mult;
|
|
mult++;
|
|
}
|
|
c->f = fact;
|
|
}
|
|
if (_finite(fact) == 0)
|
|
calc.is_nan = TRUE;
|
|
else
|
|
if (calc.base == IDC_RADIO_DEC)
|
|
c->f = fact;
|
|
else
|
|
c->i = (__int64)fact;
|
|
}
|
|
|
|
__int64 logic_dbl2int(calc_number_t *a)
|
|
{
|
|
double int_part;
|
|
int width;
|
|
|
|
modf(a->f, &int_part);
|
|
width = (int_part==0) ? 1 : (int)log10(fabs(int_part))+1;
|
|
if (width > 63) {
|
|
calc.is_nan = TRUE;
|
|
return 0;
|
|
}
|
|
return (__int64)int_part;
|
|
}
|
|
|
|
double logic_int2dbl(calc_number_t *a)
|
|
{
|
|
return (double)a->i;
|
|
}
|
|
|
|
void rpn_not(calc_number_t *c)
|
|
{
|
|
if (calc.base == IDC_RADIO_DEC) {
|
|
calc_number_t n;
|
|
n.i = logic_dbl2int(c);
|
|
c->f = (long double)(~n.i);
|
|
} else
|
|
c->i = ~c->i;
|
|
}
|
|
|
|
void rpn_pi(calc_number_t *c)
|
|
{
|
|
c->f = CALC_PI;
|
|
}
|
|
|
|
void rpn_2pi(calc_number_t *c)
|
|
{
|
|
c->f = CALC_PI*2;
|
|
}
|
|
|
|
void rpn_sign(calc_number_t *c)
|
|
{
|
|
if (calc.base == IDC_RADIO_DEC)
|
|
c->f = 0-c->f;
|
|
else
|
|
c->i = 0-c->i;
|
|
}
|
|
|
|
void rpn_exp2(calc_number_t *c)
|
|
{
|
|
if (calc.base == IDC_RADIO_DEC) {
|
|
c->f *= c->f;
|
|
if (_finite(c->f) == 0)
|
|
calc.is_nan = TRUE;
|
|
} else
|
|
c->i *= c->i;
|
|
}
|
|
|
|
void rpn_exp3(calc_number_t *c)
|
|
{
|
|
if (calc.base == IDC_RADIO_DEC) {
|
|
c->f = pow(c->f, 3.);
|
|
if (_finite(c->f) == 0)
|
|
calc.is_nan = TRUE;
|
|
} else
|
|
c->i *= (c->i*c->i);
|
|
}
|
|
|
|
static __int64 myabs64(__int64 number)
|
|
{
|
|
return (number < 0) ? 0-number : number;
|
|
}
|
|
|
|
static unsigned __int64 sqrti(unsigned __int64 number)
|
|
{
|
|
/* modified form of Newton's method for approximating roots */
|
|
#define NEXT(n, i) (((n) + (i)/(n)) >> 1)
|
|
unsigned __int64 n, n1;
|
|
|
|
#ifdef __GNUC__
|
|
if (number == 0xffffffffffffffffULL)
|
|
#else
|
|
if (number == 0xffffffffffffffffUI64)
|
|
#endif
|
|
return 0xffffffff;
|
|
|
|
n = 1;
|
|
n1 = NEXT(n, number);
|
|
while (myabs64(n1 - n) > 1) {
|
|
n = n1;
|
|
n1 = NEXT(n, number);
|
|
}
|
|
while((n1*n1) > number)
|
|
n1--;
|
|
return n1;
|
|
#undef NEXT
|
|
}
|
|
|
|
void rpn_sqrt(calc_number_t *c)
|
|
{
|
|
if (calc.base == IDC_RADIO_DEC) {
|
|
if (c->f < 0)
|
|
calc.is_nan = TRUE;
|
|
else
|
|
c->f = sqrt(c->f);
|
|
} else {
|
|
c->i = sqrti(c->i);
|
|
}
|
|
}
|
|
|
|
static __int64 cbrti(__int64 x) {
|
|
__int64 s, y, b;
|
|
|
|
s = 60;
|
|
y = 0;
|
|
while(s >= 0) {
|
|
y = 2*y;
|
|
b = (3*y*(y + 1) + 1) << s;
|
|
s = s - 3;
|
|
if (x >= b) {
|
|
x = x - b;
|
|
y = y + 1;
|
|
}
|
|
}
|
|
return y;
|
|
}
|
|
|
|
void rpn_cbrt(calc_number_t *c)
|
|
{
|
|
if (calc.base == IDC_RADIO_DEC)
|
|
#if defined(__GNUC__) && !defined(__REACTOS__)
|
|
c->f = cbrt(c->f);
|
|
#else
|
|
c->f = pow(c->f,1./3.);
|
|
#endif
|
|
else {
|
|
c->i = cbrti(c->i);
|
|
}
|
|
}
|
|
|
|
void rpn_exp(calc_number_t *c)
|
|
{
|
|
c->f = exp(c->f);
|
|
if (_finite(c->f) == 0)
|
|
calc.is_nan = TRUE;
|
|
}
|
|
|
|
void rpn_exp10(calc_number_t *c)
|
|
{
|
|
double int_part;
|
|
|
|
modf(c->f, &int_part);
|
|
if (fmod(int_part, 2.) == 0.)
|
|
calc.is_nan = TRUE;
|
|
else {
|
|
c->f = pow(10., c->f);
|
|
if (_finite(c->f) == 0)
|
|
calc.is_nan = TRUE;
|
|
}
|
|
}
|
|
|
|
void rpn_ln(calc_number_t *c)
|
|
{
|
|
if (c->f <= 0)
|
|
calc.is_nan = TRUE;
|
|
else
|
|
c->f = log(c->f);
|
|
}
|
|
|
|
void rpn_log(calc_number_t *c)
|
|
{
|
|
if (c->f <= 0)
|
|
calc.is_nan = TRUE;
|
|
else
|
|
c->f = log10(c->f);
|
|
}
|
|
|
|
static double stat_sum(void)
|
|
{
|
|
double sum = 0;
|
|
statistic_t *p = calc.stat;
|
|
|
|
while (p != NULL) {
|
|
if (p->base == IDC_RADIO_DEC)
|
|
sum += p->num.f;
|
|
else
|
|
sum += p->num.i;
|
|
p = (statistic_t *)(p->next);
|
|
}
|
|
return sum;
|
|
}
|
|
|
|
static double stat_sum2(void)
|
|
{
|
|
double sum = 0;
|
|
statistic_t *p = calc.stat;
|
|
|
|
while (p != NULL) {
|
|
if (p->base == IDC_RADIO_DEC)
|
|
sum += p->num.f * p->num.f;
|
|
else
|
|
sum += (double)p->num.i * (double)p->num.i;
|
|
p = (statistic_t *)(p->next);
|
|
}
|
|
return sum;
|
|
}
|
|
|
|
void rpn_ave(calc_number_t *c)
|
|
{
|
|
double ave = 0;
|
|
int n;
|
|
|
|
ave = stat_sum();
|
|
n = SendDlgItemMessage(calc.hStatWnd, IDC_LIST_STAT, LB_GETCOUNT, 0, 0);
|
|
|
|
if (n)
|
|
ave = ave / (double)n;
|
|
if (calc.base == IDC_RADIO_DEC)
|
|
c->f = ave;
|
|
else
|
|
c->i = (__int64)ave;
|
|
}
|
|
|
|
void rpn_ave2(calc_number_t *c)
|
|
{
|
|
double ave = 0;
|
|
int n;
|
|
|
|
ave = stat_sum2();
|
|
n = SendDlgItemMessage(calc.hStatWnd, IDC_LIST_STAT, LB_GETCOUNT, 0, 0);
|
|
|
|
if (n)
|
|
ave = ave / (double)n;
|
|
if (calc.base == IDC_RADIO_DEC)
|
|
c->f = ave;
|
|
else
|
|
c->i = (__int64)ave;
|
|
}
|
|
|
|
void rpn_sum(calc_number_t *c)
|
|
{
|
|
double sum = stat_sum();
|
|
|
|
if (calc.base == IDC_RADIO_DEC)
|
|
c->f = sum;
|
|
else
|
|
c->i = (__int64)sum;
|
|
}
|
|
|
|
void rpn_sum2(calc_number_t *c)
|
|
{
|
|
double sum = stat_sum2();
|
|
|
|
if (calc.base == IDC_RADIO_DEC)
|
|
c->f = sum;
|
|
else
|
|
c->i = (__int64)sum;
|
|
}
|
|
|
|
static void rpn_s_ex(calc_number_t *c, int pop_type)
|
|
{
|
|
double ave = 0;
|
|
double n = 0;
|
|
double dev = 0;
|
|
double num = 0;
|
|
statistic_t *p = calc.stat;
|
|
|
|
ave = stat_sum();
|
|
n = (double)SendDlgItemMessage(calc.hStatWnd, IDC_LIST_STAT, LB_GETCOUNT, 0, 0);
|
|
|
|
if (n == 0) {
|
|
c->f = 0;
|
|
return;
|
|
}
|
|
ave = ave / n;
|
|
|
|
dev = 0;
|
|
p = calc.stat;
|
|
while (p != NULL) {
|
|
if (p->base == IDC_RADIO_DEC)
|
|
num = p->num.f;
|
|
else
|
|
num = (double)p->num.i;
|
|
dev += pow(num-ave, 2.);
|
|
p = (statistic_t *)(p->next);
|
|
}
|
|
dev = sqrt(dev/(pop_type ? n-1 : n));
|
|
if (calc.base == IDC_RADIO_DEC)
|
|
c->f = dev;
|
|
else
|
|
c->i = (__int64)dev;
|
|
}
|
|
|
|
void rpn_s(calc_number_t *c)
|
|
{
|
|
rpn_s_ex(c, 0);
|
|
}
|
|
|
|
void rpn_s_m1(calc_number_t *c)
|
|
{
|
|
rpn_s_ex(c, 1);
|
|
}
|
|
|
|
void rpn_dms2dec(calc_number_t *c)
|
|
{
|
|
double d, m, s;
|
|
|
|
m = modf(c->f, &d) * 100;
|
|
s = (modf(m, &m) * 100)+.5;
|
|
modf(s, &s);
|
|
|
|
m = m/60;
|
|
s = s/3600;
|
|
|
|
c->f = d + m + s;
|
|
}
|
|
|
|
void rpn_dec2dms(calc_number_t *c)
|
|
{
|
|
double d, m, s;
|
|
|
|
m = modf(c->f, &d) * 60;
|
|
s = ceil(modf(m, &m) * 60);
|
|
c->f = d + m/100. + s/10000.;
|
|
}
|
|
|
|
void rpn_zero(calc_number_t *c)
|
|
{
|
|
c->f = 0;
|
|
}
|
|
|
|
void rpn_copy(calc_number_t *dst, calc_number_t *src)
|
|
{
|
|
*dst = *src;
|
|
}
|
|
|
|
int rpn_is_zero(calc_number_t *c)
|
|
{
|
|
return (c->f == 0);
|
|
}
|
|
|
|
void rpn_alloc(calc_number_t *c)
|
|
{
|
|
}
|
|
|
|
void rpn_free(calc_number_t *c)
|
|
{
|
|
}
|