ucrtbase: Support overriding return value in _matherr.

Signed-off-by: Piotr Caban <piotr@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Piotr Caban 2020-08-04 15:16:49 +02:00 committed by Alexandre Julliard
parent 47a5de3438
commit 2833a10ba1
2 changed files with 76 additions and 61 deletions

View File

@ -71,16 +71,14 @@ int CDECL MSVCRT__matherr(struct MSVCRT__exception *e)
} }
static void math_error(int type, const char *name, double arg1, double arg2, double retval) static double math_error(int type, const char *name, double arg1, double arg2, double retval)
{ {
struct MSVCRT__exception exception = {type, (char *)name, arg1, arg2, retval};
TRACE("(%d, %s, %g, %g, %g)\n", type, debugstr_a(name), arg1, arg2, retval); TRACE("(%d, %s, %g, %g, %g)\n", type, debugstr_a(name), arg1, arg2, retval);
if (MSVCRT_default_matherr_func) if (MSVCRT_default_matherr_func && MSVCRT_default_matherr_func(&exception))
{ return exception.retval;
struct MSVCRT__exception exception = {type, (char *)name, arg1, arg2, retval};
if (MSVCRT_default_matherr_func(&exception)) return;
}
switch (type) switch (type)
{ {
@ -97,6 +95,8 @@ static void math_error(int type, const char *name, double arg1, double arg2, dou
default: default:
ERR("Unhandled math error!\n"); ERR("Unhandled math error!\n");
} }
return exception.retval;
} }
/********************************************************************* /*********************************************************************
@ -177,8 +177,8 @@ float CDECL MSVCRT__nextafterf( float num, float next )
float CDECL MSVCRT__logbf( float num ) float CDECL MSVCRT__logbf( float num )
{ {
float ret = logbf(num); float ret = logbf(num);
if (isnan(num)) math_error(_DOMAIN, "_logbf", num, 0, ret); if (isnan(num)) return math_error(_DOMAIN, "_logbf", num, 0, ret);
else if (!num) math_error(_SING, "_logbf", num, 0, ret); if (!num) return math_error(_SING, "_logbf", num, 0, ret);
return ret; return ret;
} }
@ -216,7 +216,8 @@ float CDECL MSVCRT_acosf( float x )
* cancellation. The sqrt() makes things worse. A safer way to calculate * cancellation. The sqrt() makes things worse. A safer way to calculate
* acos() is to use atan2(sqrt((1 - x) * (1 + x)), x). */ * acos() is to use atan2(sqrt((1 - x) * (1 + x)), x). */
float ret = atan2f(sqrtf((1 - x) * (1 + x)), x); float ret = atan2f(sqrtf((1 - x) * (1 + x)), x);
if (x < -1.0 || x > 1.0 || !finitef(x)) math_error(_DOMAIN, "acosf", x, 0, ret); if (x < -1.0 || x > 1.0 || !finitef(x))
return math_error(_DOMAIN, "acosf", x, 0, ret);
return ret; return ret;
} }
@ -226,7 +227,8 @@ float CDECL MSVCRT_acosf( float x )
float CDECL MSVCRT_asinf( float x ) float CDECL MSVCRT_asinf( float x )
{ {
float ret = atan2f(x, sqrtf((1 - x) * (1 + x))); float ret = atan2f(x, sqrtf((1 - x) * (1 + x)));
if (x < -1.0 || x > 1.0 || !finitef(x)) math_error(_DOMAIN, "asinf", x, 0, ret); if (x < -1.0 || x > 1.0 || !finitef(x))
return math_error(_DOMAIN, "asinf", x, 0, ret);
return ret; return ret;
} }
@ -236,7 +238,7 @@ float CDECL MSVCRT_asinf( float x )
float CDECL MSVCRT_atanf( float x ) float CDECL MSVCRT_atanf( float x )
{ {
float ret = atanf(x); float ret = atanf(x);
if (!finitef(x)) math_error(_DOMAIN, "atanf", x, 0, ret); if (!finitef(x)) return math_error(_DOMAIN, "atanf", x, 0, ret);
return ret; return ret;
} }
@ -246,7 +248,7 @@ float CDECL MSVCRT_atanf( float x )
float CDECL MSVCRT_atan2f( float x, float y ) float CDECL MSVCRT_atan2f( float x, float y )
{ {
float ret = atan2f(x, y); float ret = atan2f(x, y);
if (isnan(x)) math_error(_DOMAIN, "atan2f", x, y, ret); if (isnan(x)) return math_error(_DOMAIN, "atan2f", x, y, ret);
return ret; return ret;
} }
@ -256,7 +258,7 @@ float CDECL MSVCRT_atan2f( float x, float y )
float CDECL MSVCRT_cosf( float x ) float CDECL MSVCRT_cosf( float x )
{ {
float ret = cosf(x); float ret = cosf(x);
if (!finitef(x)) math_error(_DOMAIN, "cosf", x, 0, ret); if (!finitef(x)) return math_error(_DOMAIN, "cosf", x, 0, ret);
return ret; return ret;
} }
@ -266,7 +268,7 @@ float CDECL MSVCRT_cosf( float x )
float CDECL MSVCRT_coshf( float x ) float CDECL MSVCRT_coshf( float x )
{ {
float ret = coshf(x); float ret = coshf(x);
if (isnan(x)) math_error(_DOMAIN, "coshf", x, 0, ret); if (isnan(x)) return math_error(_DOMAIN, "coshf", x, 0, ret);
return ret; return ret;
} }
@ -276,9 +278,9 @@ float CDECL MSVCRT_coshf( float x )
float CDECL MSVCRT_expf( float x ) float CDECL MSVCRT_expf( float x )
{ {
float ret = expf(x); float ret = expf(x);
if (isnan(x)) math_error(_DOMAIN, "expf", x, 0, ret); if (isnan(x)) return math_error(_DOMAIN, "expf", x, 0, ret);
else if (finitef(x) && !ret) math_error(_UNDERFLOW, "expf", x, 0, ret); if (finitef(x) && !ret) return math_error(_UNDERFLOW, "expf", x, 0, ret);
else if (finitef(x) && !finitef(ret)) math_error(_OVERFLOW, "expf", x, 0, ret); if (finitef(x) && !finitef(ret)) return math_error(_OVERFLOW, "expf", x, 0, ret);
return ret; return ret;
} }
@ -288,7 +290,7 @@ float CDECL MSVCRT_expf( float x )
float CDECL MSVCRT_fmodf( float x, float y ) float CDECL MSVCRT_fmodf( float x, float y )
{ {
float ret = fmodf(x, y); float ret = fmodf(x, y);
if (!finitef(x) || !finitef(y)) math_error(_DOMAIN, "fmodf", x, 0, ret); if (!finitef(x) || !finitef(y)) return math_error(_DOMAIN, "fmodf", x, 0, ret);
return ret; return ret;
} }
@ -298,8 +300,8 @@ float CDECL MSVCRT_fmodf( float x, float y )
float CDECL MSVCRT_logf( float x ) float CDECL MSVCRT_logf( float x )
{ {
float ret = logf(x); float ret = logf(x);
if (x < 0.0) math_error(_DOMAIN, "logf", x, 0, ret); if (x < 0.0) return math_error(_DOMAIN, "logf", x, 0, ret);
else if (x == 0.0) math_error(_SING, "logf", x, 0, ret); if (x == 0.0) return math_error(_SING, "logf", x, 0, ret);
return ret; return ret;
} }
@ -309,8 +311,8 @@ float CDECL MSVCRT_logf( float x )
float CDECL MSVCRT_log10f( float x ) float CDECL MSVCRT_log10f( float x )
{ {
float ret = log10f(x); float ret = log10f(x);
if (x < 0.0) math_error(_DOMAIN, "log10f", x, 0, ret); if (x < 0.0) return math_error(_DOMAIN, "log10f", x, 0, ret);
else if (x == 0.0) math_error(_SING, "log10f", x, 0, ret); if (x == 0.0) return math_error(_SING, "log10f", x, 0, ret);
return ret; return ret;
} }
@ -320,10 +322,10 @@ float CDECL MSVCRT_log10f( float x )
float CDECL MSVCRT_powf( float x, float y ) float CDECL MSVCRT_powf( float x, float y )
{ {
float z = powf(x,y); float z = powf(x,y);
if (x < 0 && y != floorf(y)) math_error(_DOMAIN, "powf", x, y, z); if (x < 0 && y != floorf(y)) return math_error(_DOMAIN, "powf", x, y, z);
else if (!x && finitef(y) && y < 0) math_error(_SING, "powf", x, y, z); if (!x && finitef(y) && y < 0) return math_error(_SING, "powf", x, y, z);
else if (finitef(x) && finitef(y) && !finitef(z)) math_error(_OVERFLOW, "powf", x, y, z); if (finitef(x) && finitef(y) && !finitef(z)) return math_error(_OVERFLOW, "powf", x, y, z);
else if (x && finitef(x) && finitef(y) && !z) math_error(_UNDERFLOW, "powf", x, y, z); if (x && finitef(x) && finitef(y) && !z) return math_error(_UNDERFLOW, "powf", x, y, z);
return z; return z;
} }
@ -333,7 +335,7 @@ float CDECL MSVCRT_powf( float x, float y )
float CDECL MSVCRT_sinf( float x ) float CDECL MSVCRT_sinf( float x )
{ {
float ret = sinf(x); float ret = sinf(x);
if (!finitef(x)) math_error(_DOMAIN, "sinf", x, 0, ret); if (!finitef(x)) return math_error(_DOMAIN, "sinf", x, 0, ret);
return ret; return ret;
} }
@ -343,7 +345,7 @@ float CDECL MSVCRT_sinf( float x )
float CDECL MSVCRT_sinhf( float x ) float CDECL MSVCRT_sinhf( float x )
{ {
float ret = sinhf(x); float ret = sinhf(x);
if (isnan(x)) math_error(_DOMAIN, "sinhf", x, 0, ret); if (isnan(x)) return math_error(_DOMAIN, "sinhf", x, 0, ret);
return ret; return ret;
} }
@ -353,7 +355,7 @@ float CDECL MSVCRT_sinhf( float x )
float CDECL MSVCRT_sqrtf( float x ) float CDECL MSVCRT_sqrtf( float x )
{ {
float ret = sqrtf(x); float ret = sqrtf(x);
if (x < 0.0) math_error(_DOMAIN, "sqrtf", x, 0, ret); if (x < 0.0) return math_error(_DOMAIN, "sqrtf", x, 0, ret);
return ret; return ret;
} }
@ -363,7 +365,7 @@ float CDECL MSVCRT_sqrtf( float x )
float CDECL MSVCRT_tanf( float x ) float CDECL MSVCRT_tanf( float x )
{ {
float ret = tanf(x); float ret = tanf(x);
if (!finitef(x)) math_error(_DOMAIN, "tanf", x, 0, ret); if (!finitef(x)) return math_error(_DOMAIN, "tanf", x, 0, ret);
return ret; return ret;
} }
@ -373,7 +375,7 @@ float CDECL MSVCRT_tanf( float x )
float CDECL MSVCRT_tanhf( float x ) float CDECL MSVCRT_tanhf( float x )
{ {
float ret = tanhf(x); float ret = tanhf(x);
if (!finitef(x)) math_error(_DOMAIN, "tanhf", x, 0, ret); if (!finitef(x)) return math_error(_DOMAIN, "tanhf", x, 0, ret);
return ret; return ret;
} }
@ -430,7 +432,8 @@ double CDECL MSVCRT_acos( double x )
* cancellation. The sqrt() makes things worse. A safer way to calculate * cancellation. The sqrt() makes things worse. A safer way to calculate
* acos() is to use atan2(sqrt((1 - x) * (1 + x)), x). */ * acos() is to use atan2(sqrt((1 - x) * (1 + x)), x). */
double ret = atan2(sqrt((1 - x) * (1 + x)), x); double ret = atan2(sqrt((1 - x) * (1 + x)), x);
if (x < -1.0 || x > 1.0 || !isfinite(x)) math_error(_DOMAIN, "acos", x, 0, ret); if (x < -1.0 || x > 1.0 || !isfinite(x))
return math_error(_DOMAIN, "acos", x, 0, ret);
return ret; return ret;
} }
@ -440,7 +443,8 @@ double CDECL MSVCRT_acos( double x )
double CDECL MSVCRT_asin( double x ) double CDECL MSVCRT_asin( double x )
{ {
double ret = atan2(x, sqrt((1 - x) * (1 + x))); double ret = atan2(x, sqrt((1 - x) * (1 + x)));
if (x < -1.0 || x > 1.0 || !isfinite(x)) math_error(_DOMAIN, "asin", x, 0, ret); if (x < -1.0 || x > 1.0 || !isfinite(x))
return math_error(_DOMAIN, "asin", x, 0, ret);
return ret; return ret;
} }
@ -450,7 +454,7 @@ double CDECL MSVCRT_asin( double x )
double CDECL MSVCRT_atan( double x ) double CDECL MSVCRT_atan( double x )
{ {
double ret = atan(x); double ret = atan(x);
if (isnan(x)) math_error(_DOMAIN, "atan", x, 0, ret); if (isnan(x)) return math_error(_DOMAIN, "atan", x, 0, ret);
return ret; return ret;
} }
@ -460,7 +464,7 @@ double CDECL MSVCRT_atan( double x )
double CDECL MSVCRT_atan2( double x, double y ) double CDECL MSVCRT_atan2( double x, double y )
{ {
double ret = atan2(x, y); double ret = atan2(x, y);
if (isnan(x)) math_error(_DOMAIN, "atan2", x, y, ret); if (isnan(x)) return math_error(_DOMAIN, "atan2", x, y, ret);
return ret; return ret;
} }
@ -470,7 +474,7 @@ double CDECL MSVCRT_atan2( double x, double y )
double CDECL MSVCRT_cos( double x ) double CDECL MSVCRT_cos( double x )
{ {
double ret = cos(x); double ret = cos(x);
if (!isfinite(x)) math_error(_DOMAIN, "cos", x, 0, ret); if (!isfinite(x)) return math_error(_DOMAIN, "cos", x, 0, ret);
return ret; return ret;
} }
@ -480,7 +484,7 @@ double CDECL MSVCRT_cos( double x )
double CDECL MSVCRT_cosh( double x ) double CDECL MSVCRT_cosh( double x )
{ {
double ret = cosh(x); double ret = cosh(x);
if (isnan(x)) math_error(_DOMAIN, "cosh", x, 0, ret); if (isnan(x)) return math_error(_DOMAIN, "cosh", x, 0, ret);
return ret; return ret;
} }
@ -490,9 +494,9 @@ double CDECL MSVCRT_cosh( double x )
double CDECL MSVCRT_exp( double x ) double CDECL MSVCRT_exp( double x )
{ {
double ret = exp(x); double ret = exp(x);
if (isnan(x)) math_error(_DOMAIN, "exp", x, 0, ret); if (isnan(x)) return math_error(_DOMAIN, "exp", x, 0, ret);
else if (isfinite(x) && !ret) math_error(_UNDERFLOW, "exp", x, 0, ret); if (isfinite(x) && !ret) return math_error(_UNDERFLOW, "exp", x, 0, ret);
else if (isfinite(x) && !isfinite(ret)) math_error(_OVERFLOW, "exp", x, 0, ret); if (isfinite(x) && !isfinite(ret)) return math_error(_OVERFLOW, "exp", x, 0, ret);
return ret; return ret;
} }
@ -502,7 +506,7 @@ double CDECL MSVCRT_exp( double x )
double CDECL MSVCRT_fmod( double x, double y ) double CDECL MSVCRT_fmod( double x, double y )
{ {
double ret = fmod(x, y); double ret = fmod(x, y);
if (!isfinite(x) || !isfinite(y)) math_error(_DOMAIN, "fmod", x, y, ret); if (!isfinite(x) || !isfinite(y)) return math_error(_DOMAIN, "fmod", x, y, ret);
return ret; return ret;
} }
@ -512,8 +516,8 @@ double CDECL MSVCRT_fmod( double x, double y )
double CDECL MSVCRT_log( double x ) double CDECL MSVCRT_log( double x )
{ {
double ret = log(x); double ret = log(x);
if (x < 0.0) math_error(_DOMAIN, "log", x, 0, ret); if (x < 0.0) return math_error(_DOMAIN, "log", x, 0, ret);
else if (x == 0.0) math_error(_SING, "log", x, 0, ret); if (x == 0.0) return math_error(_SING, "log", x, 0, ret);
return ret; return ret;
} }
@ -523,8 +527,8 @@ double CDECL MSVCRT_log( double x )
double CDECL MSVCRT_log10( double x ) double CDECL MSVCRT_log10( double x )
{ {
double ret = log10(x); double ret = log10(x);
if (x < 0.0) math_error(_DOMAIN, "log10", x, 0, ret); if (x < 0.0) return math_error(_DOMAIN, "log10", x, 0, ret);
else if (x == 0.0) math_error(_SING, "log10", x, 0, ret); if (x == 0.0) return math_error(_SING, "log10", x, 0, ret);
return ret; return ret;
} }
@ -534,10 +538,14 @@ double CDECL MSVCRT_log10( double x )
double CDECL MSVCRT_pow( double x, double y ) double CDECL MSVCRT_pow( double x, double y )
{ {
double z = pow(x,y); double z = pow(x,y);
if (x < 0 && y != floor(y)) math_error(_DOMAIN, "pow", x, y, z); if (x < 0 && y != floor(y))
else if (!x && isfinite(y) && y < 0) math_error(_SING, "pow", x, y, z); return math_error(_DOMAIN, "pow", x, y, z);
else if (isfinite(x) && isfinite(y) && !isfinite(z)) math_error(_OVERFLOW, "pow", x, y, z); if (!x && isfinite(y) && y < 0)
else if (x && isfinite(x) && isfinite(y) && !z) math_error(_UNDERFLOW, "pow", x, y, z); return math_error(_SING, "pow", x, y, z);
if (isfinite(x) && isfinite(y) && !isfinite(z))
return math_error(_OVERFLOW, "pow", x, y, z);
if (x && isfinite(x) && isfinite(y) && !z)
return math_error(_UNDERFLOW, "pow", x, y, z);
return z; return z;
} }
@ -547,7 +555,7 @@ double CDECL MSVCRT_pow( double x, double y )
double CDECL MSVCRT_sin( double x ) double CDECL MSVCRT_sin( double x )
{ {
double ret = sin(x); double ret = sin(x);
if (!isfinite(x)) math_error(_DOMAIN, "sin", x, 0, ret); if (!isfinite(x)) return math_error(_DOMAIN, "sin", x, 0, ret);
return ret; return ret;
} }
@ -557,7 +565,7 @@ double CDECL MSVCRT_sin( double x )
double CDECL MSVCRT_sinh( double x ) double CDECL MSVCRT_sinh( double x )
{ {
double ret = sinh(x); double ret = sinh(x);
if (isnan(x)) math_error(_DOMAIN, "sinh", x, 0, ret); if (isnan(x)) return math_error(_DOMAIN, "sinh", x, 0, ret);
return ret; return ret;
} }
@ -567,7 +575,7 @@ double CDECL MSVCRT_sinh( double x )
double CDECL MSVCRT_sqrt( double x ) double CDECL MSVCRT_sqrt( double x )
{ {
double ret = sqrt(x); double ret = sqrt(x);
if (x < 0.0) math_error(_DOMAIN, "sqrt", x, 0, ret); if (x < 0.0) return math_error(_DOMAIN, "sqrt", x, 0, ret);
return ret; return ret;
} }
@ -577,7 +585,7 @@ double CDECL MSVCRT_sqrt( double x )
double CDECL MSVCRT_tan( double x ) double CDECL MSVCRT_tan( double x )
{ {
double ret = tan(x); double ret = tan(x);
if (!isfinite(x)) math_error(_DOMAIN, "tan", x, 0, ret); if (!isfinite(x)) return math_error(_DOMAIN, "tan", x, 0, ret);
return ret; return ret;
} }
@ -587,7 +595,7 @@ double CDECL MSVCRT_tan( double x )
double CDECL MSVCRT_tanh( double x ) double CDECL MSVCRT_tanh( double x )
{ {
double ret = tanh(x); double ret = tanh(x);
if (isnan(x)) math_error(_DOMAIN, "tanh", x, 0, ret); if (isnan(x)) return math_error(_DOMAIN, "tanh", x, 0, ret);
return ret; return ret;
} }
@ -838,8 +846,8 @@ __int64 CDECL _abs64( __int64 n )
double CDECL MSVCRT__logb(double num) double CDECL MSVCRT__logb(double num)
{ {
double ret = logb(num); double ret = logb(num);
if (isnan(num)) math_error(_DOMAIN, "_logb", num, 0, ret); if (isnan(num)) return math_error(_DOMAIN, "_logb", num, 0, ret);
else if (!num) math_error(_SING, "_logb", num, 0, ret); if (!num) return math_error(_SING, "_logb", num, 0, ret);
return ret; return ret;
} }
@ -1070,10 +1078,10 @@ double CDECL MSVCRT_ldexp(double num, MSVCRT_long exp)
double z = ldexp(num,exp); double z = ldexp(num,exp);
if (isfinite(num) && !isfinite(z)) if (isfinite(num) && !isfinite(z))
math_error(_OVERFLOW, "ldexp", num, exp, z); return math_error(_OVERFLOW, "ldexp", num, exp, z);
else if (num && isfinite(num) && !z) if (num && isfinite(num) && !z)
math_error(_UNDERFLOW, "ldexp", num, exp, z); return math_error(_UNDERFLOW, "ldexp", num, exp, z);
else if (z == 0 && signbit(z)) if (z == 0 && signbit(z))
z = 0.0; /* Convert -0 -> +0 */ z = 0.0; /* Convert -0 -> +0 */
return z; return z;
} }

