Почему я получаю то же значение от sin (x) и sinf (x), что и в (8) и (9) ниже? Почему я получаю разные значения [x — sinf (x)] из двух разных реализаций (2) и (3) ниже? Почему (6) дает тот же результат, что и (5)?
Я использую g ++ (Ubuntu / Linaro 4.6.3-1ubuntu5) 4.6.3. Я использовал -O0, чтобы отключить оптимизацию.
Предыстория: я обнаружил ошибку в моей программе, где я должен использовать float по всей программе, потому что она будет перенесена во встроенную систему. Тем не менее, в настоящее время я отлаживаю Ubuntu на своем компьютере только потому, что это удобно. Я обнаружил, что операция типа (x-s) вызывала неточность, когда x был маленьким. Это заставило меня думать, что это должно быть связано с потерей значащих цифр в результате катастрофической отмены. Однако, когда я заменил переменную s на sinf (x), проблема неточности не возникла (как видно из (2) против (3)). Я мог бы предположить, что sinf () может быть реализован так же, как sin (). Если так, то почему явное приведение к float не имеет эффекта, как в (4) и (5). Теперь я озадачен.
int main()
{
unsigned long int xx(0x3d65c2f2);
float x(*reinterpret_cast<float*>(&xx));
float s(sinf(x));
printf("( 1) x = %.10e\n", x);
printf("( 2) x - s = %.10e\n", x-s);
printf("( 3) x - sinf(x) = %.10e\n", x-sinf(x)); // Why is it different from (2)?
printf("( 4) x - float(sinf(x)) = %.10e\n", x-float(sinf(x))); // Compare with (3). Why casting has no effect?
printf("( 5) float(x) - float(sinf(x)) = %.10e\n", float(x)-float(sinf(x))); // Compare with (3). Why casting has no effect?
printf("( 6) x - sin(x) = %.10e\n", x - sin(x));
printf("( 7) s = %.10e\n", s);
printf("( 8) sinf(x) = %.10e\n", sinf(x));
printf("( 9) sin(x) = %.10e\n", sin(x)); // Compare with (8). Is sinf() identical to sin()?
printf("(10) float(sinf(x)) = %.10e\n", float(sinf(x))); // Compare with (8). Why casting has no effect?
double s_df(sinf(x));
double s_dd(sin(x));
float s_fd(sin(x));
float s_ff(sinf(x));
printf("(20) s_df = %.10e\n", s_df);
printf("(21) s_dd = %.10e\n", s_dd); // Compare with (20). Is sinf() identical to sin()?
printf("(22) s_fd = %.10e\n", s_fd);
printf("(23) s_ff = %.10e\n", s_ff);
return 0;
}
Вот вывод:
$ make && ./main
g++ main.cc -Wall -c -o main.o -O0
g++ -o main main.o
( 1) x = 5.6094117463e-02
( 2) x - s = 2.9411166906e-05
( 3) x - sinf(x) = 2.9412529899e-05
( 4) x - float(sinf(x)) = 2.9412529899e-05
( 5) float(x) - float(sinf(x)) = 2.9412529899e-05
( 6) x - sin(x) = 2.9412529899e-05
( 7) s = 5.6064706296e-02
( 8) sinf(x) = 5.6064704933e-02
( 9) sin(x) = 5.6064704933e-02
(10) float(sinf(x)) = 5.6064704933e-02
(20) s_df = 5.6064704933e-02
(21) s_dd = 5.6064704933e-02
(22) s_fd = 5.6064706296e-02
(23) s_ff = 5.6064706296e-02
В C ++ sin
имеет перегрузку float sin(float f)
, И разрешение перегрузки делается на типе аргумента, а не на типе возврата. Чтобы заставить использование double sin(double d)
вам нужно привести аргумент: sin(static_cast<double>(x))
,
(2) против (3): стандарт FP позволяет реализациям хранить промежуточные результаты с большей точностью, чем конечный результат. Так что ценность s
не должны быть точно такими же, как промежуточный результат для sin(f)
в (3).
Многое из этого зависит от вашего компилятора, настроек компилятора и аппаратного обеспечения. Например, если я запускаю ваш код в моей системе, я получаю:
( 1) x = 5.6094117463e-02
( 2) x - s = 2.9411166906e-05
( 3) x - sinf(x) = 2.9411166906e-05
( 4) x - float(sinf(x)) = 2.9411166906e-05
( 5) float(x) - float(sinf(x)) = 2.9411166906e-05
( 6) x - sin(x) = 2.9412529899e-05
( 7) s = 5.6064706296e-02
( 8) sinf(x) = 5.6064706296e-02
( 9) sin(x) = 5.6064704933e-02
(10) float(sinf(x)) = 5.6064706296e-02
(20) s_df = 5.6064706296e-02
(21) s_dd = 5.6064704933e-02
(22) s_fd = 5.6064706296e-02
(23) s_ff = 5.6064706296e-02