[英]MPFR Accuracy and precision problem, binding to pyton with ctypes
我正在尝试使用“fpow”包装器从 python 调用“fpow”C function。 它有效,但不幸的是缺乏精度和准确性。 我想我对mpfr
的使用不正确。 我该如何解决这个问题?
数字.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gmp.h>
#include <mpfr.h>
#include "numeric.h"
char *fpow(char *x, char *y, unsigned long long prec)
{
mpfr_t a, b;
mpfr_init2(a, prec);
mpfr_init2(b, prec);
mpfr_set_str(a, x, 10, 0);
mpfr_set_str(b, y, 10, 0);
mpfr_pow(a, a, b, 1);
long dot;
char *str = malloc(sizeof(char) * (prec + 1));
mpfr_get_str(str, &dot, 10, prec, a, 1);
for(long j = dot; j < dot; j--)
str[j + 1] = str[j];
str[dot] = '.';
mpfr_clear(a);
mpfr_clear(b);
mpfr_free_cache();
return str;
}
基础.py
import ctypes as c
cfunc = c.CDLL('numeric.so')
def fpow(base: str, exponent: str, precision: int) -> str:
'''base to the exponent with the specified precision'''
cfunc.fpow.argtypes = (c.c_char_p, c.c_char_p, c.c_ulonglong)
cfunc.fpow.restype = c.c_char_p
return (
cfunc.fpow(
c.create_string_buffer(base.encode('utf-8')),
c.create_string_buffer(exponent.encode('utf-8')),
precision
)).decode('utf-8')
测试.py
import Numer.basics as nm
print(nm.fpow('5.1', '3', 100))
test.py 的 Output 是“132.509999999999999999999999997382748843093935872477183435247383158639422617852687835693359375000000”
您for
循环有一个错误。 它以j
等于dot
,因此它将执行零次。 并且,它正在递减j
。
C 中的字符串末尾需要一个额外的 0x00 字符来表示“字符串结尾”。
malloc
为prec
+ 1 个额外字符分配空间。 这对于mpfr_pow
output [带有 0x00 EOS 字符串终止符] 已经足够了。
但这并没有为.
我们正在尝试添加。 因此,我们需要增加分配的数量malloc
。
mpfr_pow 的 [编辑] 原始mpfr_pow
是:
132650999999999999999999999999738274884309393587247718343524738315 ...
但是,你的 function 的 output 是:
132.50999999999999999999999999738274884309393587247718343524738315 ...
请注意,您正在将 [first] 6
与.
这是 C 代码的重构版本。 我添加了main
以便能够独立于 python 代码进行测试:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gmp.h>
#include <mpfr.h>
//#include "numeric.h"
char *
fpow(char *x, char *y, unsigned long long prec)
{
mpfr_t a, b;
mpfr_init2(a, prec);
mpfr_init2(b, prec);
mpfr_set_str(a, x, 10, 0);
mpfr_set_str(b, y, 10, 0);
mpfr_pow(a, a, b, 1);
long dot;
// NOTE/BUG: allocates enough space for 0x00 EOS char but _not_ the added
// '.' character
#if 0
char *str = malloc(sizeof(char) * (prec + 1));
#else
char *str = malloc(sizeof(char) * (prec + 1 + 1));
#endif
mpfr_get_str(str, &dot, 10, prec, a, 1);
// original code ...
#if 0
for (long j = dot; j < dot; j--)
str[j + 1] = str[j];
str[dot] = '.';
// refactored code ...
#else
int prev = str[dot];
int next;
char *cur;
for (cur = &str[dot + 1]; *cur != 0; ++cur) {
next = *cur;
*cur = prev;
prev = next;
}
*cur++ = prev;
*cur = 0;
str[dot] = '.';
#endif
mpfr_clear(a);
mpfr_clear(b);
mpfr_free_cache();
return str;
}
int
main(void)
{
char *ret = fpow("5.1","3",100);
printf("%s\n",ret);
return 0;
}
更新:
我有两个问题。 首先是有理由不修复 for 循环错误:
for (long j = dot; j < prec; j--) str[j + 1] = str[j]; str[dot] = '.';
for (long j = dot; j < prec; j--) str[j + 1] = str[j]; str[dot] = '.';
这似乎也可以完成工作。
它对我不起作用。 当然,插入有不同的方法。 我的[有效]只是一个。 随意尝试。 您可以将插入代码隔离到一个单独的 function [并尝试不同的功能]。
第二个问题是,您可能会尝试看到“prec”实际上不起作用,例如,当我输入 prec=20 时,它并没有准确给出 20 位数字。 – arasedeş
那是因为对于mpfr_init2
,第二个参数不是小数位数。 它是位数。
因此,粗略地说,要获得给定的精度位数,我们需要大约 4 倍的位数(例如,要表示数字9
,我们需要 4 位)。
所以,改变:
mpfr_init2(a, prec);
mpfr_init2(b, prec);
进入:
mpfr_init2(a, prec * 4);
mpfr_init2(b, prec * 4);
请注意,我们可以准确计算所需的精度,但我认为在给定的用例中这太过分了。
修改后的 output 为:
132.65099999999999999999999999999999999999999999999999999999999999 ...
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.