Каков общепринятый способ отправки 64-битных значений через JSON?

Некоторые из моих данных представляют собой 64-битные целые числа. Я хотел бы отправить их в программу JavaScript, работающую на странице.

Однако, насколько я могу судить, целые числа в большинстве реализаций JavaScript представляют собой 32-битные величины со знаком.

Мои два варианта кажутся:

  1. Отправить значения в виде строк
  2. Отправлять значения в виде 64-битных чисел с плавающей запятой

Вариант (1) не идеален, но вариант (2) кажется гораздо менее совершенным (потеря данных).

Как вы справились с этой ситуацией?


person Frank Krueger    schedule 16.10.2008    source источник
comment
горе программистам, которые попали на эту страницу!   -  person duhaime    schedule 06.04.2020


Ответы (6)


Кажется, это не столько проблема с JSON, сколько проблема с самим Javascript. Что вы планируете делать с этими цифрами? Если это просто волшебный токен, который вам нужно передать обратно на веб-сайт позже, во что бы то ни стало, просто используйте строку, содержащую значение. Если вам действительно нужно выполнять арифметические действия со значением, вы могли бы написать свои собственные подпрограммы Javascript для 64-битной арифметики.

Одним из способов представления значений в Javascript (и, следовательно, JSON) было бы разделение чисел на два 32-битных значения, например.

  [ 12345678, 12345678 ]

Чтобы разделить 64-битное значение на два 32-битных значения, сделайте что-то вроде этого:

  output_values[0] = (input_value >> 32) & 0xffffffff;
  output_values[1] = input_value & 0xffffffff;

Затем, чтобы объединить два 32-битных значения в 64-битное значение:

  input_value = ((int64_t) output_values[0]) << 32) | output_values[1];
person Simon Howard    schedule 16.10.2008
comment
Когда я пытаюсь ввести input_value ›› 32, я просто получаю обратно input_value, как будто 32 бита просто полностью обтекаются. ››› имеет тот же эффект. Тем не менее, когда я пытаюсь использовать Math.floor(input_value / 4294967296) (4294967296 = Math.pow(2, 32)), я получаю старшую 32-битную часть input_value; Что мне не хватает? - person snapfractalpop; 04.07.2012
comment
Вы не говорите, какой язык вы используете, поэтому я предполагаю, что вы используете Java. Является ли 'input_value' длинным (т.е. 64-битным целым числом)? Если это int (32-битное целое число), то сдвиг битов влево на 32 бита не сработает. - person Simon Howard; 05.07.2012
comment
извините за это .. Я должен был быть явным. Я использую javascript (в nodejs) и был действительно удивлен, увидев, что битовый сдвиг на 32 бита вправо каким-то образом дал мне то же число. Я не совсем понимаю все неявные преобразования типов в javascript и их основные представления. - person snapfractalpop; 06.07.2012
comment
Это по-прежнему является проблемой JSON, а не проблемой Javascript, поскольку JSON не поддерживает целые числа с более чем 9 десятичными цифрами. Если бы это было так, реализация Javascript была бы вынуждена обеспечивать его поддержку каким-то образом под капотом. - person Lightness Races in Orbit; 22.09.2012
comment
@LightnessRacesinOrbit, я не могу найти ссылку на оператор с 9 десятичными цифрами. Это в какой-то спецификации? - person akhaku; 30.03.2013
comment
@akhaku: Это версия спецификации (RFC 4627 и www.json.org), которую я придумал в своей голове, когда неверно истолковал грамматику. :) - person Lightness Races in Orbit; 30.03.2013

На самом деле существует ограничение на уровне точности JavaScript/ECMAScript до 53 бит для целых чисел (они хранятся в мантиссе двойного буфера памяти размером 8 байт). Таким образом, передача больших чисел в формате JSON не будет десериализована, как ожидается клиентом JavaScript, который усекает их до 53-битного разрешения.

> parseInt("10765432100123456789")
10765432100123458000

См. Number.MAX_SAFE_INTEGER константа и Number.isSafeInteger() функция:

Константа MAX_SAFE_INTEGER имеет значение 9007199254740991. Причина этого числа заключается в том, что JavaScript использует числа формата с плавающей запятой двойной точности, как указано в IEEE 754, и может безопасно представлять только числа между -(2^53 - 1) и 2^53 - 1.

Безопасный в этом контексте относится к способности точно представлять целые числа и правильно их сравнивать. Например, Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2 будет равно true, что математически неверно. См. Number.isSafeInteger() для получения дополнительной информации.

Из-за разрешения поплавков в JavaScript использование 64-битных чисел с плавающей запятой, как вы предложили, будет страдать от того же ограничения.

ИМХО лучший вариант - передавать такие значения как текст. Это будет по-прежнему прекрасно читаемый контент JSON, и с ним будет легко работать на уровне JavaScript.

