为什么BigFloat.to_s不够精确?

时间:2017-10-12 13:40:20

标签: gmp crystal-lang

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

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

代码

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}"

输出

BigInt: 1237940039285380274899124224
BigFloat: 1237940039285380274900000000
BigInt from BigFloat: 1237940039285380274899124224

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

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

输出

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 :(得分:0)

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

此外,请注意,使用显式精度比设置默认精度更好,因为默认精度可能不会重新输入(取决于configure时间切换)。另外,如果某人仅读取您的代码的一部分,则他们可能会跳过设置默认精度的那一部分,并认为是错误的代码。尽管我不太了解Crystal绑定,但是我认为这种功能在某个地方公开。

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

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

  • 设置全局默认精度。尽管此方法可行,但将需要经常更改默认精度,或者在整个程序中使用默认精度。两种方法中,默认精度的方法都是一种拖延形式,以后会有所报复。
  • 基于变量设置精度。这是比前一种更好的解决方案。尽管它需要更多的代码(每个变量初始化多出1-2行),但稍后会收回。例如,在空间物体跟踪系统中,物理计算必须非常精确,但是其他系统可能会使用较低的精度数字来节省速度和内存。

我仍然不确定是什么导致转换BigFloat --> BigInt产生缺失的数字。