Как проверить, изменится ли числовое значение?
Я выполняю некоторые преобразования типов данных, где я должен представлять 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.
Надеюсь это поможет.