繁体   English   中英

MPFR 准确度和精度问题,用ctypes绑定到pyton

[英]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 字符来表示“字符串结尾”。

mallocprec + 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM