Как уже объясняли другие, когда вы вводите это число непосредственно в исходном коде, будут использоваться не все цифры дроби, поскольку вы получите только 15/16 десятичных знаков для точности. Фактически они преобразуются в двоичное значение с ближайшим двойным значением (все, что находится за пределами фиксированного предела цифр, отбрасывается).
Что еще хуже, и согласно @ R , IEEE 754 допускает ошибку в последнем бите при использовании функции косинуса. Я действительно столкнулся с этим при использовании разных компиляторов.
Чтобы проиллюстрировать это, я протестировал следующий MEX-файл, один раз скомпилированный с компилятором LCC по умолчанию, а затем с использованием VS2010 (я использую 32-битную версию WinXP).
В одной функции мы напрямую вызываем функции C (mexPrintf
- это просто макрос #define
как printf
). В другом случае мы вызываем mexEvalString
для проверки содержимого механизма MATLAB (эквивалентно использованию командной строки в MATLAB).
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();
}
соединено с LCC
>> prec
[M] a = 2.8930877659523189000000000 (4007250b32d9c886)
[M] b = -0.9692812353565483100000000 (bfef045a14cf738a)
[C] a = 2.8930877659523189000000000 ( 32d9c886)
[C] b = -0.9692812353565484200000000 ( 14cf738b) <---
скомпилировано с VS2010
>> prec
[M] a = 2.8930877659523189000000000 (4007250b32d9c886)
[M] b = -0.9692812353565483100000000 (bfef045a14cf738a)
[C] a = 2.8930877659523189000000000 ( 32d9c886)
[C] b = -0.9692812353565483100000000 ( 14cf738a) <---
Я компилирую вышеупомянутое, используя: mex -v -largeArrayDims prec.c
, и переключаюсь между внутренними компиляторами, используя: mex -setup
Обратите внимание, что я также пытался напечатать шестнадцатеричное представление чисел. Мне удалось показать только нижнюю половину двоичных двойных чисел в C (возможно, вы можете получить другую половину, используя какие-то битовые манипуляции, но я не уверен, как!)
Наконец, если вам нужна большая точность в ваших вычислениях, рассмотрите возможность использования библиотеки для арифметики с переменной точностью. В MATLAB, если у вас есть доступ к Symbolic Math Toolbox , попробуйте:
>> a = sym('2.89308776595231886830');
>> b = cos(a);
>> vpa(b,25)
ans =
-0.9692812353565483652970737
Итак, вы можете видеть, что фактическое значение находится где-то между двумя различными приближениями, которые я получил выше, и на самом деле все они равны до 15-го знака после запятой:
-0.96928123535654831.. # 0xbfef045a14cf738a
-0.96928123535654836.. # <--- actual value (cannot be represented in 64-bit)
-0.96928123535654842.. # 0xbfef045a14cf738b
^
15th digit --/
UPDATE:
Если вы хотите правильно отобразить шестнадцатеричное представление чисел с плавающей запятой в C, используйте вместо этого эту вспомогательную функцию (аналогично NUM2HEX функции в MATLAB):
/* 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]);
}
}