Как проверить, изменится ли числовое значение?

Я выполняю некоторые преобразования типов данных, где я должен представлять uint, long, ulong а также decimal как IEEE 754 двойные значения с плавающей запятой. Я хочу быть в состоянии обнаружить, если тип данных IEEE 754 не может содержать значение, прежде чем выполнить преобразование.

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

Есть ли надежный способ сделать эту проверку? Я ищу полноты над простотой реализации. У меня такое чувство, что я буду внимательно читать спецификацию IEEE 754 и тщательно проверять матиссу и экспоненту...

Я должен добавить, что меня больше всего интересует точное представление целых чисел и что потеря точности с плавающей запятой имеет второстепенное значение (но все же стоит рассмотреть).

РЕДАКТИРОВАТЬ: Int32 может быть полностью выражен как IEE-754. Так же Decimal Тип данных является очень важной частью вопроса.

Важное обновление: если вы имеете в виду этот вопрос, вам также следует прочитать этот вопрос: IEEE-754 Двойной (64-разрядная с плавающей запятой) и Длинный (64-разрядное целое).

Он отмечает недостаток в ответе, когда некоторые очень большие значения также могут быть точно представлены IEEE-754. Хотя это может означать, что значение будет корректно в обоих направлениях, для моей первоначальной цели (будет ли оно в обоих направлениях в JavaScript) оно не будет.

Также, похоже, есть ошибка в типе CLR System.Double, потому что он не позволяет правильно использовать эти значения в обоих направлениях.

3 ответа

Решение

Простое решение может быть что-то вроде (если x является int):

if ((int)(double)x != x) { 
  // won't convert
} else {
  // will convert
}

и тд долго и тд

(double) x преобразует x из int в double. Затем (int) снова преобразует его. Таким образом, (int) (double) x преобразовывает форму int в double, а затем обратно. По сути, код проверяет, что преобразование в double является обратимым (и, следовательно, что double может хранить точное значение типа int).

Это в основном зависит от диапазона номеров, с которым вы работаете. Пока вы в пределах 15 цифр (для двойного), вы должны быть в безопасности для целых чисел.

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

Так что, пока ваш номер < 2^53, вы обычно хороши.

IEEE 754 Double имеет 52 бита для мантиссы, и вы конвертируете из / в целое / длинное, поэтому его довольно легко проверить. Если ваше целое число потребляет менее 52 битов, то оно должно быть без проблем преобразовано в IEEE 754 double.

Я предполагаю (я знаю точно в случае Java, но не C# и ленив, чтобы проверить), что int 32-битный, а long 64-битный. Так что наверняка int может поместиться в double без проблем, как sign, так и unsign.

Для ulong вам просто нужно, чтобы все биты выше 52-го были похожи ((aULong && 0xFFF0000000000000) == 0).

Надо долго доводить свой знак до рассмотрения. Поскольку Long является вторым дополнением, а IEEE754 - нет (просто имеет отрицательный бит), он может просто конвертировать отрицательный длинный в положительный (*-1) и проверять как положительный. Так что, если long отрицательный, время его сначала -1 (ничего не делать для положительного). Затем проверьте это как ulong.

Надеюсь это поможет.

Другие вопросы по тегам