2012-01-18 2 views
15

Преподавание себя C и обнаружение, что когда я выполняю уравнение для временного преобразования, оно не будет работать, если я не изменю дробь на десятичную. т.е.Почему деление приводит к нулю вместо десятичного числа?

tempC=(.555*(tempF-32)) будет работать, но tempC=((5/9)*(tempF-32)) не будет работать.

Почему?
Согласно C Primer Plus, он должен работать, поскольку я использую поплавки как для tempC, так и для tempF.

ответ

16

Похоже, у вас есть целое подразделение во втором случае:

tempC=((5/9)*(tempF-32)) 

5/9 получит усекается до нуля.

Чтобы исправить это, вам нужно сделать одну из них типа с плавающей точкой:

tempC=((5./9.)*(tempF-32)) 
+2

Это усечение, хотя и не округление. – jmkeyes

+0

Спасибо за терминологию. :) – Mysticial

+0

doh! Спасибо ... это имеет смысл сейчас. Я видел несколько примеров с десятичной точкой, но ни копейки не упал. Всем спасибо! –

0

Если положить 5/9 в скобках, это будет вычислен первым, и так как те два целых числа, это будет сделано с помощью целочисленного деления, и результат будет равен 0, пока не будет оценена остальная часть выражения.

Вы можете изменить ваше выражение так, что преобразование плавать происходит первым:

tempC=((5/9)*(tempF-32));tempC=(5*(tempF-32))/9;

или, конечно, как говорят другие, использовать константы с плавающей точкой.

+0

Даже если они не были в скобках, они все равно могут быть оценены как целые числа. Порядок оценки этого выражения является неопределенным поведением в C, компилятор может выбрать оценку слева направо или справа налево, и вы не можете знать, какой порядок оценки применяется. – Lundin

+1

Не совсем верно. Скобки имеют эффект! Если вы убедитесь, что первый расчет, который выполняется, приводит к поплавке, остальная часть выражения не вернется обратно к целому. Я добавил свой ответ, чтобы показать, что я имею в виду. –

+0

Переписанный пример не эквивалентен удалению скобок, вы намеренно изменили порядок оценки. 'tempC = 5/9 * (tempF-32);' - пример с удалением скобок. Этот пример может дать или не дать желаемый результат в зависимости от того, оценивает ли компилятор слева направо или справа налево - он зависит от неуказанного поведения. – Lundin

1

5/95/9 - целочисленное выражение, поэтому оно усекается до 0. Ваш компилятор должен предупредить вас об этом, иначе вы должны изучить возможность включения предупреждений.

+3

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

+1

@ Lundin: вот почему это было бы (педантичное) предупреждение. если константное целочисленное выражение дает нуль, оно должно быть либо нулевым, либо имело бы надзор. – Necrolis

+0

Не обязательно, может быть много случаев, когда вы хотите получить постоянное выражение с результатом 0. Например, 'result = SOME_CONSTANT & MASK;' может очень хорошо привести к нулю. Допустим, у вас есть аналогичные операции маскировки бит по всему коду с разными масками. Тогда вам не понадобятся сотни предупреждений, почему вы должны быть наказаны только потому, что вы пишете общий, согласованный код без каких-либо магических чисел в нем? – Lundin

2

Когда вы делаете 5/9, 5 и 9 являются целыми числами и целочисленного деления происходит. Результатом целочисленного деления является целое число, и это фактор двух операндов. Таким образом, фактор в случае 5/9 равен 0, и поскольку вы умножаетесь на 0, tempC получается равным 0. Чтобы не иметь целочисленного деления, по крайней мере один из двух операндов должен быть float.

E.g. если вы используете 5.0/9 или 5/9.0 или 5.0/9.0, он будет работать, как ожидалось.

5

Другие уже сказали вам, что 5 и 9 являются целыми числами, поэтому результат усекается.

Я добавлю объяснение для более глубокого понимания того, что на самом деле происходит между строками:

double tempC; 
double tempF; 
tempC = (5/9) * (tempF-32); // removed unnecessary parenthesis 

В зависимости от которых порядка оценки, слева направо или справа налево, что ваш конкретный компилятор использует, он либо начнет оценивать подвыражение (5/9), либо (tempF-32).

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


Предположим, что один конкретный компилятор использует оценку слева направо.

  • Правила приоритета оператора C определяют, где начинается оценка. Оператор скобки имеет наивысший приоритет в C, поэтому компилятор начнет с оценки содержимого первой скобки.
  • Поэтому оно начинается с выражения (5/9).
  • Компилятор проверяет тип каждого операнда.
  • В этом случае они являются постоянными целыми литералами. Целочисленные литералы всегда имеют тип int в C.
  • Поскольку оба операнда одного типа, не требуется неявных преобразований типов.
  • Расчет производится по типу int, а результат - int.

Так что теперь выражение теперь оценивается в:

tempC = (int)0 * (tempF-32);

  • Компилятор затем оценивает (tempF-32).
  • Типы операндов double и int. Они не одного типа.
  • Имеются неявные преобразования типов. В этом случае что-то называемое балансировкой (формально называемое обычными арифметическими преобразованиями).
  • Правила балансировки говорят, что если один тип является двойным, а другой - чем-то другим, другой тип должен быть преобразован в double.
  • После неявного преобразования типов выражение теперь эквивалентно (double)tempF - (double)32.0. Результат этого рассчитывается и сохраняется во временной, невидимой переменной типа double. Эта невидимая переменная хранится в регистре CPU или в стеке.

Теперь выражение может быть описана как

tempC = (int)result1 * (double)result2;

, где "результат1" равно 0 и "результат2" является результатом tempF - 32,0.

  • Затем компилятор оценивает это новое выражение. Он находит int и double.
  • Снова происходит балансировка, а int преобразуется в двойную.
  • Умножение выполняется в двух двухместных, а результат - в двойном.
  • Результат сохраняется в еще одной временной, невидимой переменной.

tempC = (double)result3;

  • компилятор оценивает это новое выражение.Он обнаруживает, что двойник должен быть сохранен внутри двойника. Это не проблема, поэтому никаких неявных преобразований не требуется. «result3» хранится в tempC.
+4

Порядок оценки подвыражений вообще не имеет значения, так как у него нет побочных эффектов. – aschepler

+1

Порядок оценки имеет значение только в том случае, если вы написали его так: '5/9 * (tempF-32)' теперь все ненужные круглые скобки удаляются. Оценка из tempF «наружу» даст двойной. Если вы поместите '(5/9) * anything', оно всегда будет' 0', если только какой-то компилятор не сделает магическое действие таким выражением, которое, вероятно, будет даже против стандартного, но я не буду делать ставку на него. – luk32

2

5/9 - целочисленное деление, не являющееся делением с плавающей запятой. Вот почему вы получаете неправильный результат.

Сделайте 5 или 9 переменных с плавающей запятой, и вы получите правильный ответ.

Как 5.0/9 ИЛИ 5/9.0

+0

Согласовано. Это всегда хорошая практика, чтобы добавить константу 'f', обозначающую плавающую точку, к константам. Точно так же 'u' для целых констант без знака – Andrew

Смежные вопросы