首页 文章

为什么BigFloat.to_s不够精确?

提问于
浏览
7

我不确定这是不是一个错误 . 但我一直在玩 big ,我无法理解为什么这段代码会这样运作:

https://carc.in/#/r/2w96

Code

require "big"

x = BigInt.new(1<<30) * (1<<30) * (1<<30)
puts "BigInt: #{x}"

x = BigFloat.new(1<<30) * (1<<30) * (1<<30) 
puts "BigFloat: #{x}"
puts "BigInt from BigFloat: #{x.to_big_i}"

Output

BigInt: 1237940039285380274899124224
BigFloat: 1237940039285380274900000000
BigInt from BigFloat: 1237940039285380274899124224

首先,我认为BigFloat需要更改 BigFloat.default_precision 才能使用更大的数字 . 但是从这段代码看起来它只在尝试输出 #to_s 值时才有意义 .

与BigFloat的精度设置为1024(https://carc.in/#/r/2w98)相同:

Output

BigInt: 1237940039285380274899124224
BigFloat: 1237940039285380274899124224
BigInt from BigFloat: 1237940039285380274899124224

BigFloat.to_s 使用 LibGMP.mpf_get_str(nil, out expptr, 10, 0, self) . GMP在哪里说:

mpf_get_str(char * str,mp_exp_t * expptr,int base,size_t n_digits,const mpf_t op)将op转换为基数中的数字字符串 . 基本参数可以从2到62或从-2到-36不等 . 最多将生成n_digits数字 . 不返回尾随零 . 生成的数字不能超过op准确表示的数字 . 如果n_digits为0,则生成准确的最大位数 .

谢谢 .

1 回答

  • 0

    在GMP中(它适用于所有语言而不仅仅是Crystal),整数(C mpz_t ,Crystal BigInt )和浮点数(C mpf_t ,Crystal BigFloat )具有单独的默认精度 .

    另请注意,使用显式精度优于设置默认精度,因为默认精度可能不是可重入的(它取决于 configure -time开关) . 此外,如果有人只读取您代码的一部分,他们可能会跳过该部分并设置默认精度并假设错误 . 虽然我不太了解Crystal绑定,但我认为这样的功能在某处暴露 .

    传递给 mpf_get_str 的零参数意味着从精度中猜测值 . 我知道有效位数是成比例的,接近 precision / log2(10) . 浮点数具有有限的精度 . 在这种情况下,不是 mpf_get_str 调用使最后的数字为零 - 这是内部表示没有保留这些数据 . 看起来您的(默认)精度太小,无法存储所有必要的数字 .

    总而言之,有两种解决方案:

    • Set a global default precision. 虽然这种方法可行,但需要经常更改默认精度,或者在整个程序中使用一个 . 两种方式,具有默认精度的方法是拖延的一种形式,它将在以后报复 .

    • Set a precision on variable basis. 这是比前者更好的解决方案 . 虽然它需要更多的代码(每个变量初始化1-2行),但它将在稍后回收 . 例如,在空间物体跟踪系统中,物理计算必须是超精确的,但是其他系统可以使用较低精度的数字来节省速度和内存 .

    我仍然不确定是什么使转换 BigFloat --> BigInt 产生了缺失的数字 .

相关问题