Принуждение плавать в безписанном char на руке против Intel

StackOverflow https://stackoverflow.com/questions/4752315

Вопрос

Когда я запускаю следующий код 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

Как вы можете себе представить, такая разница может представить какой ужас Ошибки в кроссплатформенных проектах. Мои вопросы:

  1. Был ли я ошибаюсь, что принуждает поплавок в безписанном символе, даст те же результаты на всех платформах?

  2. Может ли его быть проблемой компилятора?

  3. Есть ли элегантный обходной путь?

Это было полезно?

Решение

Стандарт C не имеет очень жестких правил для того, что вы пытаетесь сделать. Вот рассматриваемый абзац из раздела 6.3.1 Арифметические операнды (конкретно раздел 6.3.1.4 Настоящий плавающий и целый ряд):

Когда конечное значение реального плавающего типа преобразуется в целочисленный тип, отличный от _Bool, Фракционная часть отбрасывается (то есть значение усекается в сторону нуля). Если значение интегральной части не может быть представлено целочисленным типом, поведение не определен.

Есть даже более конкретная сноска относительно точного случая, о котором вы спрашиваете:

Остановка, выполняемая, когда значение целочисленного типа преобразуется в тип без знака, нельзя выполнять, когда значение реального плавающего типа преобразуется в тип без знака. Таким образом, диапазон портативных реальных значений плавающего (−1, Utype_MAX+1).

UtypeMAX+1 для вашего дела 256. Анкет Ваши несоответствующие случаи - все негативные числа. После усечения они по-прежнему негативны и находятся за пределами диапазона (-1, 256), поэтому они прочно в зоне «неопределенного поведения». Даже некоторые из подходящих случаев, которые вы показали, где число плавающей запятой больше или равно 256, не гарантированно работать - вам просто повезло.

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

  1. Да, ты ошибался.
  2. Это проблема компилятора в том смысле, что ваши разные компиляторы дают разные результаты, но, поскольку им разрешено спецификация, я бы не назвал это ошибкой компилятора.
  3. Это зависит от того, что вы хотите сделать - если вы можете объяснить это лучше, кто -то, так что сообщество почти наверняка сможет вам помочь.

Другие советы

Я собираюсь ответить на 3 в своем собственном вопросе, но не буду отмечать это как принятый ответ. Хитрость, кажется, простой состав в принуждении:

c = (char) f;

Использование (int) или (короткое) тоже работает. Мне все еще интересно выяснить, в чем заключается причина этой проблемы: компилятор или процессор.

Конкретная проблема, с которой вы сталкиваетесь, для меня выглядит как эндсианство. Попробуйте заменить ту или иную реализацию c = *((char *)&f + sizeof(float) - 1); Или что -то похожее на получение последнего байта поплавка, и посмотрите, соответствует ли он результат для другой платформы.

В целом, поведение будет зависеть от эндзианности, длины слов и плавающей запятой процессора и того, как компилятор нацелен на это. Рука-би-эдиан, поэтому она может или не может соответствовать упорядочению байтов. Похоже, что нет общей гарантии, что одна реализация C поддерживает тот же формат плавающей запятой, что и другая: Типы плавучих точек с фиксированным размером .

Вы используете это в производственном коде? Я бы очень усердно посмотрел на то, почему это нужно сделать. Один или другой тип, вероятно, не используется в соответствии с задумами. Обходные пути не будут элегантными.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top