Различные результаты усечения при приведении - PullRequest
4 голосов
/ 20 декабря 2010

У меня возникли некоторые трудности с предсказанием того, как мой код на C будет усекать результаты.См. Следующее:

float fa,fb,fc;
short ia,ib;

fa=160
fb=0.9;
fc=fa*fb;
ia=(short)fc;
ib=(short)(fa*fb);

Результаты: ia = 144, ib = 143.

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

Редактировать: результаты компилируются с MS Visual C ++ Express 2010 на Intel Core i3-330m.Я получаю те же результаты на gcc версии 4.4.3 (Ubuntu 4.4.3-4ubuntu5) в Virtual Box на той же машине.

Ответы [ 3 ]

7 голосов
/ 20 декабря 2010

Компилятору разрешено использовать больше точности для подвыражения, например fa*fb, чем при назначении переменной float, например fc.Таким образом, это часть fc=, которая очень незначительно меняет результат (и, следовательно, приводит к значительному усечению).

3 голосов
/ 20 декабря 2010

Ашеплер объяснил механику того, что происходит хорошо, но фундаментальная проблема с вашим кодом заключается в использовании значения, которое не существует как float в коде, которое зависит от значения его аппроксимации внестабильный путь.Если вы хотите умножить на 0,9 (фактическое число 0,9 = 9/10, а не значение с плавающей запятой 0.9 или 0.9f), вы должны умножить на 9, затем разделить на 10 или забыть о типах с плавающей запятой и использовать десятичное числобиблиотека арифметики.

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

0 голосов
/ 20 декабря 2010

Это зависит от компилятора.На моем (gcc 4.4.3) он дает одинаковый результат для обоих выражений, а именно -144, вероятно, потому что идентичное выражение оптимизировано.

Другие хорошо объяснили, что произошло.Другими словами, я бы сказал, что различия, вероятно, происходят из-за того, что ваш компилятор внутренне переводит числа с плавающей запятой в регистры fpu на 80 битов перед выполнением умножения, а затем преобразует обратно либо в число с плавающей точкой, либо в короткую позицию.

Если моя гипотеза верна, если вы напишите ib = (short)(float)(fa * fb);, вы получите тот же результат, что и при броске fc в short.

...