R:如何将长数字转换为字符串以节省精度

R: How to convert long number to string to save precision

我在 R 中将长数字转换为字符串时遇到问题。如何轻松地将数字转换为字符串以保持精度?下面有一个简单的例子。

a = -8664354335142704128
toString(a)

[1] "-8664354335142704128"

b = -8664354335142703762
toString(b)

[1] "-8664354335142704128"

a == b

[1] TRUE

我预计 toString(a) == toString(b),但我得到了不同的值。我想 toString() 在转换为字符串之前将数字转换为浮点数或类似的东西。

感谢您的帮助。

编辑:

> -8664354335142704128 == -8664354335142703762

[1] TRUE

> along = bit64::as.integer64(-8664354335142704128)
> blong = bit64::as.integer64(-8664354335142703762)
> along == blong

[1] TRUE

> blong

integer64
[1] -8664354335142704128

我也试过:

> as.character(blong)

[1] "-8664354335142704128"

> sprintf("%f", -8664354335142703762)

[1] "-8664354335142704128.000000"

> sprintf("%f", blong)

[1] "-0.000000"

编辑 2:

我的第一个问题是,是否可以将长数字无损地转换为字符串。然后我意识到,在 R 中不可能获得传递给函数的 long 数字的真实值,因为 R 会自动读取带损失的值。

比如我有函数:

> my_function <- function(long_number){
+ string_number <- toString(long_number)
+ print(string_number)
+ }

如果有人使用它并传递了一个很长的数字,我无法获得准确传递了哪个数字的信息。

> my_function(-8664354335142703762)
[1] "-8664354335142704128"

例如,如果我从文件中读取一些数字,这很容易。但这不是我的情况。我只需要使用一些用户通过的东西。

我不是 R 专家,所以我很好奇为什么它在另一种语言中有效而在 R 中无效。例如 Python:

>>> def my_function(long_number):
...     string_number = str(long_number)
...     print(string_number)
... 
>>> my_function(-8664354335142703762)
-8664354335142703762

现在我知道了,问题是 R 如何读取和存储数字。每种语言都有不同的做法。我必须改变将数字传递给 R 函数的方式,它解决了我的问题。

所以我的问题的正确答案是:

""I suppose toString() converts the number to float", 不,你自己做的(即使是无意的)。" - 不,R 自己做的,这就是 R 的读法数字。

所以我将 r2evans 的答案标记为最佳答案,因为该用户帮助我找到了正确的解决方案。谢谢!

前面的底线,您必须(在这种情况下)在转换为 64 位整数之前将大数字作为字符串读入:

bit64::as.integer64("-8664354335142704128") == bit64::as.integer64("-8664354335142703762")
# [1] FALSE

关于您尝试过的一些要点:

  • "I suppose toString() converts the number to float",不,是你自己做的(即使是无意的)。在 R 中,创建数字时,5 是一个浮点数,5L 是一个整数。即使您尝试将其创建为整数,它也会抱怨并失去精度:

    class(5)
    # [1] "numeric"
    class(5L)
    # [1] "integer"
    class(-8664354335142703762)
    # [1] "numeric"
    class(-8664354335142703762L)
    # Warning: non-integer value 8664354335142703762L qualified with L; using numeric value
    # [1] "numeric"
    
  • 更恰当地说,当您将其作为数字输入并然后尝试转换它时,R 首先处理括号内的内容。也就是说,用

    bit64::as.integer64(-8664354335142704128)
    

    R 首先必须解析和 "understand" 括号内的所有内容,然后才能将其传递给函数。 (这通常是一个 compiler/language-parsing 的东西,而不仅仅是一个 R 的东西。)在这种情况下,它看到它似乎是一个(大)负浮点数,所以它创建了一个 class numeric (漂浮)。只有 then 才会将此 numeric 发送给函数,但此时精度已经丢失。因此,否则不合逻辑

    bit64::as.integer64(-8664354335142704128) == bit64::as.integer64(-8664354335142703762)
    # [1] TRUE
    

    在这种情况下,*碰巧该数字的 64 位版本等于您想要的。

    bit64::as.integer64(-8664254335142704128)  # ends in 4128
    # integer64
    # [1] -8664254335142704128                 # ends in 4128, yay! (coincidence?)
    

    如果你减去一个,结果是相同的有效 integer64:

    bit64::as.integer64(-8664354335142704127)  # ends in 4127
    # integer64
    # [1] -8664354335142704128                 # ends in 4128 ?
    

    这样持续了很长一段时间,直到它最终转移到下一个圆点

    bit64::as.integer64(-8664254335142703617)
    # integer64
    # [1] -8664254335142704128
    bit64::as.integer64(-8664254335142703616)
    # integer64
    # [1] -8664254335142703104
    

    差异为 1024 或 2^10 不太可能是巧合。我还没有钓鱼,但我猜这对于 32 位领域的浮点精度有一些意义。

  • 幸运的是,bit64::as.integer64 有几个 S3 方法,可用于将不同的 formats/classes 转换为 integer64

    library(bit64)
    methods(as.integer64)
    # [1] as.integer64.character as.integer64.double    as.integer64.factor   
    # [4] as.integer64.integer   as.integer64.integer64 as.integer64.logical  
    # [7] as.integer64.NULL     
    

    因此,bit64::as.integer64.character 可能很有用,因为当您键入或将其作为字符串读入时,精度 不会 丢失:

    bit64::as.integer64("-8664354335142704128")
    # integer64
    # [1] -8664354335142704128
    bit64::as.integer64("-8664354335142704128") == bit64::as.integer64("-8664354335142703762")
    # [1] FALSE
    
  • 仅供参考,您的数字已经接近 64 位边界:

    -.Machine$integer.max
    # [1] -2147483647
    -(2^31-1)
    # [1] -2147483647
    log(8664354335142704128, 2)
    # [1] 62.9098
    -2^63 # the approximate +/- range of 64-bit integers
    # [1] -9.223372e+18
    -8664354335142704128
    # [1] -8.664354e+18