将双精度(例如N)转换为 p/q 形式时存在问题
...当 N = 8.2
一个典型的double
不能完全编码8.2。相反,最接近的可表示double
是关于
8.19999999999999928945726423989981412887573...
8.20000000000000106581410364015027880668640... // next closest
当代码执行
double N = 8.2;
它将8.19999999999999928945726423989981412887573...
被转换为理性形式。
将 a 转换double
为 p/q 形式:
将 N 乘以一个大数,例如 $k = 10^{10}$
这可能会溢出double
. 第一步应该是确定是否double
很大,它是什么情况,它是一个整数。
不要乘以 10 的幂,因为double
肯定使用二进制编码。乘以 10、100 等可能会引入舍入误差。
绝大多数的C 实现double
使用二进制编码,因此FLT_RADIX == 2
.
然后每个有限double x
都有一个有效数字,它是某个整数的分数超过 2 的某个幂:数字的二进制分数DBL_MANT_DIG
@Richard Critten。这通常是 53 个二进制数字。
确定 的指数double
。如果足够大 或x == 0.0
,double
是一个整数。
否则,将分子和分母按比例缩放DBL_MANT_DIG
。当分子是偶数时,将分子和分母都减半。由于denominator
是 2 的幂,因此出于简化考虑,不需要其他素数。
#include <float.h>
#include <math.h>
#include <stdio.h>
void form_ratio(double x) {
double numerator = x;
double denominator = 1.0;
if (isfinite(numerator) && x != 0.0) {
int expo;
frexp(numerator, &expo);
if (expo < DBL_MANT_DIG) {
expo = DBL_MANT_DIG - expo;
numerator = ldexp(numerator, expo);
denominator = ldexp(1.0, expo);
while (fmod(numerator, 2.0) == 0.0 && denominator > 1.0) {
numerator /= 2.0;
denominator /= 2.0;
}
}
}
int pre = DBL_DECIMAL_DIG;
printf("%.*g --> %.*g/%.*g\n", pre, x, pre, numerator, pre, denominator);
}
int main(void) {
form_ratio(123456789012.0);
form_ratio(42.0);
form_ratio(1.0 / 7);
form_ratio(867.5309);
}
输出
123456789012 --> 123456789012/1
42 --> 42/1
0.14285714285714285 --> 2573485501354569/18014398509481984
867.53089999999997 --> 3815441248019913/4398046511104