使用 NativeCall 调用 C fn `erf` 比 C 中的 `erf` 获得更精确的输出

Using NativeCall to call the C fn `erf` gets more precise output than `erf` in C

我写了一个 Raku 脚本来调用 C 标准库中的 erf 函数:

use NativeCall;
sub erf(num64) returns num64 is native { * };

say [0.5,1,2,3,4,-0.9].map: {erf($_.Num)};

这个脚本的输出

(0.5204998778130465 0.8427007929497149 0.9953222650189527 0.9999779095030014 0.9999999845827421 -0.7969082124228322)

与 C 的所有值 [0.5,1,2,3,4,-0.9] 的输出匹配,除了 4.

对于 4 C 输出 1.000000 而 Raku 给出 0.9999999845827421.

要在 C 中测试 4 的输出,运行 此代码:

#include <stdio.h> // Including header file for printf function
#include <math.h>  // Including header file for erf function

int main (){

  double param, result;
  param = 4.0;
  result = erf(param);
  printf("erf (%f) = %f\n", param, result);
  return 0;
}

知道发生了什么事吗?我也需要从 Raku 输出 1.0

对于 C 代码,将 %f 更改为 %.99g 以显示更多数字。这揭示了 erf(4) returns 0.9999999845827420852373279558378271758556365966796875.

%f要求小数点后六位。该值四舍五入以适合该格式。 %.<i>number</i>f 请求小数点后 <i>number</i> 位数点并始终使用“固定”格式。 %.<i>number</i>g 请求 <i>number</i> 有效数字并使用一个“通用”格式,在适当的时候切换到指数表示法。

对于Raku代码,如果你想输出“1.0”或“1.000000”,你需要对输出应用一些格式化请求。我不练习 Raku,但简短的搜索显示 Raku 具有可以使用的 printf-like 功能,因此请求 %f 格式应该复制 C 输出。

你是在比较苹果和橙子。

use NativeCall;
sub erf(num64) returns num64 is native { * };

say .fmt("%f") for [0.5,1,2,3,4,-0.9].map: {erf($_.Num)}

0.520500
0.842701
0.995322
0.999978
1.000000
-0.796908

你在 C 中使用 printf,如果你使用 .fmt(在 Raku 中更容易说 sprintf),那么你也会得到 1.0.

另一个基于@Eric 关于精度的解释的版本:

my $fmt = '%.6f';
printf [$fmt ~ ' '] x 5 ~ $fmt ~ "\n", [0.5,1,2,3,4,-0.9].map: {erf($_.Num)};

[$fmt ~ ' '] x 5 ~ $fmt ~ "\n" 构建格式字符串 "%.6f %.6f %.6f %.6f %.6f %.6f\n"

输出

0.520500 0.842701 0.995322 0.999978 1.000000 -0.796908

甚至更多 as proposed by raiph, using the infix operator xx:

printf "{'%.6f' xx 6} \n", [0.5,1,2,3,4,-0.9].map: {erf($_.Num)};