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. Тьфу.
Ответы
Ответ 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 являются специфичными для реализации и архитектуры.