【问题标题】:To handle rational number without losing accuracy of computation in Matlab?在 Matlab 中处理有理数而不损失计算的准确性?
【发布时间】:2014-01-19 03:24:01
【问题描述】:

我想在计算中使用这个有理数而不损失 Matlab 中图片的准确性:

f = 359.0 + 16241/16250.0

我认为存储,例如 f = uint64(359.0 + 16241/16250.0) 会失去准确性,在 Matlab 中被视为 360。

我认为处理这件事的最好方法是永远不要存储价值,而是存储它的因素,例如

% f = a + b/c
a = 359
b = 16241
c = 16250

然后通过变量a、b和c进行计算,并以图片的形式给出结果。

这是保持准确性的好方法吗?

【问题讨论】:

  • 你想用这个数字做什么样的计算? “图片”是什么意思?
  • 与 A. Donda 相同的问题,请澄清。
  • 实际上,Matlab 将这个数字“视为”359.99944615384617918。如果计算x = 359 + 16241/16250,然后计算(x - 359) * 16250,则结果为16241.000000000411092,即x 的绝对误差约为2.5e-14。你真的需要比这更好的精度吗?
  • 不是 C 专家,但据我所知,C 数据类型不是通用定义的,但它们的精度取决于机器架构和/或编译器。相比之下,无论机器或 Matlab 版本如何,Matlab “双”总是相同的。由于 64 位架构是当今的常态,我希望 C “双”与 Matlab “双”相同。不过,似乎有一个“long double”,它可能会提供更好的准确性,请参阅en.wikipedia.org/wiki/C_data_types#Basic_types
  • 我有时会处理脑电图数据,12 位精度定点是常态,特殊放大器可能达到 16 位。这比“双精度”的 52 位 浮点 少得多,以至于这些差异并不重要。

标签: matlab computation


【解决方案1】:

正如您所建议的,如果您绝对不想在存储有理数时失去准确性,最好的解决方案可能是按照整数分量存储数字。

代替您的三个组件 (f = a + b/c),您可以将表示减少到两个组件:f = n/d。因此,每个有理数都将被定义(并存储)为二分量整数向量[n d]。例如,您示例中的数字f 对应于n=5849991 和d=16250。

为了简化以这种方式存储的有理数的处理,您可以定义一个辅助函数,在应用所需的操作之前将[n d] 表示形式转换为n/d:

useInteger = @(x, nd, fun) fun(x,double(nd(1))/double(nd(2)));

然后

>> x = sqrt(pi);
>> nd = int64([5849991 16250]);
>> useInteger(x, nd, @plus)
ans =
  361.7719
>> useInteger(x, nd, @times)
ans =
  638.0824

如果您想在计算中实现任意高精度,您应该考虑使用带有字符串参数的variable-precision arithmetic (vpa)。通过这种方法,您可以指定所需的位数:

>> vpa('sqrt(pi)*5849991/16250', 50)
ans =
638.08240465923757600307902117159072301901656248436

【讨论】:

  • 你估计这个方法的绝对误差是多少?优于 2.5e-14?
  • 对于存储:错误为零,因为您以整数形式存储精确的分子和分母。对于计算:错误与您直接执行x*5849991/16250 相同
  • 请看我的回答,作为您回答的扩展。我从计算中得到的错误比存储更多。我认为,两种情况下的计算错误应该是相同的,但是您是否存储变量。
  • @Masi 我不确定我是否理解您的回答。无论如何,如果您想要任意高的精度,请考虑使用vpa。查看我的更新答案及其链接
【解决方案2】:

也许创建一个Rational 类并定义所需的操作(plus、minus、times 等)。从这样的开始:

Rational.m

