Принуждение плавать в безписанном char на руке против Intel
-
13-10-2019 - |
Вопрос
Когда я запускаю следующий код C на машине Intel ...
float f = -512;
unsigned char c;
while ( f < 513 )
{
c = f;
printf( "%f -> %d\n", f, c );
f += 64;
}
... Вывод выглядит следующим образом:
-512.000000 -> 0
-448.000000 -> 64
-384.000000 -> 128
-320.000000 -> 192
-256.000000 -> 0
-192.000000 -> 64
-128.000000 -> 128
-64.000000 -> 192
0.000000 -> 0
64.000000 -> 64
128.000000 -> 128
192.000000 -> 192
256.000000 -> 0
320.000000 -> 64
384.000000 -> 128
448.000000 -> 192
512.000000 -> 0
Однако, когда я запускаю один и тот же код на устройстве ARM (в моем случае iPad), результаты совершенно разные:
-512.000000 -> 0
-448.000000 -> 0
-384.000000 -> 0
-320.000000 -> 0
-256.000000 -> 0
-192.000000 -> 0
-128.000000 -> 0
-64.000000 -> 0
0.000000 -> 0
64.000000 -> 64
128.000000 -> 128
192.000000 -> 192
256.000000 -> 0
320.000000 -> 64
384.000000 -> 128
448.000000 -> 192
512.000000 -> 0
Как вы можете себе представить, такая разница может представить какой ужас Ошибки в кроссплатформенных проектах. Мои вопросы:
Был ли я ошибаюсь, что принуждает поплавок в безписанном символе, даст те же результаты на всех платформах?
Может ли его быть проблемой компилятора?
Есть ли элегантный обходной путь?
Решение
Стандарт C не имеет очень жестких правил для того, что вы пытаетесь сделать. Вот рассматриваемый абзац из раздела 6.3.1 Арифметические операнды (конкретно раздел 6.3.1.4 Настоящий плавающий и целый ряд):
Когда конечное значение реального плавающего типа преобразуется в целочисленный тип, отличный от
_Bool
, Фракционная часть отбрасывается (то есть значение усекается в сторону нуля). Если значение интегральной части не может быть представлено целочисленным типом, поведение не определен.
Есть даже более конкретная сноска относительно точного случая, о котором вы спрашиваете:
Остановка, выполняемая, когда значение целочисленного типа преобразуется в тип без знака, нельзя выполнять, когда значение реального плавающего типа преобразуется в тип без знака. Таким образом, диапазон портативных реальных значений плавающего
(−1, Utype_MAX+1)
.
UtypeMAX+1
для вашего дела 256
. Анкет Ваши несоответствующие случаи - все негативные числа. После усечения они по-прежнему негативны и находятся за пределами диапазона (-1, 256), поэтому они прочно в зоне «неопределенного поведения». Даже некоторые из подходящих случаев, которые вы показали, где число плавающей запятой больше или равно 256
, не гарантированно работать - вам просто повезло.
Поэтому ответы на ваши пронумерованные вопросы:
- Да, ты ошибался.
- Это проблема компилятора в том смысле, что ваши разные компиляторы дают разные результаты, но, поскольку им разрешено спецификация, я бы не назвал это ошибкой компилятора.
- Это зависит от того, что вы хотите сделать - если вы можете объяснить это лучше, кто -то, так что сообщество почти наверняка сможет вам помочь.
Другие советы
Я собираюсь ответить на 3 в своем собственном вопросе, но не буду отмечать это как принятый ответ. Хитрость, кажется, простой состав в принуждении:
c = (char) f;
Использование (int) или (короткое) тоже работает. Мне все еще интересно выяснить, в чем заключается причина этой проблемы: компилятор или процессор.
Конкретная проблема, с которой вы сталкиваетесь, для меня выглядит как эндсианство. Попробуйте заменить ту или иную реализацию c = *((char *)&f + sizeof(float) - 1);
Или что -то похожее на получение последнего байта поплавка, и посмотрите, соответствует ли он результат для другой платформы.
В целом, поведение будет зависеть от эндзианности, длины слов и плавающей запятой процессора и того, как компилятор нацелен на это. Рука-би-эдиан, поэтому она может или не может соответствовать упорядочению байтов. Похоже, что нет общей гарантии, что одна реализация C поддерживает тот же формат плавающей запятой, что и другая: Типы плавучих точек с фиксированным размером .
Вы используете это в производственном коде? Я бы очень усердно посмотрел на то, почему это нужно сделать. Один или другой тип, вероятно, не используется в соответствии с задумами. Обходные пути не будут элегантными.