如果我們具有最大最小值的具體範圍,將它標準化爲0..1浮點值是相當容易的,但是如果我們沒有具體限制?是否有可能建立通用功能,輸出0和1之間?在我看來,我認爲這是不可能的,但我不是數學專家。將範圍(-inf ... + inf)中的任何值標準化爲(0 ... 1)。可能嗎?
我在尋找上的JavaScript或PHP實現,但在C/C++/Python的/德爾福的任何代碼即可提供實例(如果有一些)
如果我們具有最大最小值的具體範圍,將它標準化爲0..1浮點值是相當容易的,但是如果我們沒有具體限制?是否有可能建立通用功能,輸出0和1之間?在我看來,我認爲這是不可能的,但我不是數學專家。將範圍(-inf ... + inf)中的任何值標準化爲(0 ... 1)。可能嗎?
我在尋找上的JavaScript或PHP實現,但在C/C++/Python的/德爾福的任何代碼即可提供實例(如果有一些)
有很多方法可以做到這一點。我將會忽略映射-inf
和+inf
,這可以通過條件語句來完成。
exp(x)/(1 + exp(x))
或等效1/(1 + exp(-x))
其中exp
是指數函數。這是一個後勤功能。atan(x)/pi + 1/2
(tanh(x) + 1)/2
(1 + x/sqrt(1 + x*x))/2
(1 + x/(1 + abs(x))/2
(erf(x) + 1)/2
你可能已經注意到,大多數的這些都需要一個映射(-1,1),並將其更改爲(0 ,1)。前者通常更容易。下面是這些函數的圖表:
在我的Python 3.5.2中,最快的是(1 + x/(1 + abs(x)) * 0.5
。
與幾乎所有的編程浮點數,值是對數分佈。因此首先採取值的log()
開始映射關注邊緣案例的關注。
double map(double x, double x0, double x1, double y0, double y1) {
return (x - x0)/(x1 - x0) * (y1 - y0) + y0;
}
double noramlize01(double x) {
assert(x == x); // fail is x is NaN
// These values only need to be calculated once.
double logxmin = log(DBL_TRUE_MIN); // e.g. -323.306...
double logxmax = log(DBL_MAX); // e.g. 308.254...
double y;
if (x < -DBL_MAX) y = 0.0;
else if (x < 0.0) {
y = map(log(-x), logxmax, logxmin, nextafter(0.0,1.0), nextafter(0.5,0.0));
} else if (x == 0.0) {
y = 0.5;
} else if (x <= DBL_MAX) {
y = map(log(x), logxmin, logxmax, nextafter(0.5,1.0), nextafter(1.0,0.5));
} else {
y = 1.0;
}
return y;
}
double round_n(double x, unsigned n) {
return x * n;
}
void testr(double x) {
printf("% 20e %#.17g\n", x, noramlize01(x));
//printf("% 20e %.17f\n", -x, noramlize01(-x));
}
int main(void) {
double t[] = {0.0, DBL_TRUE_MIN, DBL_MIN, 1/M_PI, 1/M_E,
1.0, M_E, M_PI, DBL_MAX, INFINITY};
for (unsigned i = sizeof t/sizeof t[0]; i > 0; i--) {
testr(-t[i-1]);
}
for (unsigned i = 0; i < sizeof t/sizeof t[0]; i++) {
testr(t[i]);
}
}
樣本輸出
-inf 0.0000000000000000
-1.797693e+308 4.9406564584124654e-324
-3.141593e+00 0.24364835649917244
-2.718282e+00 0.24369811843639441
-1.000000e+00 0.24404194470924687
-3.678794e-01 0.24438577098209935
-3.183099e-01 0.24443553291932130
-2.225074e-308 0.48760724499523350
-4.940656e-324 0.49999999999999994
-0.000000e+00 0.50000000000000000
0.000000e+00 0.50000000000000000
4.940656e-324 0.50000000000000011
2.225074e-308 0.51239275500476655
3.183099e-01 0.75556446708067870
3.678794e-01 0.75561422901790065
1.000000e+00 0.75595805529075311
2.718282e+00 0.75630188156360556
3.141593e+00 0.75635164350082751
1.797693e+308 0.99999999999999989
inf 1.0000000000000000
如果你不介意位點播和有信心,代碼使用IEEE二進制64位浮點,一些快速的代碼只有一個幾FP數學運算
// If double is 64-bit and same endian as integer
double noramlize01(double x) {
assert(x == x); // fail if x is NaN
union {
double d;
int64_t i64;
uint64_t u64;
} u = {x};
double d;
if (u.i64 < 0) {
u.u64 -= 0x8000000000000000;
d = (double) -u.i64;
} else {
d = (double) u.i64;
}
return d/(+2.0 * 0x7ff0000000000000) + 0.5;
}
//類似的測試代碼爲this answer
-inf 0.0000000000000000
-1.797693e+308 0.0000000000000000
-3.141593e+00 0.24973844740430023
-2.718282e+00 0.24979014633262589
-1.000000e+00 0.25012212994626282
-2.225074e-308 0.49975574010747437
-4.940656e-324 0.50000000000000000
-0.000000e+00 0.50000000000000000
0.000000e+00 0.50000000000000000
4.940656e-324 0.50000000000000000
2.225074e-308 0.50024425989252563
1.000000e+00 0.74987787005373718
2.718282e+00 0.75020985366737414
3.141593e+00 0.75026155259569971
1.797693e+308 1.0000000000000000
inf 1.0000000000000000
哇!這真的看起來不錯,但我有點但堅持這個「union {d; i64} = x」部分,據我所知,這是C++如何將數字拆分成union,但在JS中我沒有實現它。無論如何看起來像這個代碼在JS中很好用https://jsfiddle.net/p1ratrulezzz/qz5nzry8/ – P1ratRuleZZZ