Prob.h Prob.cc
文档作者:jianzhu
创立时间:08.09.11
--------------------------------------
1、概述
--------------------------------------
这两个文件定义了一组函数用于处理浮点数和对数的加减操作。
同时定义一个用于将字符串浮点数转换为浮点数的函数。
--------------------------------------
2、函数功能解释
--------------------------------------
a) LogPtoProb函数
<src>
0 inline Prob LogPtoProb(LogP2 prob)
1 {
2 if (prob == LogP_Zero) {
3 return 0;
4 } else {
5 return exp(prob * M_LN10);
6 }
7 }
</src>
功能:将以10为底的对数概率值转化为概率值本身
细解:第2行判断对数概率值是否为LogP_Zero即-HUGE_VAL,若为该值,则
直接返回0;否则执行第5行。
第5行首先将prob*M_LN10获得概率的ln值,然后对其求以自然数为底的运算,
则获得概率值本身,同时返回该概率值。
注: prob --> log10(a)
M_LN10 --> ln10
exp^X --> e^X
log10(a) = ln(a)/ln10
-> prob*M_LN10 = ln(a)/ln10 * ln10 = ln(a)
-> exp(prob * M_LN10) = exp(ln(a)) = a
b) LogPtoPPL函数
<src>
0 inline Prob LogPtoPPL(LogP prob)
1 {
2 return exp(- prob * M_LN10);
3 }
</src>
功能:将以10为底的对数概率值转化为概率值对应的perplexity。
对于至含一个概率的情况,即为将以10为底的对数概率值转化为概率值倒数
perplexity = P(W1W2...Wn)^-1/N
当只有一个概率值P时,上式简化为:
perplexity = P^-1
细解:
prob --> log10(a)
M_LN10 --> ln10
exp^X --> e^X
log10(a) = ln(a)/ln10
-> prob*M_LN10 = ln(a)/ln10 * ln10 = ln(a)
-> exp(-prob * M_LN10) = exp(-ln(a)) = exp(ln(a^-1)) = a^-1 = 1/a
c) ProbToLogP函数
<src>
0 inline LogP ProbToLogP(Prob prob)
1 {
2 return log10(prob);
3 }
</src>
功能:将概率值转化为以10为底的对数值
细解:第2行通过调用log10函数,直接将prob转换为以10为底的对数值,同时返回该值
d) MixLogP函数
<src>
0 inline LogP MixLogP(LogP prob1, LogP prob2, double lambda)
1 {
2 return ProbToLogP(lambda * LogPtoProb(prob1) +
3 (1 - lambda) * LogPtoProb(prob2));
4 }
</src>
功能:对prob1和prob2概率对数值求lambda线性插值
细解:第2行首先通过调用LogPtoProb函数分别获取prob1和prob2中的概率值,然后对该
概率值使用lambda参数进行线性插值运算,同时通过调用ProbToLogP函数将运算结果转换
为以10为底的概率对数值,同时返回该值。
e)AddLogP函数
<src>
0 inline LogP2 AddLogP(LogP2 x, LogP2 y)
1 {
2 if (x<y) {
3 LogP2 temp = x; x = y; y = temp;
4 }
5 if (y == LogP_Zero) {
6 return x;
7 } else {
8 LogP2 diff = y - x;
9 return x + log10(1.0 + exp(diff * M_LN10));
10 }
11 }
</src>
功能:对概率对数值中的概率进行求和运算,同时返回求和运算结果的以10为底的对数值
细解:第2-4行用于处理x小于y的情况,同时将较大的值存储到x中,而将较小的值存储到y中。
第5-7行用于处理当y为longP_Zero的情况,此时直接返回x值。
第7-10行用于处理剩下的情况
x --> log10(a)
y --> log10(b)
diff = y-x = log10(b) - log10(a) = log10(b/a)
diff * M_LN10 = ln(b/a)
exp(diff*M_LN10) = b/a
log10(1.0 + exp(diff * M_LN10)) = log10(1.0 + b/a) = log10( (a+b)/a )
x + log10(1.0 + exp(diff * M_LN10)) = log10(a) + log10( (a+b)/a )
= log10(a+b);
f) SubLogP函数
<src>
0 inline LogP2 SubLogP(LogP2 x, LogP2 y)
1 {
2 assert(x >= y);
3 if (x == y) {
4 return LogP_Zero;
5 } else if (y == LogP_Zero) {
6 return x;
7 } else {
8 LogP2 diff = y - x;
9 return x + log10(1.0 - exp(diff * M_LN10));
10 }
11 }
</src>
功能:对概率对数值中的概率进行求差运算,同时返回求差运算结果的以10为底的对数值
细解:第2行用于处理x小于y情况,由于log10(A)是一个递增函数,且定义域为(0, 正无穷)
因此需要保证 x >= y。
第3-5行处理当x==y的情况,此时直接返回LogP_Zero,即负无穷小。
第5-7行处理当y为负无穷小的情况,此时直接返回x。
第7-10行处理剩余的情况
x --> log10(a)
y --> log10(b)
diff = y-x = log10(b) - log10(a) = log10(b/a)
diff * M_LN10 = ln(b/a)
exp(diff*M_LN10) = b/a
log10(1.0 - exp(diff * M_LN10)) = log10(1.0 - b/a) = log10( (a-b)/a )
x + log10(1.0 - exp(diff * M_LN10)) = log10(a) + log10( (a-b)/a )
= log10(a-b);
g) weightLogP函数
<src>
0 inline LogP weightLogP(double weight, LogP prob)
1 {
2 /*
3 * avoid NaN if weight == 0 && prob == -Infinity
4 */
5 if (weight == 0.0) {
6 return 0.0;
7 } else {
8 return weight * prob;
9 }
10 }
</src>
功能:对将权重weight乘到prob上,并返回运算结果
细解:第5-7行处理当weight为0.0的情况,此时直接返回0.0;否则执行第7-9行
第7-9行对weight和prob进行乘法运算,并返回运算结果。
h) rint函数
<src>
0 inline double rint(double x)
1 {
2 if (x >= 0) {
3 return (double)(int)(x + 0.5);
4 } else {
5 return (double)(int)(x - 0.5);
6 }
7 }
</src>
功能:对浮点数x进行求顶或求底操作
细解:第2-4行处理当x >= 0时进行求顶运算,同时返回运算结果
第4-6行处理当x < 0时进行求底运算,同时返回运算结果
i) finite函数
<src>
0 inline int finite (double x)
1 {
2 if (x < 1.e+300 && x > -1.e+300)
3 return 1;
4 else
5 return 0;
6 }
</src>
功能:判断浮点数是否足够大或足够小,同时在满足条件时返回1,否则返回0
细解:第2-3行处理x大于10的300次方或小于-10的300次方的情况,此时认为
x为一个无穷值,直接返回1;否则执行第5行返回0。
j) parseLogP函数
<src>
0 Boolean
1 parseLogP(const char *str, LogP &result)
2 {
3 const unsigned maxDigits = 8; // number of decimals in an integer
4
5 const char *cp = str;
6 const char *cp0;
7 Boolean minus = false;
8
9 /*
10 * Log probabilties are typically negative values of magnitude > 0.0001,
11 * and thus are usually formatted without exponential notation.
12 * We parse this type of format using integer arithmetic for speed,
13 * and fall back onto scanf() in all other cases.
14 * We also use scanf() when there are too many digits to handle with
15 * integers.
16 * Finally, we also parse +/- infinity values as they are printed by
17 * printf(). These are "[Ii]nf" or "[Ii]nfinity".
18 */
19
20 /*
21 * Parse optional sign
22 */
23 if (*cp == '-') {
24 minus = true;
25 cp++;
26 } else if (*cp == '+') {
27 cp++;
28 }
29 cp0 = cp;
30
31 unsigned digits = 0; // total value of parsed digits
32 unsigned decimals = 1; // scaling factor from decimal point
33 unsigned precision = 0; // total number of parsed digits
34
35 /*
36 * Parse digits before decimal point
37 */
38 while (isdigit(*cp)) {
39 digits = digits * 10 + (*(cp++) - '0');
40 precision ++;
41 }
42
43 if (*cp == '.') {
44 cp++;
45
46 /*
47 * Parse digits after decimal point
48 */
49 while (isdigit(*cp)) {
50 digits = digits * 10 + (*(cp++) - '0');
51 precision ++;
52 decimals *= 10;
53 }
54 }
55
56 /*
57 * If we're at the end of the string then we're done.
58 * Otherwise there was either an error or some format we can't
59 * handle, so fall back on scanf(), after checking for infinity
60 * values.
61 */
62 if (*cp == '\0' && precision <= maxDigits) {
63 result = (minus ? - (LogP)digits : (LogP)digits) / (LogP)decimals;
64 return true;
65 } else if ((*cp0 == 'i' || *cp0 == 'I') &&
66 (strncmp(cp0, "Inf", 3) == 0 || strncmp(cp0, "inf", 3) == 0))
67 {
68 result = (minus ? LogP_Zero : LogP_Inf);
69 return true;
70 } else {
71 return (sscanf(str, "%f", &result) == 1);
72 }
73 }
</src>
功能:用于分析字符串表示的浮点数并将分析出的浮点数存储到result中。
细解:第23-28行用于分析str中表示的浮点数是否为负值,若为负值,则将minus设为true。
第38-41行分析str表示的浮点数小数点之前的数值,并将其保存到digits中;
第43-54行分析小数点之后的数值,同时将小数点之后的位数值记录到decimals中;
第62-65行处理当str被正确分析,且precision小于8的情况,即浮点数的数值位数小于8,此时
直接将result设为digits和decimals相除运算的结果。若minus为true,则将result设为负的结
果,同时返回true;
第65-70行通过分析剩下的无法分析的字符串是否为Inf或inf,同时结合minus来决定将result
设为LogP_Zero(负无穷小)还是LogP_Inf(正无穷大),同时返回true;
第70-72行用于处理其他情况,此时直接调用sscanf函数,从str中读出一个浮点数并将其保存在
result中。同时返回调用sscanf是否成功的判断结果。
k) ProbToBytelog函数
<src>
0 inline Bytelog ProbToBytelog(Prob prob)
1 {
2 return (int)rint(log(prob) * (10000.5 / 1024.0));
3 }
</src>
功能:将prob转换为Bytelog
细解:
Bytelog介绍:A bytelog is a logarithm to base 1.0001, divided by 1024 and rounded to
an integer。
因此第2行通过使用ln(prob)*10000.5来模拟log1.0001(prob)
举例:prob == 2.0
则
ln(prob)*10000.5 = 6931.8183791897330668270298306425
log1.0001(prob) = ln(prob) / ln(1.0001) = 6931.8183734137953551959678499998
因此可见 10000.5 约等于 1/ln(1.0001) 因此这里直接用10000.5来模拟1/ln(1.0001)
通过使用1024除上面结果,并将结果取整,即得到相应的Bytelog值
l) ProbToIntlog函数
<src>
0 inline Intlog ProbToIntlog(Prob prob)
1 {
2 return (int)rint(log(prob) * 10000.5);
3 }
</src>
功能:将prob转化为Intlog
细解:
Intlog介绍:A Intlog is a logarithm to base 1.0001, and rounded to
an integer。
因此第2行通过使用ln(prob)*10000.5来模拟log1.0001(prob)
举例:prob == 2.0
则
ln(prob)*10000.5 = 6931.8183791897330668270298306425
log1.0001(prob) = ln(prob) / ln(1.0001) = 6931.8183734137953551959678499998
因此可见 10000.5 约等于 1/ln(1.0001) 因此这里直接用10000.5来模拟1/ln(1.0001)
通过将结果取整,即得到相应的Intlog值
m) LogPtoBytelog函数
<src>
0 inline Bytelog LogPtoBytelog(LogP prob)
1 {
2 return (int)rint(prob * (M_LN10 * 10000.5 / 1024.0));
3 }
</src>
功能:将logProb转化为Bytelog
细解:
Bytelog介绍:A bytelog is a logarithm to base 1.0001, divided by 1024 and rounded to
an integer。
因此第2行通过使用Prob * M_LN10(Prob == log10(prob) == ln(prob)/ln(10); M_LN10 == ln10)
来获得ln(prob),并通过ln(prob)*10000.5来模拟log1.0001(prob)
举例:prob == 2.0
则
ln(prob)*10000.5 = 6931.8183791897330668270298306425
log1.0001(prob) = ln(prob) / ln(1.0001) = 6931.8183734137953551959678499998
因此可见 10000.5 约等于 1/ln(1.0001) 因此这里直接用10000.5来模拟1/ln(1.0001)
通过使用1024除上面结果,并将结果取整,即得到相应的Bytelog值
n) LogPtoIntlog函数
<src>
0 inline Intlog LogPtoIntlog(LogP prob)
1 {
2 return (int)rint(prob * (M_LN10 * 10000.5));
3 }
</src>
功能:将logProb转化为Intlog
细解:
Intlog介绍:A Intlog is a logarithm to base 1.0001, and rounded to
an integer。
因此第2行通过使用Prob * M_LN10(Prob == log10(prob) == ln(prob)/ln(10); M_LN10 == ln10)
来获得ln(prob),并通过ln(prob)*10000.5来模拟log1.0001(prob)
举例:prob == 2.0
则
ln(prob)*10000.5 = 6931.8183791897330668270298306425
log1.0001(prob) = ln(prob) / ln(1.0001) = 6931.8183734137953551959678499998
因此可见 10000.5 约等于 1/ln(1.0001) 因此这里直接用10000.5来模拟1/ln(1.0001)
通过将结果取整,即得到相应的Intlog值
o) IntlogToLogP函数
<src>
0 inline LogP IntlogToLogP(double prob) /* use double argument to avoid loss
1 * of information when converting from
2 * floating point values */
3 {
4 return prob/(M_LN10 * 10000.5);
5 }
</src>
功能:将Intlog转换为logProb
细解:
Intlog介绍:A Intlog is a logarithm to base 1.0001, and rounded to
an integer。
因此第2行通过使用Prob / M_LN10(Prob == log1.0001(prob) == ln(prob)/ln(1.0001); M_LN10 == ln10)
来获得log10(prob)/ln(1.0001),并通过log10(prob)/(ln(1.0001)*10000.5)来模拟log10(prob)
因为 10000.5 约等于 1/ln(1.0001)
p) BytelogToLogP函数
<src>
0 inline LogP BytelogToLogP(double bytelog) /* use double argument so we can
1 * scale float values without loss of
2 * precision */
3 {
4 return bytelog * (1024.0 / 10000.5 / M_LN10);
5 }
</src>
功能:将Bytelog转换为logProb
细解:
Bytlog介绍:
A bytelog is a logarithm to base 1.0001, divided by 1024 and rounded to
an integer。
因此第2行通过使用bytelog*1024.0近似得到log1.0001(prob),通过使用10000.5/M_LN10
即ln(1.0001)/ln10和log1.0001(prob)进行乘法操作得到logProb
因为log1.0001(prob) = lnprob/ln1.0001
因此log1.0001(prob)*(ln(1.0001)/ln10) = lnprob/ln10 = log10(prob) --> logProb
q) IntlogToBytelog函数
<src>
0 inline Bytelog IntlogToBytelog(Intlog intlog)
1 {
2 int bytelog = ((-intlog) + (1 << (BytelogShift-1))) >> BytelogShift;
3
4 if (bytelog > 255) {
5 bytelog = 255;
6 }
7 return -bytelog;
8 }
</src>
功能:将Intlog转换为Bytelog函数
细解:
r) BytelogToIntlog函数
<src>
0 inline Intlog BytelogToIntlog(Bytelog bytelog)
1 {
2 return bytelog << BytelogShift;
3 }
</src>
功能:将Bytelog转换为Intlog
细解:由于Bytelog和Intlog只差了1024,所以只需要
将bytelog乘以1024即可得到Intlog。(也即左移10位)
--------------------------------------
知识点:
--------------------------------------
1、对数和其系数之间的相互转换
系数转对数
该操作一般可以通过直接调用库函数log10来实现。
对数转系数
log10(a) --> a
根据以下公式
log10(a) = ln(a)/ln(10)
M_LN10 = ln(10)
可以推导出
log10(a) * M_LN10 = ln(a)/ln(10) * ln(10) = ln(a)
又有公式
exp(ln(a)) = a
可得
exp(log10(a) * M_LN10) = exp(ln(a)) = a;
2、对数系数的加减运算
加运算
log10(a) log10(b) --> log10(a+b)
假设 b > a
由log10的递增特性以及对数的相加和相减运算操作可得
log10(b) - log10(a) = log10(b/a)
1 + exp(log10(b/a)*M_LN10) = 1 + b/a = (a+b)/a
因此
log10(a) + log10(1 + exp(log10(b/a)*M_LN10)
= log10(a) + log10((a+b)/a) = log10(a+b)
减运算
log10(a) log10(b) --> log10(a-b)
由log10的递增特性以及对数的相加和相减运算操作可得
log10(b) - log10(a) = log10(b/a)
1 - exp(log10(b/a)*M_LN10) = 1 - b/a = (a-b)/a
因此
log10(a) + log10(1 - exp(log10(b/a)*M_LN10)
= log10(a) + log10((a-b)/a) = log10(a-b)