0 01111111 00000000000000000000000 符号 指数8(e) 仮数23(f)符合、指数、仮数の各部が示す値の組み合わせによって、以下のような数値を表現することができ ます。
0/1 10000000 10010010000111111011010 = +/- 3.14159250 0/1 10000000 10010010000111111011011 = +/- 3.14159274 0/1 10000000 10010010000111111011100 = +/- 3.14159297つまり、3.1241592までは正しく表現することが出来ますが、それ以上の精度を 期待することはできません。
0 01111111111 000000000000000000000...00 符号 指数11(e) 仮数52(f)単精度浮動小数点数と同様に、符合、指数、仮数の組合わせで以下のような数 値が表現できます。
0/1 10000000000 10010010000111111011010..0111 = +/- 3.14159265358979267 0/1 10000000000 10010010000111111011010..1000 = +/- 3.14159265358979311 0/1 10000000000 10010010000111111011010..1001 = +/- 3.14159265358979356つまり、3.141592653589793までは正しく表現することが出来ます。
Ax2+By+C=0このとき、二次方程式の解を求めるプログラムの入出力形式は次のようになりま す。
#include <stdio.h>
#include <math.h>
const int BUFFER_SIZE = 100;
main()
{
double a, b, c, d;
double x1, x2;
int n;
char buf[BUFFER_SIZE], e;
printf("Please input a b c : ");
while(1){
fgets(buf, BUFFER_SIZE, stdin);
n = sscanf(buf,"%lf %lf %lf %c", &a, &b, &c, &e);
if(n == 3) {
break;
}
else if(n == -1) {
continue;
}
else {
printf("Usage:a b c\n");
return 0;
}
}
d = b * b - 4 * a * c; /* 判別式の計算 */
if(d >= 0){ /* 判別式が正のとき、解を計算 */
x1 = (-b + sqrt(d)) / (2.0 * a);
x2 = (-b - sqrt(d)) / (2.0 * a);
printf("x1 = %27.20e(%27.20e)\n",x1,a*x1*x1+b*x1+c);
printf("x2 = %27.20e(%27.20e)\n",x2,a*x2*x2+b*x2+c);
}
else{ /* 判別式が負のとき、虚数解と表示 */
printf("x1 = 虚数解\n");
printf("x2 = 虚数解\n");
}
return 0;
}
|
A=1、B=-1000000、C=1のとき
|B|=|-1000000|
=1000000.000000000000000 (0x412e848000000000)
√(B2-4AC)=√(1000000000000.0-4.0)
=999999.999997999984771 (0x412e847fffffbce4)
2つの差 (|B|-√(B2-4AC))
=0.000002000015229 (0x3ec0c70000000000)
この部分の情報が欠落
つまりここでは、Bの絶対値がA,Cに比べて大きくなることで桁落ちが生じ、正確な
解が得られなくなってしまっているわけです。
x1 = (fabs(b)+sqrt(d))/(2.0*a); /*fabs()は絶対値を返す関数*/
x1 = (b<0.0)?x1:-x1; /*bが負ならx1=x1,bが正ならx1=-x1を返す*/
x2 = c/(a*x1);
修正したプログラムは次のようになります。
#include <stdio.h>
#include <math.h>
const int BUFFER_SIZE = 100;
main()
{
double a, b, c, d;
double x1, x2;
int n;
char buf[BUFFER_SIZE], e;
printf("Please input a b c : ");
while(1){
fgets(buf, BUFFER_SIZE, stdin);
n = sscanf(buf,"%lf %lf %lf %c", &a, &b, &c, &e);
if(n == 3) {
break;
}
else if(n == -1) {
continue;
}
else{
printf("Usage:a b c\n");
return 0;
}
}
if(a == 0){
printf("x = %27.20e\n",-c/b);
}
else{
if(b == 0 && c == 0){
printf("x1 = %27.20e\n",0.0);
printf("x2 = %27.20e\n",0.0);
}
else{
d = b * b - 4 * a * c; /* 判別式の計算 */
if(d >= 0){ /* 判別式が正のとき、解を計算 */
x1 = (fabs(b) + sqrt(d)) / (2.0 * a); /* 修正箇所 */
if ( b >= 0.0 ) {
x1 = -x1;
}
x2 = c / (a * x1); /* 修正箇所 */
printf("x1 = %27.20e(%27.20e)\n",x1,a*x1*x1+b*x1+c);
printf("x2 = %27.20e(%27.20e)\n",x2,a*x2*x2+b*x2+c);
}else{ /* 判別式が負のとき虚数解と表示 */
printf("x1 = 虚数解\n");
printf("x2 = 虚数解\n");
}
}
}
return 0;
}
|
1 -2 1 x1 = 1.00000000000000000000e+00( 0.00000000000000000000e+00) x2 = 1.00000000000000000000e+00( 0.00000000000000000000e+00) 1 -1000 1 x1 = 9.99998999999000034222e+02( 1.16415321826934814453e-10) x2 = 1.00000100000200006431e-03( 0.00000000000000000000e+00) 1 -1000000 1 x1 = 9.99999999998999992386e+05( 0.00000000000000000000e+00) x2 = 1.00000000000100008890e-06( 0.00000000000000000000e+00) 1 -1000000000 1 x1 = 1.00000000000000000000e+09( 1.00000000000000000000e+00) x2 = 1.00000000000000006228e-09( 0.00000000000000000000e+00)