我有将双重(比如 N )转换为p / q形式(理性形式)的问题,为此我有以下策略:
p = y*k
和q = k
gcd(p,q)
并找到p = p/gcd(p,q)
和q = p/gcd(p,q)
当N = 8.2
时,如果我们使用笔和纸解决问题,答案是正确的,但8.2
在8.19999999
(双倍)中表示为N
,则会导致问题它的理性形式转换。
我尝试了其他方式:(我使用了大号10 ^ k而不是100)
if(abs(y*100 - round(y*100)) < 0.000001) y = round(y*100)/100
但是这种方法也没有给出正确的表示。
有什么方法可以执行从double到p / q的等效转换?
答案 0 :(得分:1)
浮点运算非常困难。正如评论中提到的,部分困难在于你需要用二进制表示你的数字。
例如,数字0.125可以用二进制表示:
0.125 = 2^-3 = 0b0.001
但数字0.12不能。
至11位重要人物:
0.12 = 0b0.00011110101
如果将其转换回小数,则错误变得明显:
0b0.00011110101 = 0.11962890625
所以如果你写:
double a = 0.2;
机器实际上做的是找到最接近的二进制表示,它可以在双数据类型中保存。这是一个近似值,因为如上所述,0.2不能用二进制精确表示。
一种可能的方法是定义一个“ε”。它确定您的数字与最近的可表示二进制浮点的接近程度。
这是一篇关于浮点的好文章:
https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
答案 1 :(得分:1)
有将双倍(例如 N )转换为p / q形式的问题 ......当N = 8.2
时
典型的double
无法正确编码8.2 。相反,最接近的可表示double
约为
8.19999999999999928945726423989981412887573...
8.20000000000000106581410364015027880668640... // next closest
当代码
时double N = 8.2;
将8.19999999999999928945726423989981412887573...
转换为合理形式。
double
转换为p / q格式:将N倍乘以大数字表示$ k = 10 ^ {10} $
这可能会溢出double
。第一步应该是确定double
是否很大,它是一个整数。
当double
当然使用二进制编码时,不要乘以10的幂。乘以10,100等可能会引入舍入误差。
double
的C实现压倒性地使用二进制编码,因此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