无心人
发表于 2008-12-17 15:14:22
发现50#用的程序是每10^7计数一次
所以以前说的每4秒 10^8错误
是40秒
无心人
发表于 2008-12-18 08:00:01
当前界限下的阶乘77773146302!, 对应的对数0.497149872686531
开始数字3141592653534
检验:
(15:09) gp > n = 77773146302
%26 = 77773146302
(07:55) gp > (n+1/2)*log(n) + 1/2*log(2*pi) - n + 1/(12*n) - 1/(360*n^3)
%27 = 1872548868987.616634238479898278998546965437108456887088346
(07:55) gp > %27 / log(10)
%28 = 813237640895.4971498726865435859265735865657796251777623316
(07:55) gp > %28 - floor(%28)
%29 = 0.497149872686543585926573586565779625177762331512
(07:56) gp > 10^%29
%30 = 3.14159265353488687304651737046701462136950721154
gxqcn
发表于 2008-12-18 08:33:02
原帖由 无心人 于 2008-12-17 14:08 发表 http://bbs.emath.ac.cn/images/common/back.gif
另外一个问题,似乎目前的库,对long double都不支持
我修改程序用long double,取不到对数值
而目前的double精度恐怕支持不了多久了
真正的 long double 有 80 bits,至少需要 10 字节存储,
所以一般编译器不支持。
但可以通过内嵌汇编得到80 bits精度的浮点数,
可惜FPU设计成栈模式,手工编写调试汇编代码非常累人(我之前曾写过一些)!
以下是 FPU 中一些可能对本主题讨论话题有帮助的指令:
FLDL2E Load the log base 2 of e (Napierian constant)
FLDL2T Load the log base 2 of Ten
FLDLG2 Load the log base 10 of 2 (common log of 2)
FLDLN2 Load the log base e of 2 (natural log of 2)
FLDPI Load the value of PI
FSQRT Square root of ST(0)
FYL2X Y*Log2X
FYL2XP1 Y*Log2(X+1)
无心人
发表于 2008-12-18 09:06:09
写汇编代码在 gcc里并不方便
在windows下编译GMP和MPFR也不方便
呵呵
两难
无心人
发表于 2008-12-18 09:17:27
下一个选择就是5000亿左右了, 估计是极限了
仅仅在两个界限的对数值最后四位有差别
每次加1000个,再调整一次
估计差别值在范围上很小
有错过的可能
=============================
刚才加大了输出的精度
发现是6位的差别
我再去查下double的具体数值
无心人
发表于 2008-12-20 21:10:13
今天有时间测试了下程序的时间#include <gmp.h>
#include <mpfr.h>
#include <stdio.h>
#include <math.h>
#include <sys/time.h>
#define DefaultBitLength 128
mpfr_t ln10, cA, cT;
//=1/2ln(2π)+(n+1/2)ln(n)-n+1/12n
//n > 10^8 误差<1/360Z^3 = 10^(-26)
long double stirling(double n)
{
mpfr_t a, b, c, s;
long double r;
mpfr_init2(a, DefaultBitLength);
mpfr_init2(b, DefaultBitLength);
mpfr_set_d(a, n, GMP_RNDD); //a=n
mpfr_init2(c, DefaultBitLength);
mpfr_init2(s, DefaultBitLength);
mpfr_div(s, cT, a, GMP_RNDD); //s = 1/12a = 1/12n
mpfr_sub(s, s, a, GMP_RNDD); //s = s - a = - n + 1/12n
mpfr_log(c, a, GMP_RNDD); //c = ln(n)
mpfr_set_d(b, 0.5, GMP_RNDD);
mpfr_add(b, a, b, GMP_RNDD);
mpfr_mul(b, b, c, GMP_RNDD); //b = (n + 1/2)ln(n)
mpfr_add(s, s, b, GMP_RNDD); //s = (n + 1/2)ln(n) - n + 1/12n
mpfr_add(s, s, cA, GMP_RNDD); //最终结果s=1/2ln(2*pi) + (n + 1/2)ln(n) - n + 1/12n
mpfr_mul(s, s, ln10, GMP_RNDD); //转换为log10
mpfr_floor(b, s);
mpfr_sub(s, s, b, GMP_RNDD); //只要小数
r = mpfr_get_ld(s, GMP_RNDD);
mpfr_clear(a);
mpfr_clear(b);
mpfr_clear(c);
mpfr_clear(s);
return r;
}
int main(void)
{
double a, b, low, hi, lf, t;
double start, used_time;
int find = 0, test1 = 0;
long i = 0, j = 0;
struct timeval start_time, end_time;
mpfr_init2(ln10, DefaultBitLength);
mpfr_init2(cA, DefaultBitLength);
mpfr_init2(cT, DefaultBitLength);
mpfr_set_ui(ln10, 10, GMP_RNDD);
mpfr_set_ui(cT, 1, GMP_RNDD);
mpfr_log(ln10, ln10, GMP_RNDD);
mpfr_div(ln10, cT, ln10, GMP_RNDD);
mpfr_const_pi(cA, GMP_RNDD);
mpfr_mul_ui(cA, cA, 2, GMP_RNDD);
mpfr_log(cA, cA, GMP_RNDD);
mpfr_div_ui(cA, cA, 2, GMP_RNDD);
mpfr_div_ui(cT, cT, 12, GMP_RNDD);
printf("请输入起始搜索数字:");
scanf("%lf", &start);
printf("请输入上界: ");
scanf("%lf", &hi);
printf("请输入下界: ");
scanf("%lf", &low);
hi = log10(hi);
low = log10(low);
printf("起始位置:%.0f\n当前界限[%.17f, %.17f]\n", start, low, hi);
a = start;
b = 1.0;
while (a >= 10.0)
{
a /= 10.0;
b /= 10.0;
}
lf = 2.0;
gettimeofday(&start_time, NULL);
for (i = 0; i < 1000; i ++)
lf += stirling(lf + 100000000.0);
gettimeofday(&end_time, NULL);
used_time = ((end_time.tv_sec - start_time.tv_sec)*1000000.0 + (end_time.tv_usec - start_time.tv_usec)) / 1000.0;
printf("高精度stirling函数执行时间(毫秒): %8.6lf, 测试值:%.8lf\n", used_time, lf);
lf = stirling(start);
printf("起始对数: %.17f\n", lf);
gettimeofday(&start_time, NULL);
while (1)
{
start += 1.0;
a = log10(start*b);
if (a >= 1.0)
{
b /= 10.0;
a -= 1.0;
}
i ++;
lf += a;
if (lf >= 1.0)
lf -= 1.0;
// printf("[%.0f, %.15f]", start, lf);
if ((lf >= low) && (lf < hi))
{
find = 1;
break;
}
if (i >= 1000)
{
lf = stirling(start);
i = 0;
j ++;
if (j >= 1000)
{
if (test1 == 0)
{
gettimeofday(&end_time, NULL);
used_time = ((end_time.tv_sec - start_time.tv_sec)*1000000.0 + (end_time.tv_usec - start_time.tv_usec))/1000000.0;
printf("每10^6次查找执行时间(秒): %.6lf\n", used_time);
test1 = 1;
}
}
}
}
if (find)
{
printf("\n当前界限下的阶乘%.0f!, 对应的对数%.15f", start, lf);
}
mpfr_clear(ln10);
mpfr_clear(cA);
mpfr_clear(cT);
return 1;
}===============================================
E:\math>factpi1
请输入起始搜索数字:100000000
请输入上界: 3.1415927
请输入下界: 3.1415926
起始位置:100000000
当前界限
高精度stirling函数执行时间(毫秒): 40.058000, 测试值:493.74586181
起始对数: 0.20876475177585671
每10^6次查找执行时间(秒): 0.460662
当前界限下的阶乘149832529!, 对应的对数0.497149873593380
=================================================
对应机器P4 2.0/1G DDR400
软件XP
编译器MinGW + GCC 4.3.2
使用GMP 4.2.4 + MPFR 2.3.2, 编译为静态包
===================================================
可以看到MPFR下,每1000次stirling函数计算仅0.04秒
而double下每1000*1000次叠加对数值是需要0.46秒
可计算出高精度浮点的运算时间仅是double叠加对数的100倍
所以在目前情况下,普通函数计算称为瓶颈
另外,根据执行时间判断
每次计算需要800-1000时钟周期
无心人
发表于 2008-12-20 21:50:52
使用-ffast-math选项编译后的输出
请输入起始搜索数字:100000000
请输入上界: 3.1415927
请输入下界: 3.1415926
起始位置:100000000
当前界限
高精度stirling函数执行时间(毫秒): 30.043000, 测试值:493.74586181
起始对数: 0.20876475177585671
每10^6次查找执行时间(秒): 0.150216
当前界限下的阶乘143984543!, 对应的对数0.497149871271711
无心人
发表于 2008-12-20 21:55:52
57#同样条件下
但修改程序如下
#include <gmp.h>
#include <mpfr.h>
#include <stdio.h>
#include <math.h>
#include <sys/time.h>
#define DefaultBitLength 128
mpfr_t ln10, cA, cT;
//=1/2ln(2π)+(n+1/2)ln(n)-n+1/12n
//n > 10^8 误差<1/360Z^3 = 10^(-26)
long double stirling(double n)
{
mpfr_t a, b, c, s;
long double r;
mpfr_init2(a, DefaultBitLength);
mpfr_init2(b, DefaultBitLength);
mpfr_set_d(a, n, GMP_RNDD); //a=n
mpfr_init2(c, DefaultBitLength);
mpfr_init2(s, DefaultBitLength);
mpfr_div(s, cT, a, GMP_RNDD); //s = 1/12a = 1/12n
mpfr_sub(s, s, a, GMP_RNDD); //s = s - a = - n + 1/12n
mpfr_log(c, a, GMP_RNDD); //c = ln(n)
mpfr_set_d(b, 0.5, GMP_RNDD);
mpfr_add(b, a, b, GMP_RNDD);
mpfr_mul(b, b, c, GMP_RNDD); //b = (n + 1/2)ln(n)
mpfr_add(s, s, b, GMP_RNDD); //s = (n + 1/2)ln(n) - n + 1/12n
mpfr_add(s, s, cA, GMP_RNDD); //最终结果s=1/2ln(2*pi) + (n + 1/2)ln(n) - n + 1/12n
mpfr_mul(s, s, ln10, GMP_RNDD); //转换为log10
mpfr_floor(b, s);
mpfr_sub(s, s, b, GMP_RNDD); //只要小数
r = mpfr_get_ld(s, GMP_RNDD);
mpfr_clear(a);
mpfr_clear(b);
mpfr_clear(c);
mpfr_clear(s);
return r;
}
int main(void)
{
double a, b, low, hi, lf, t;
double start, used_time;
int find = 0, test1 = 0;
long i = 0, j = 0;
struct timeval start_time, end_time;
mpfr_init2(ln10, DefaultBitLength);
mpfr_init2(cA, DefaultBitLength);
mpfr_init2(cT, DefaultBitLength);
mpfr_set_ui(ln10, 10, GMP_RNDD);
mpfr_set_ui(cT, 1, GMP_RNDD);
mpfr_log(ln10, ln10, GMP_RNDD);
mpfr_div(ln10, cT, ln10, GMP_RNDD);
mpfr_const_pi(cA, GMP_RNDD);
mpfr_mul_ui(cA, cA, 2, GMP_RNDD);
mpfr_log(cA, cA, GMP_RNDD);
mpfr_div_ui(cA, cA, 2, GMP_RNDD);
mpfr_div_ui(cT, cT, 12, GMP_RNDD);
printf("请输入起始搜索数字:");
scanf("%lf", &start);
printf("请输入上界: ");
scanf("%lf", &hi);
printf("请输入下界: ");
scanf("%lf", &low);
hi = log10(hi);
low = log10(low);
printf("起始位置:%.0f\n当前界限[%.17f, %.17f]\n", start, low, hi);
a = start;
b = 1.0;
while (a >= 10.0)
{
a /= 10.0;
b *= 10.0;////////////////////////////////////修改
}
lf = 2.0;
gettimeofday(&start_time, NULL);
for (i = 0; i < 1000; i ++)
lf += stirling(lf + 100000000.0);
gettimeofday(&end_time, NULL);
used_time = ((end_time.tv_sec - start_time.tv_sec)*1000000.0 + (end_time.tv_usec - start_time.tv_usec)) / 1000.0;
printf("高精度stirling函数执行时间(毫秒): %8.6lf, 测试值:%.8lf\n", used_time, lf);
lf = stirling(start);
printf("起始对数: %.17f\n", lf);
gettimeofday(&start_time, NULL);
while (1)
{
start += 1.0;
a = log10(start/b);//////////////////////////////修改
if (a >= 1.0)
{
b *= 10.0; //////////////////////////////修改
a -= 1.0;
}
i ++;
lf += a;
if (lf >= 1.0)
lf -= 1.0;
// printf("[%.0f, %.15f]", start, lf);
if ((lf >= low) && (lf < hi))
{
find = 1;
break;
}
if (i >= 1000)
{
// lf = stirling(start);
i = 0;
j ++;
if (j >= 1000)
{
if (test1 == 0)
{
gettimeofday(&end_time, NULL);
used_time = ((end_time.tv_sec - start_time.tv_sec)*1000000.0 + (end_time.tv_usec - start_time.tv_usec))/1000000.0;
printf("每10^6次查找执行时间(秒): %.6lf\n", used_time);
test1 = 1;
}
}
}
}
if (find)
{
printf("\n当前界限下的阶乘%.0f!, 对应的对数%.15f", start, lf);
}
mpfr_clear(ln10);
mpfr_clear(cA);
mpfr_clear(cT);
return 1;
}
请输入起始搜索数字:100000000
请输入上界: 3.1415927
请输入下界: 3.1415926
起始位置:100000000
当前界限
高精度stirling函数执行时间(毫秒): 30.043000, 测试值:493.74586181
起始对数: 0.20876475177585671
每10^6次查找执行时间(秒): 0.230332
当前界限下的阶乘149832529!, 对应的对数0.497149873593496
无心人
发表于 2008-12-20 21:57:48
可以看到
乘法和除法在浮点运算下
对时间的影响是很大的
还有一个可以修改优化的地方是对取10的对数
改为取2的对数再乘以log10(2)
但不知道标准函数有取2的对数的函数么?
还是需要自己写汇编
无心人
发表于 2008-12-20 22:13:45
去掉-ffast-math,同时增加了对求10的对数的优化
#include <gmp.h>
#include <mpfr.h>
#include <stdio.h>
#include <math.h>
#include <sys/time.h>
#define DefaultBitLength 128
mpfr_t ln10, cA, cT;
//=1/2ln(2π)+(n+1/2)ln(n)-n+1/12n
//n > 10^8 误差<1/360Z^3 = 10^(-26)
long double stirling(double n)
{
mpfr_t a, b, c, s;
long double r;
mpfr_init2(a, DefaultBitLength);
mpfr_init2(b, DefaultBitLength);
mpfr_set_d(a, n, GMP_RNDD); //a=n
mpfr_init2(c, DefaultBitLength);
mpfr_init2(s, DefaultBitLength);
mpfr_div(s, cT, a, GMP_RNDD); //s = 1/12a = 1/12n
mpfr_sub(s, s, a, GMP_RNDD); //s = s - a = - n + 1/12n
mpfr_log(c, a, GMP_RNDD); //c = ln(n)
mpfr_set_d(b, 0.5, GMP_RNDD);
mpfr_add(b, a, b, GMP_RNDD);
mpfr_mul(b, b, c, GMP_RNDD); //b = (n + 1/2)ln(n)
mpfr_add(s, s, b, GMP_RNDD); //s = (n + 1/2)ln(n) - n + 1/12n
mpfr_add(s, s, cA, GMP_RNDD); //最终结果s=1/2ln(2*pi) + (n + 1/2)ln(n) - n + 1/12n
mpfr_mul(s, s, ln10, GMP_RNDD); //转换为log10
mpfr_floor(b, s);
mpfr_sub(s, s, b, GMP_RNDD); //只要小数
r = mpfr_get_ld(s, GMP_RNDD);
mpfr_clear(a);
mpfr_clear(b);
mpfr_clear(c);
mpfr_clear(s);
return r;
}
int main(void)
{
double a, b, low, hi, lf, t;
double start, used_time, LT2;
int find = 0, test1 = 0;
long i = 0, j = 0;
struct timeval start_time, end_time;
mpfr_init2(ln10, DefaultBitLength);
mpfr_init2(cA, DefaultBitLength);
mpfr_init2(cT, DefaultBitLength);
mpfr_set_ui(ln10, 10, GMP_RNDD);
mpfr_set_ui(cT, 1, GMP_RNDD);
mpfr_log(ln10, ln10, GMP_RNDD);
mpfr_div(ln10, cT, ln10, GMP_RNDD);
mpfr_const_pi(cA, GMP_RNDD);
mpfr_mul_ui(cA, cA, 2, GMP_RNDD);
mpfr_log(cA, cA, GMP_RNDD);
mpfr_div_ui(cA, cA, 2, GMP_RNDD);
mpfr_div_ui(cT, cT, 12, GMP_RNDD);
printf("请输入起始搜索数字:");
scanf("%lf", &start);
printf("请输入上界: ");
scanf("%lf", &hi);
printf("请输入下界: ");
scanf("%lf", &low);
LT2 = log10(2);
hi = log10(hi);
low = log10(low);
printf("起始位置:%.0f\n当前界限[%.17f, %.17f]\n", start, low, hi);
a = start;
b = 1.0;
while (a >= 10.0)
{
a /= 10.0;
b /= 10.0;
}
lf = 2.0;
gettimeofday(&start_time, NULL);
for (i = 0; i < 1000; i ++)
lf += stirling(lf + 100000000.0);
gettimeofday(&end_time, NULL);
used_time = ((end_time.tv_sec - start_time.tv_sec)*1000000.0 + (end_time.tv_usec - start_time.tv_usec)) / 1000.0;
printf("高精度stirling函数执行时间(毫秒): %8.6lf, 测试值:%.8lf\n", used_time, lf);
lf = stirling(start);
printf("起始对数: %.17f\n", lf);
gettimeofday(&start_time, NULL);
while (1)
{
start += 1.0;
a = log2(start*b) * LT2;
if (a >= 1.0)
{
b /= 10.0;
a -= 1.0;
}
i ++;
lf += a;
if (lf >= 1.0)
lf -= 1.0;
// printf("[%.0f, %.15f]", start, lf);
if ((lf >= low) && (lf < hi))
{
find = 1;
break;
}
if (i >= 1000)
{
// lf = stirling(start);
i = 0;
j ++;
if (j >= 1000)
{
if (test1 == 0)
{
gettimeofday(&end_time, NULL);
used_time = ((end_time.tv_sec - start_time.tv_sec)*1000000.0 + (end_time.tv_usec - start_time.tv_usec))/1000000.0;
printf("每10^6次查找执行时间(秒): %.6lf\n", used_time);
test1 = 1;
}
}
}
}
if (find)
{
printf("\n当前界限下的阶乘%.0f!, 对应的对数%.15f", start, lf);
}
mpfr_clear(ln10);
mpfr_clear(cA);
mpfr_clear(cT);
return 1;
}
=========================================
请输入起始搜索数字:100000000
请输入上界: 3.1415927
请输入下界: 3.1415926
起始位置:100000000
当前界限
高精度stirling函数执行时间(毫秒): 30.044000, 测试值:493.74586181
起始对数: 0.20876475177585671
每10^6次查找执行时间(秒): 0.240346
当前界限下的阶乘149832529!, 对应的对数0.497149877671004
页:
1
2
3
4
5
[6]
7
8
9
10
11
12
13
14