Подтвердить что ты не робот

Java vs C с плавающей запятой: "x * x" отличается от "pow (x, 2)"?

Почему это так? Кажется, что Java создает результат с небольшим несоответствием при умножении двух поплавков по сравнению с C и даже на метод Java Math.pow.

Java:

float a = 0.88276923;

double b = a * a;   // b becomes 0.779281497001648  <---- what???
b = Math.pow(a,2);  // b becomes 0.7792815081874238

С

float a = 0.88276923;

double b = a * a;   // b becomes 0.7792815081874238
pow(a,2);           // b becomes 0.7792815081874238

Обновление: в комментарии от пользователя Ed Ed, я также обнаружил, что поведение C изменяется в зависимости от компилятора. Использование gcc похоже на поведение Java. Используя визуальную студию (в зависимости от вашей целевой платформы), она может отображать результаты, увиденные выше, или те, что видны на Java. Тьфу.

4b9b3361

Ответ 1

Поскольку pst и trutheality уже разумно отмечены, C продвигает float до a double перед умножением. Фактически, они продвигаются до 80-битного расширенного значения точности, когда они помещаются в стек. Вот вывод ассемблера (VS2005 x86 C89)

    double b = a * a;
00411397  fld         dword ptr [a] 
0041139A  fmul        dword ptr [a] 
0041139D  fstp        qword ptr [b] 

Инструкция FLD

Команда FLD загружает 32-битное, 64-битное или 80-битное значение с плавающей запятой в стек. Эта команда преобразует 32- и 64-разрядные операнды в 80-разрядное расширенное значение точности, прежде чем нажимать значение на стек с плавающей точкой.


Интересно, что если я создаю для целевого x64, используется команда movss, и вы получите значение 0.779281497001648 в качестве результата, то есть то, что вы видите в своем примере java. Попробуйте.

Ответ 2

Что делает Java для

double b = a * a;

умножает a * a как (32-разрядный) float первым и преобразует результат в (64-разрядный) double при назначении b.

b = Math.pow(a,2);

Преобразует a в (64-разрядный) double первый (поскольку параметры для Math.pow являются double, double), а затем квадратизируют его.

Что вызывает недоумение (для меня), почему C, кажется, отбрасывает a в double сначала в

double b = a * a;

Является ли это стандартным?

Изменить: Я смутно помню о том, что C не требует конкретной реализации (с точки зрения количества бит) для чисел... это то, что происходит здесь? Являются ли ваши float 64 бит? (В Java a float всегда 32 бита, а double всегда 64 бит).

Изменить: Оба ответа Ed и ответьте, что разные компиляторы дают разные результаты, показывают, что результаты C являются специфичными для реализации и архитектуры.