我不确定这是不是一个错误 . 但我一直在玩 big
,我无法理解为什么这段代码会这样运作:
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 回答
在GMP中(它适用于所有语言而不仅仅是Crystal),整数(C
mpz_t
,CrystalBigInt
)和浮点数(Cmpf_t
,CrystalBigFloat
)具有单独的默认精度 .另请注意,使用显式精度优于设置默认精度,因为默认精度可能不是可重入的(它取决于
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
产生了缺失的数字 .