简体   繁体   English

matlab和c与cos函数不同

[英]matlab and c differ with cos function

I have a program implemented in matlab and the same program in c, and the results differ. 我有一个在matlab中实现的程序和c中的相同程序,结果不同。

I am bit puzzled that the cos function does not return the exact same result. 我有点困惑的是cos函数没有返回完全相同的结果。

I use the same computer, Intel Core 2 Duo, and 8 bytes double data type in both cases. 在这两种情况下,我使用相同的计算机,Intel Core 2 Duo和8字节双数据类型。

Why does the result differ? 为什么结果不同?

Here is the test: 这是测试:

c:
double a = 2.89308776595231886830;
double b = cos(a);
printf("a = %.50f\n", a);
printf("b = %.50f\n", b);
printf("sizeof(a): %ld\n", sizeof(a));
printf("sizeof(b): %ld\n", sizeof(b));

a = 2.89308776595231886830106304842047393321990966796875
b = -0.96928123535654842068964853751822374761104583740234
sizeof(a): 8
sizeof(b): 8



matlab:
a = 2.89308776595231886830
b = cos(a);
fprintf('a = %.50f\n', a);
fprintf('b = %.50f\n', b);
whos('a')
whos('b')

a = 2.89308776595231886830106304842047393321990966796875
b = -0.96928123535654830966734607500256970524787902832031
  Name      Size            Bytes  Class     Attributes
  a         1x1                 8  double              

  Name      Size            Bytes  Class     Attributes
  b         1x1                 8  double  


So, b differ a bit (very slightly, but enough to make my debuging task difficult)

b = -0.96928123535654842068964853751822374761104583740234  c
b = -0.96928123535654830966734607500256970524787902832031  matlab

I use the same computer, Intel Core 2 Duo, and 8 bytes double data type. 我使用相同的计算机,Intel Core 2 Duo和8字节双数据类型。

Why does the result differ? 为什么结果不同?

does matlab do not use the cos function hardware built-in in Intel? matlab不使用英特尔内置的cos函数硬件吗?

Is there a simple way to use the same cos function in matlab and c (with exact results), even if a bit slower, so that I can safely compare the results of my matlab and c program? 有没有一种简单的方法在matlab和c中使用相同的cos函数(具有精确的结果),即使有点慢,这样我可以安全地比较我的matlab和c程序的结果?


Update: 更新:

thanks a lot for your answers! 非常感谢你的回答!

So, as you have pointed out, the cos function for matlab and c differ. 因此,正如您所指出的,matlab和c的cos函数不同。 That's amazing! 棒极了! I thought they were using the cos function built-in in the Intel microprocessor. 我以为他们正在使用英特尔微处理器内置的cos函数。

The cos version of matlab is equal (at least for this test) to the one of matlab. matlab的cos版本与matlab的cos版本相同(至少对于此测试而言)。 you can try from matlab also: b=java.lang.Math.cos(a) 你也可以从matlab尝试:b = java.lang.Math.cos(a)

Then, I did a small MEX function to use the cos c version from within matlab, and it works fine; 然后,我做了一个小的MEX函数来使用matlab中的cos c版本,它运行正常; This allows me to debug the my program (the same one implemented in matlab and c) and see at what point they differ, which was the purpose of this post. 这允许我调试我的程序(在matlab和c中实现的同一个程序)并查看它们的不同之处,这是本文的目的。

The only thing is that calling the MEX c cos version from matlab is way too slow. 唯一的事情是从matlab调用MEX c cos版本太慢了。

I am now trying to call the Java cos function from c (as it is the same from matlab), see if that goes faster. 我现在试图从c调用Java cos函数(因为它与matlab相同),看看它是否更快。

Floating point numbers are stored in binary, not decimal. 浮点数以二进制形式存储,而不是十进制。 A double precision float has 52 bits of precision, which translates to roughly 15 significant decimal places. double精度浮点数具有52位精度,可转换为大约15位有效小数位。 In other words, the first 15 nonzero decimal digits of a double printed in decimal are enough to uniquely determine which double was printed. 换句话说,以十进制打印的double精度的前15个非零十进制数字足以唯一地确定打印了哪个double精度数。