View File

@ -575,6 +575,9 @@ static struct _exception exception;
static int CDECL matherr_callback(struct _exception *e) static int CDECL matherr_callback(struct _exception *e)
{ {
exception = *e; exception = *e;
if (!strcmp(e->name, "acos") && e->arg1 == 2)
e->retval = -1;
return 0; return 0;
} }
@ -748,6 +751,7 @@ static void test_math_errors(void)
double (CDECL *p_func3d)(double, double, double); double (CDECL *p_func3d)(double, double, double);
double (CDECL *p_funcdl)(double, long); double (CDECL *p_funcdl)(double, long);
HMODULE module; HMODULE module;
double d;
int i; int i;
__setusermatherr(matherr_callback); __setusermatherr(matherr_callback);
@ -817,6 +821,9 @@ static void test_math_errors(void)
ok(exception.arg2 == testsdl[i].b, ok(exception.arg2 == testsdl[i].b,
"%s(%f, %ld) got exception arg2 %f\n", testsdl[i].func, testsdl[i].a, testsdl[i].b, exception.arg2); "%s(%f, %ld) got exception arg2 %f\n", testsdl[i].func, testsdl[i].a, testsdl[i].b, exception.arg2);
} }
d = acos(2.0);
ok(d == -1.0, "failed to change log10 return value: %e\n", d);
} }
static void test_asctime(void) static void test_asctime(void)