Принуждение плавать в неподписанном символе на ARM против Intel - PullRequest
5 голосов
/ 20 января 2011

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

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

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

Ответы [ 3 ]

7 голосов
/ 21 января 2011

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

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

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

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

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

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

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

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

c = (char) f;

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

0 голосов
/ 21 января 2011

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

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

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

...