classdef Rational
    properties
        n;
        d;
    end
    methods
        function obj = Rational(n,d)
            GCD = gcd(n,d);
            obj.n = n./GCD;
            obj.d = d./GCD;
        end

        function d = dec(obj)
            d = double(obj.n)/double(obj.d);
        end

        % X .* Y
        function R = times(X,Y)
            chkxy(X,Y);
            if isnumeric(X),
                N = X .* Y.n; D = Y.d;
            elseif isnumeric(Y),
                N = X.n .* Y; D = X.d;
            else
                N = X.n .* Y.n; D = X.d .* Y.d;
            end
            R = Rational(N,D);
        end

        % X * Y
        function R = mtimes(X,Y)
            R = times(X,Y);
        end

        % X ./ Y
        function R = rdivide(X,Y)
            if isnumeric(Y),
                y = Rational(1,Y);
            else
                y = Rational(Y.d,Y.n);
            end
            R = times(X,y);
        end

        % X / Y
        function R = mrdivide(X,Y)
            R = rdivide(X,Y);
        end

        % X + Y
        function R = plus(X,Y)
            chkxy(X,Y);
            if isnumeric(X),
                N = X.*Y.d + Y.n; D = Y.d;
            elseif isnumeric(Y),
                N = Y.*X.d + X.n; D = X.d;
            else
                D = lcm(X.d,Y.d);
                N = sum([X.n Y.n].*(D./[X.d Y.d]));
            end
            R = Rational(N,D);
        end

        % X - Y
        function R = minus(X,Y)
            R = plus(X,-Y);
        end

        % -X
        function R = uminus(X)
            R = Rational(-X.n,X.d);
        end

        function chkxy(X,Y)
            if (~isa(X, 'Rational') && ~isnumeric(X)) || ...
                    (~isa(Y, 'Rational') && ~isnumeric(Y)),
                error('X and Y must be Rational or numeric.');
            end
        end
    end
end

示例

构造对象:

>> clear all % reset class definition
>> r1 = Rational(int64(1),int64(2))
r1 = 
  Rational with properties:

    n: 1
    d: 2
>> r2 = Rational(int64(3),int64(4))
r2 = 
  Rational with properties:

    n: 3
    d: 4

加减法:

>> r1+r2
ans = 
  Rational with properties:

    n: 5
    d: 4
>> r1-r2
ans = 
  Rational with properties:

    n: -1
    d: 4

乘除法:

>> r1*r2
ans = 
  Rational with properties:

    n: 3
    d: 8
>> r1/r2
ans = 
  Rational with properties:

    n: 2
    d: 3

获取十进制值:

>> r12 = r1/r2; % 2/3 ((1/2)/(3/4))
>> f = r12.dec
f =
    0.6667

【讨论】:

  • 应该 int64 是 uint64 吗?
  • 可能是这样,但是要使减法正常工作,签名数据类型是必要的。 int64 有很多位。事实上,double 为您提供了 53 位的精确整数表示,因此几乎在所有情况下都足够了。
  • 我在下面为您的答案添加了一个扩展。我有 Wigner-Ville 分布,它给了我两倍。我想用 Rational 乘以双倍。你能举个例子吗?
  • @Masi 创建Rational 和double 分子/分母。同样,53 位精确整数高达 9,007,199,254,740,992。
【解决方案3】:

LuisMendo 答案的扩展

我认为这是您 py 建议的错误

>>> a = 638.08240465923757600307902117159072301901656248436059
>>> a
638.0824046592376             % do not know if Python is computing here with exact number
>>> b = 638.0824
>>> ave = abs(b+a)/2
>>> diff = abs(b-a)
>>> ave = abs(b+a)/2
>>> diff/ave
7.30193709165014e-09

这比上面建议的错误存储错误更多。

我在 WolframAlpha 中运行

x = sqrt(pi)
x*5849991/16250

得到

509.11609919757198016211937362635174599076143654820109

我不确定这是否是您对答案的评论中的意思。

【讨论】:

    【解决方案4】:

    chappjc 答案的扩展。

    我现在有

    [B,T,F] = tfrwv(data1, 1:length(data1), length(data1)); % here F double
    fs = Rational(uint64(5849991), uint64(16250));
    t = 1/fs;
    imagesc(T*t, F*fs, B); 
    

    我运行它

    Error using  .* 
    Integers can only be combined with integers of
    the same class, or scalar doubles.
    
    Error in  .*  (line 23)
                        N = X .* Y.n; D = Y.d;
    
    Error in  *  (line 34)
                    R = times(X,Y);
    

    如何在这个类中用 Rational 乘以双精度数?

    【讨论】:

    • T 和 F 是向量(不是标量),因此需要使用 double 分子/分母生成 Rational。但我看不出这种方法能给你带来什么。如果乘以十进制值(即T 和F),为什么要保持有理表示?
    • 对于imagesc,您必须使用F*fs.dec,但这并不比使用F*5849991/16250 更好。只有在对多个有理数进行算术运算时,使用像 Rational 这样的有理类才有帮助。
    • @chappjc 是的,我正在开发有理数的 Wigner-Ville 分布。对于这些示例,我必须参考可用的公共资源进行交流。
    猜你喜欢
    • 1970-01-01
    • 2017-11-23
    • 2019-02-18
    • 2019-10-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-08
    • 2011-11-18
    相关资源
    最近更新 更多