Чистое строковое представление — это то, что задает OData для своих типов Edm.Int64 или Edm.Decimal.

Что делает Twitter API в этом случае, так это добавляет конкретное поле ".._str": в JSON, как таковое:

{
   "id": 10765432100123456789,           // for JSON compliant clients
   "id_str": "10765432100123456789",     // for JavaScript
    ...
}

Мне очень нравится этот вариант, так как он по-прежнему совместим с клиентами с поддержкой int64. На практике такой дублированный контент в JSON не сильно повредит, если его сдуть/заархивировать на уровне HTTP.

После передачи в виде строки вы можете использовать такие библиотеки, как strint — библиотека JavaScript для строковых целых чисел для обработки такие ценности.

Обновление. Новые версии движков JavaScript включают BigInt объектный класс, способный обрабатывать более 53-бит. На самом деле его можно использовать для произвольно больших целых чисел, поэтому он хорошо подходит для 64-битных целых чисел. Но при сериализации в формате JSON значение BigInt будет сериализоваться как строка JSON - как ни странно, но я думаю, в целях совместимости.

person Arnaud Bouchez    schedule 25.01.2016
comment
Отличное объяснение. Я только что столкнулся с этим, с интерфейсом JS и серверной частью веб-API С#. Мой HttpResponseMessage сериализовал мои объекты (которые содержали длинные идентификаторы), и они усекались. Что было действительно странно, так это то, что они не были обрезаны, пока не попали в браузер. Если я создал ответ, а затем проанализировал содержимое перед его отправкой, идентификаторы не были усечены. Только когда браузер получил и проанализировал ответ, он волшебным образом усекся. Ваше объяснение помогло мне понять, почему. Спасибо! В итоге я зарегистрировал собственный конвертер для длинных типов. - person Kris Coleman; 28.04.2017

Тип Javascript Number (64-битный IEEE 754) имеет только около 53 бит точности.

Но если вам не нужно выполнять сложение или умножение, вы можете сохранить 64-битное значение в виде 4-символьной строки, поскольку JavaScript использует UTF-16.

Например, 1 может быть закодировано как "". Это имеет то преимущество, что сравнение значений (==, >, ‹) работает со строками, как и ожидалось. Также кажется простым писать битовые операции:

function and64(a,b) {
    var r = "";
    for (var i = 0; i < 4; i++)
        r += String.fromCharCode(a.charCodeAt(i) & b.charCodeAt(i));
    return r;
}
person David Leonard    schedule 13.05.2009
comment
Это умный способ экономить на байтах, передаваемых по сети, и в то же время иметь возможность более или менее прочитать число. Мне было бы интересно узнать, есть ли какие-либо ошибки с этим подходом. - person acjay; 10.11.2017

Представление числа JS является стандартным ieee double, поэтому вы не можете представить 64-битное целое число. iirc вы получаете, возможно, 48 бит фактической точности int в двойном, но все биты JS уменьшаются до 32-битной точности (это то, что требует спецификация, ура!), поэтому, если вам действительно нужен 64-битный int в js, вам нужно будет реализовать свой собственный 64-битная библиотека встроенной логики.

person olliej    schedule 16.10.2008
comment
53 бита всегда, исключая знак. - person quadrupleslap; 28.06.2017

Сам JSON не заботится об ограничениях реализации. ваша проблема в том, что JS не может обрабатывать ваши данные, а не протокол. Другими словами, ваш клиентский код JS должен использовать любой из этих несовершенных вариантов.

person Javier    schedule 16.10.2008

Это случилось со мной. Весь ад разразился при отправке больших целых чисел через json в JSON.parse. Я провел дни, пытаясь отладить. Проблема сразу же решилась, когда я передал значения в виде строк.

Используйте { "the_sequence_number": "20200707105904535" } вместо { "the_sequence_number": 20200707105904535 }

Что еще хуже, может показаться, что там, где реализован каждый JSON.parse, есть какая-то общая библиотека между Firefox, Chrome и Opera, потому что все они вели себя одинаково. Сообщения об ошибках Opera содержат ссылки на URL-адреса Chrome, почти как WebKit, используемый браузерами.

console.log('event_listen[' + global_weird_counter + ']: to be sure, server responded with [' + aresponsetxt + ']');
var response = JSON.parse(aresponsetxt);
console.log('event_listen[' + global_weird_counter + ']: after json parse: ' + JSON.stringify(response));

Поведение, которое я получил, было таким, когда математика указателя пошла ужасно плохо. Призраки вылетали из моей рабочей станции, сея хаос во сне. Теперь, когда я перешел на струны, они все изгнаны.

person Desmond Coertzen    schedule 07.07.2020
comment
Разве это не проблема Javascript, а не JSON. - person user48956; 03.11.2020
comment
Видимо так. Прочитайте stackoverflow.com/questions/ 9643626/ - person Desmond Coertzen; 05.11.2020