双向PyFloat转换是不正确的

时间:2012-07-09 13:03:24

标签: python c swig

我正在学习SWIG,因为在Python中使用C语言。我写过这个函数,但是我无法理解,为什么包裹的myfunc返回错误的float / double值:

mfuncs.c

#include <stdlib.h>

float myfunc(int n) {
    float result;
    result = 100 / n;
    return result;
}

mfuncs.i

%module mfuncs
%typemap(out) double, float "$result = PyFloat_FromDouble($1);"

extern float myfunc(int n);

最后我得到1107558400.0而不是33.33333。

>>> import mfuncs
>>> mfuncs.myfunc(3)
1107558400.0
>>> 

错误在哪里?

2 个答案:

答案 0 :(得分:4)

SWIG类型图是不必要的 - 默认情况下提供它,您只需要为“深奥”类型编写类型图,并且提供的默认double / float类型就可以了。

这里真正的问题是你没有启用警告编译或忽略它们! 使用“-Wall -Wextra”编译习惯或者编译器需要启用最大警告和注意它们的任何内容都是值得的。

您的SWIG界面仅告诉 SWIG 有关函数myfunc但该界面中没有任何内容可以使您用于编译生成的myfuncs_wrap.c的编译器可以使用该声明。这意味着当您来编译共享库时,您依赖于myfunc的隐式声明。我的机器上的GCC -Wall报告了这个:

  

test_wrap.c:3139:3:警告:隐式声明函数'myfunc'

隐式声明假定它返回int。如果没有声明,这只是C中的规则,就像你写的那样:

#include <stdlib.h>

int myfunc(int n);

int main() {
  printf("%d\n", myfunc(3));   
  return 0;
}

这显然是错误的(确切地说是未定义的行为),因为myfunc的定义会返回float。你的实现(合法地)选择为这个未定义的行为做最简单的事情,这从intfloat大致是一点点演员。 (同样可以做任何,即使每次运行都有不同的东西 - 这就是未定义行为的美妙之处。)

您可以通过将其更改为:

来修复SWIG界面
%module mfuncs
%{
extern float myfunc(int n);
%}

extern float myfunc(int n);

这是有效的,因为%{%}之间的代码直接传递给生成的包装器,这使得编译器在构建包装器时能够识别myfunc的真实声明。

在我的视图中有一个更好的解决方案:只在头文件中提供一次声明,然后你的接口文件变为:

%module mfuncs
%{
#include "myfunc.h"
%}

%include "myfunc.h"

(显然是myfunc.c中的#include "myfunc.h")。通过这种方式,您只需编写一次声明,如果有任何不太令人期待的事情,编译器将发出警告/错误,而不是仅仅采取(通常是错误的)最佳猜测。

答案 1 :(得分:1)

此代码中有两个错误阻止myfunc(3)返回您期望的33.3333。 Flexo奇妙地解释了包装问题。另一个问题是这一行

    result = 100 / n;

100是一个int,n是一个int,除法的结果将是一个int,然后转换为float。所以myfunc(3) == 33。 Python 2的行为方式相同,它在Python 3中已被更改,因此两个int的划分会导致浮动。

只需将该行更改为

即可
    result = 100.0 / n;

或使n浮动/加倍。