R: Как преобразовать длинное число в строку, чтобы сохранить точность - PullRequest
0 голосов
/ 14 февраля 2019

У меня проблема с преобразованием длинного числа в строку в 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 невозможно получить действительное значение длинного числа, переданного в функцию, потому что 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, и это решает мою проблему.

Поэтому правильный ответ на мой вопрос:

«« Я полагаю, toString () преобразует число в число с плавающей точкой », нет, вы сделали это сами (даже если непреднамеренно).» - Нет, R сделал это сам, именно так R читает числа.

Поэтому я отметил ответ r2evans как лучший ответ, потому что этот пользователь помог мне найти правильное решение.Спасибо!

1 Ответ

0 голосов
/ 14 февраля 2019

В начале строки вы должны (в данном случае) прочитать ваши большие числа в виде строки перед преобразованием в 64-разрядные целые числа:

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

Некоторые замечания о том, что вы пробовали:

  • «Я полагаю, toString () преобразует число в число с плавающей точкой» Нет, вы сделали это самостоятельно (даже если непреднамеренно).В 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 сначала нужно разобрать и «понять» все внутри скобок, прежде чем это можно будет передать функции.(Обычно это вещь для разбора компилятора / языка, а не просто для R.) В этом случае он видит, что это (большой) отрицательный float, поэтому он создает класс numeric (float).Только , затем отправляет эту numeric в функцию, но к этому моменту точность уже потеряна.Ergo, иначе нелогичный

    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, полезно для преобразования различных форматов / классов в 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
    
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...