As a diadic rational , a double has an exact representation in decimal, which takes many more decimal places than 15 to represent (in your case, 52 or 53 places, I believe). 作为一个diadic理性double有一个十进制的精确表示,它需要比15更多的小数位代表(在你的情况下,52或53个地方,我相信)。 However, the standards for printf and similar functions do not require the digits past the 15th to be correct; 但是, printf和类似功能的标准不要求超过15的数字是正确的; they could be complete nonsense. 他们可能完全是胡说八道。 I suspect one of the two environments is printing the exact value, and the other is printing a poor approximation, and that in reality both correspond to the exact same binary double value. 我怀疑这两个环境中的一个是打印精确值,另一个是打印差的近似值,实际上两者都对应于完全相同的二进制double值。

Using the script at http://www.mathworks.com/matlabcentral/fileexchange/1777-from-double-to-string 使用http://www.mathworks.com/matlabcentral/fileexchange/1777-from-double-to-string上的脚本

the difference between the two numbers is only in the last bit: 两个数字之间的差异仅在最后一位:

octave:1> bc = -0.96928123535654842068964853751822374761104583740234;
octave:2> bm = -0.96928123535654830966734607500256970524787902832031;
octave:3> num2bin(bc)
ans = -.11111000001000101101000010100110011110111001110001011*2^+0
octave:4> num2bin(bm)
ans = -.11111000001000101101000010100110011110111001110001010*2^+0

One of them must be closer to the "correct" answer, assuming the value given for a is exact. 其中一个必须更接近“正确”答案,假设a给出的值是准确的。

>> be = vpa('cos(2.89308776595231886830)',50)                 
be =
-.96928123535654836529707365425580405084360377470583
>> bc = -0.96928123535654842068964853751822374761104583740234;
>> bm = -0.96928123535654830966734607500256970524787902832031;
>> abs(bc-be)                                                 
ans =
.5539257488326242e-16
>> abs(bm-be)                                                 
ans =
.5562972757925323e-16

So, the C library result is more accurate. 因此,C库结果更准确。

For the purposes of your question, however, you should not expect to get the same answer in matlab and whichever C library you linked with. 但是,出于您的问题的目的,您不应期望在matlab和您链接的任何C库中获得相同的答案。

The result is the same up to 15 decimal places, I suspect that is sufficient for almost all applications and if you require more you should probably be implementing your own version of cosine anyway such that you are in control of the specifics and your code is portable across different C compilers. 结果是相同的最多15个小数位,我怀疑几乎所有应用程序都足够了,如果你需要更多,你可能应该实现自己的余弦版本,这样你就可以控制细节,你的代码是可移植的跨越不同的C编译器。

They will differ because they undoubtedly use different methods to calculate the approximation to the result or iterate a different number of times. 它们会有所不同,因为它们无疑使用不同的方法来计算结果的近似值或迭代不同的次数。 As cosine is defined as an infinite series of terms an approximation must be used for its software implementation. 由于余弦定义为无穷大的一系列术语,因此必须使用近似值来实现其软件。 The CORDIC algorithm is one common implementation. CORDIC算法是一种常见的实现方式。

Unfortunately, I don't know the specifics of the implementation in either case, indeed the C one will depend on which C standard library implementation you are using. 不幸的是,在任何一种情况下我都不知道实现的具体细节,实际上C语言将取决于您使用的是哪个C标准库实现。

As others have explained, when you enter that number directly in your source code, not all the fraction digits will be used, as you only get 15/16 decimal places for precision. 正如其他人所解释的那样,当您在源代码中直接输入该数字时,并非所有小数位都将被使用,因为您只能得到15/16小数位数以获得精确度。 In fact, they get converted to the nearest double value in binary (anything beyond the fixed limit of digits is dropped). 实际上,它们被转换为二进制中最接近的double值(超出固定数字限制的任何值都会被删除)。

To make things worse, and according to @R , IEEE 754 tolerates error in the last bit when using the cosine function. 更糟糕的是,根据@R ,IEEE 754在最后一位使用余弦函数时,容忍错误。 I actually ran into this when using different compilers. 我在使用不同的编译器时遇到了这个问题。

