GMP - mpf_cmp_si 对于负值无法正常工作

GMP - mpf_cmp_si not working correctly for negative values

我正在通过 Rust 使用 GNU 多精度库,我正在尝试为 mpf_sqrt() 函数编写一个包装器。 为此,我需要确保数字为正数,但 mpf_cmp_si() 不正常。

编辑:新示例

extern crate libc;
use libc::{c_double, c_int, c_long, c_ulong, c_void,c_char};
use std::mem::uninitialized;

type mp_limb_t = usize; // TODO: Find a way to use __gmp_bits_per_limb instead.
type mp_bitcnt_t = c_ulong;
type mp_exp_t = c_long;

#[link(name = "gmp")]
extern "C" {
    fn __gmpf_init2(x: mpf_ptr, prec: mp_bitcnt_t);
    fn __gmpf_set_si(rop: mpf_ptr,op: c_int);
    fn __gmpf_cmp_si(op1: mpf_srcptr, op2: c_long) -> c_int;
}

#[repr(C)]
pub struct mpf_struct {
    _mp_prec: c_int,
    _mp_size: c_int,
    _mp_exp: mp_exp_t,
    _mp_d: *mut c_void
}

pub type mpf_srcptr = *const mpf_struct;
pub type mpf_ptr = *mut mpf_struct;

fn main() {
    let mut ten:mpf_struct;
    unsafe{
        ten = uninitialized();
        __gmpf_init2(&mut ten,512);
        __gmpf_set_si(&mut ten,10);
    }
    let mut minus_ten:mpf_struct;
    unsafe{
        minus_ten = uninitialized();
        __gmpf_init2(&mut minus_ten,512);
        __gmpf_set_si(&mut minus_ten,-10);
    }


    // compare things
    unsafe{
        println!("Result of comparison of -10 (mpf) and 10 (signed int) = {}",
            __gmpf_cmp_si(&minus_ten,10));
        println!("Result of comparison of -10 (mpf) and 0 (signed int) = {}",
            __gmpf_cmp_si(&minus_ten,0));
        println!("Result of comparison of 10 (mpf) and 0 (signed int) = {}",
            __gmpf_cmp_si(&ten,0));
    }


}

这个returns:

Running `target/debug/so_test`
Result of comparison of -10 (mpf) and 10 (signed int) = 1
Result of comparison of -10 (mpf) and 0 (signed int) = 1
Result of comparison of 10 (mpf) and 0 (signed int) = 1

根据文档,这是行为:

Function: int mpf_cmp_si (const mpf_t op1, signed long int op2)
Compare op1 and op2. Return a positive value if op1 > op2, zero if op1 = op2, and a negative value if op1 < op2.

我是 运行 rust 1.4.0,x64 上的 GMP 6.1.0-1 Linux

旧代码:

    pub fn sqrt(self) -> Mpf {
    let mut retval:Mpf;
    unsafe {
        retval = Mpf::new(__gmpf_get_prec(&self.mpf) as usize);
        retval.set_from_si(0);
        if __gmpf_cmp_ui(&self.mpf,0) > 0 {
            __gmpf_sqrt(&mut retval.mpf,&self.mpf);
        } else {
            panic!("Square root of negative/zero");
        }
    }
    retval
}

the mpf struct is defined like this:

#[repr(C)]
pub struct mpf_struct {
    _mp_prec: c_int,
    _mp_size: c_int,
    _mp_exp: mp_exp_t,
    _mp_d: *mut c_void
} and the function from gmp is imported like this:

#[link(name = "gmp")]
extern "C" {
    fn __gmpf_cmp_si(op1: mpf_srcptr, op2: c_long) -> c_int;

}

The problem I'm having is that mpf_cmp_si (which is exposed to Rust as __gmpf_cmp_si) doesn't return negative when it should.

This function should return negative if the value of my mpf is less than 0. But it doesn't so the function divides by zero and crashes (an "unknown error", not because of the panic!() call)

__gmpf_set_si 的签名不正确。 C定义为:

void mpf_set_si (mpf_t rop, signed long int op)

因此 Rust FFI 声明应该使用 c_long,而不是 c_int:

fn __gmpf_set_si(rop: mpf_ptr,op: c_long);

进行此更改会使输出(加上一些额外的打印)如下:

Result of comparison of -10 (mpf) and -10 (signed int) = 0
Result of comparison of -10 (mpf) and 0 (signed int) = -1
Result of comparison of -10 (mpf) and 10 (signed int) = -1
Result of comparison of 10 (mpf) and -10 (signed int) = 1
Result of comparison of 10 (mpf) and 0 (signed int) = 1
Result of comparison of 10 (mpf) and 10 (signed int) = 0

(注意。添加与 -10/10 的比较是我深入了解的方式:与 -10 相比,-10 失败了。)

问题是 intlong 不一定相同:在 64 位平台上,它们通常分别是 32 位和 64 位。在任何一种情况下,参数都在相同的(64 位)寄存器中传递,但类型不匹配意味着寄存器仅使用 32 位 -10 初始化,这与 64 位寄存器有很大不同。每个的位模式是:

0000000000000000000000000000000011111111111111111111111111110110
1111111111111111111111111111111111111111111111111111111111110110

当解释为带符号的 64 位整数时(就像 gmpf_set_si 在内部所做的那样),第一个是 232 - 10 = 4294967286,这正是minus_ten 初始化为:

extern crate libc;
use libc::{c_double, c_int, c_long, c_ulong, c_void,c_char};
use std::mem::uninitialized;

type mp_limb_t = usize; // TODO: Find a way to use __gmp_bits_per_limb instead.
type mp_bitcnt_t = c_ulong;
type mp_exp_t = c_long;

#[link(name = "gmp")]
extern "C" {
    fn __gmpf_init2(x: mpf_ptr, prec: mp_bitcnt_t);
    fn __gmpf_set_si(rop: mpf_ptr,op: c_int);
    fn __gmp_printf(x: *const c_char, ...);
}

#[repr(C)]
pub struct mpf_struct {
    _mp_prec: c_int,
    _mp_size: c_int,
    _mp_exp: mp_exp_t,
    _mp_d: *mut c_void
}
pub type mpf_ptr = *mut mpf_struct;


fn main() {
    unsafe{
        let mut ten = uninitialized();
        __gmpf_init2(&mut ten,512);
        __gmpf_set_si(&mut ten,10);

        let mut minus_ten = uninitialized();
        __gmpf_init2(&mut minus_ten,512);
        __gmpf_set_si(&mut minus_ten,-10);

        __gmp_printf(b"10 == %Ff\n-10 == %Ff\n".as_ptr() as *const c_char,
                     &ten, &minus_ten);
    }
}

输出:

10 == 10.000000
-10 == 4294967286.000000

最后一点,rust-bindgen 是避免机械转录错误的好工具,如下所示:它将根据 C header.

生成正确的内容