To illustrate, I tested with the following MEX file, once compiled with the default LCC compiler, and then using VS2010 (I am on WinXP 32-bit). 为了说明,我使用以下MEX文件测试,一旦使用默认LCC编译器编译,然后使用VS2010(我在WinXP 32位上)。

In one function we directly call the C functions ( mexPrintf is simply a macro #define as printf ). 在一个函数中,我们直接调用C函数( mexPrintf只是一个宏#define作为printf )。 In the other, we call mexEvalString to evaulate stuff in the MATLAB engine (equivalent to using the command prompt in MATLAB). 另一方面,我们调用mexEvalString来评估MATLAB引擎中的东西(相当于在MATLAB中使用命令提示符)。

prec.c prec.c

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include "mex.h"

void c_test()
{
    double a = 2.89308776595231886830L;
    double b = cos(a);

    mexPrintf("[C] a =  %.25Lf (%16Lx)\n", a, a);
    mexPrintf("[C] b = %.25Lf (%16Lx)\n", b, b);
}

void matlab_test()
{
    mexEvalString("a = 2.89308776595231886830;");
    mexEvalString("b = cos(a);");

    mexEvalString("fprintf('[M] a =  %.25f (%bx)\\n', a, a)");
    mexEvalString("fprintf('[M] b = %.25f (%bx)\\n', b, b)");
}

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
    matlab_test();
    c_test();
}

copmiled with LCC 与LCC合作

>> prec
[M] a =  2.8930877659523189000000000 (4007250b32d9c886)
[M] b = -0.9692812353565483100000000 (bfef045a14cf738a)
[C] a =  2.8930877659523189000000000 (        32d9c886)
[C] b = -0.9692812353565484200000000 (        14cf738b)    <---

compiled with VS2010 用VS2010编译

>> prec
[M] a =  2.8930877659523189000000000 (4007250b32d9c886)
[M] b = -0.9692812353565483100000000 (bfef045a14cf738a)
[C] a =  2.8930877659523189000000000 (        32d9c886)
[C] b = -0.9692812353565483100000000 (        14cf738a)    <---

I compile the above using: mex -v -largeArrayDims prec.c , and switch between the backend compilers using: mex -setup 我使用以下命令编译以上内容: mex -v -largeArrayDims prec.c ,并使用以下命令在后端编译器之间切换: mex -setup

Note that I also tried to print the hexadecimal representation of the numbers. 请注意,我还尝试打印数字的十六进制表示。 I only managed to show the lower half of binary double numbers in C (perhaps you can get the other half using some sort of bit manipulations, but I'm not sure how!) 我只设法在C中显示二进制双数的下半部分(也许你可以使用某种位操作得到另一半,但我不确定如何!)

Finally, if you need more precision in you calculations, consider using a library for variable precision arithmetic. 最后,如果在计算中需要更高的精度,请考虑使用库进行变量精度算术。 In MATLAB, if you have access to the Symbolic Math Toolbox , try: 在MATLAB中,如果您可以访问Symbolic Math Toolbox ,请尝试:

>> a = sym('2.89308776595231886830');
>> b = cos(a);
>> vpa(b,25)
ans =
-0.9692812353565483652970737

So you can see that the actual value is somewhere between the two different approximations I got above, and in fact they are all equal up to the 15th decimal place: 所以,你可以看到,实际值是两个不同的逼近我上面得到的,而事实上,他们都是平等的第15位小数之间的某处:

-0.96928123535654831..    # 0xbfef045a14cf738a
-0.96928123535654836..    # <--- actual value (cannot be represented in 64-bit)
-0.96928123535654842..    # 0xbfef045a14cf738b
                 ^
    15th digit --/

UPDATE: 更新:

If you want to correctly display the hexadecimal representation of floating point numbers in C, use this helper function instead (similar to NUM2HEX function in MATLAB): 如果要在C中正确显示浮点数的十六进制表示,请改用此辅助函数(类似于MATLAB中的NUM2HEX函数):

/* you need to adjust for double/float datatypes, big/little endianness */
void num2hex(double x)
{
    unsigned char *p = (unsigned char *) &x;
    int i;
    for(i=sizeof(double)-1; i>=0; i--) {
        printf("%02x", p[i]);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM