Целочисленная арифметика Objective-C

Я пытаюсь вычислить некоторые числа в приложении для iPhone.

int i = 12;
int o = (60 / (i * 50)) * 1000;

Я бы ожидал, что o будет 100 (это миллисекунды) в этом примере, но он равен 0, как показано NSLog(@"%d", o).

Это также равно 0.

int o = 60 / (i * 50) * 1000;

Это равно 250 000, что является прямой математикой слева направо.

int o = 60 / i * 50 * 1000;

Что здесь пролетает над моей головой?

Спасибо,
Ник


person Stateful    schedule 16.11.2010    source источник
comment
См. [Проблема C - результат деления всегда равен нулю](stackoverflow.com/questions/2345902/).   -  person Matthew Flaschen    schedule 16.11.2010


Ответы (7)


В Objective-C / выполняет целочисленное деление целочисленных аргументов, поэтому 4/5 усекается до 0, 3/2 усекается до 1 и так далее. Вы, вероятно, захотите привести некоторые из ваших чисел к формам с плавающей запятой перед выполнением деления.

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

60 / (i * 50) * 1000

член в скобках вычисляется первым, поэтому 60 делится на 600, что дает результат 0. В

60 / i * 50 * 1000

первая операция - разделить 60 на 12, что дает результат 5, а затем выполняются умножения.

person High Performance Mark    schedule 16.11.2010
comment
С тоже, конечно. Арифметика в Objective-C идентична арифметике в C. :) - person Jonathan Grynspan; 16.11.2010
comment
Строго говоря, результат не округляется, а усекается. - person Dima; 16.11.2010
comment
Ну, усечение — это форма округления (в сторону нуля), так что... да. - person Jonathan Grynspan; 16.11.2010
comment
@Jonathan Извините, но это помогает быть точным в этих вещах. Усечение — это такая же форма округления, как целочисленное деление — форма деления с плавающей запятой. Как видите, незнание разницы может привести к неприятностям. - person Dima; 16.11.2010
comment
@ Дима, усечение - это своего рода округление, также называемое округлением до нуля. - person Vovanium; 16.11.2010
comment
@Vovanium и целочисленное деление - это своего рода деление, но если вы не будете точно помнить, какое это деление, вы можете получить неожиданные результаты. Округление определяется как округление в большую сторону, если дробная часть больше или равна 0,5, и округление в меньшую сторону в противном случае. Усечение округляется до ближайшего целого числа, также известного как операция пола. Округление до ближайшего целого числа называется операцией потолка. Эти три операции связаны, но они совершенно различны. Результатом целочисленного деления является пол, он же усечение, он же округление в меньшую сторону, а не в большую или меньшую сторону. - person Dima; 16.11.2010
comment
@Dima: Trucation эквивалентен полу только для неподписанных. Сравните: пол(-5./2.) и -5/2. Первый будет -3., а второй -2. Усечение, также известное как округление до нуля, если нижний предел для положительных значений, и верхний предел для отрицательных значений. Это мелочь, но она может вас запутать. - person Vovanium; 17.11.2010
comment
Хорошая точка зрения! Не понял этого. Тогда усечение не округляется, ни пол, ни потолок. Спасибо. - person Dima; 17.11.2010
comment
см. en.wikipedia.org/wiki/Округление#Округление_до_целого для терминологии, и да, усечение - это форма округления - person Christoph; 17.11.2010

Целое число, деленное на целое число, является целым числом.

так что 60/600 это не 0.1, это 0.

Вместо этого приведите (или объявите) некоторые вещи как float.

person Dave DeLong    schedule 16.11.2010
comment
Приведение или введение неявного преобразования в float или double не способ вычисления целочисленных выражений. В системах без IEEE с плавающей запятой это может дать очень ложные результаты (C почти не предъявляет требований к качеству реализации с плавающей запятой), и даже с IEEE с плавающей запятой вы можете получить ошибки из-за потери точности с целыми числами, слишком большими, чтобы поместиться в используется тип с плавающей запятой. Правильное решение проблемы OP — просто алгебраически упростить выражение. - person R.. GitHub STOP HELPING ICE; 16.11.2010

Это делает целочисленную математику. 60/(12 * 50) равно 0,1, усекается до 0.

Должно работать, если вы форсируете числа с плавающей запятой, а затем возвращаетесь к целому числу.

int o = (int)(60.0 / ((double) i / 50.0) * 1000.0;

Наверное, не очень-то и нужно делать все в двойном размере.

person Neth    schedule 16.11.2010
comment
Приведение или введение неявного преобразования в float или double не способ вычисления целочисленных выражений. Смотрите мой комментарий к ответу Дэйва. - person R.. GitHub STOP HELPING ICE; 16.11.2010

Заменять:

int o = (60 / (i * 50)) * 1000;

с участием:

int o = 1200/i;
person R.. GitHub STOP HELPING ICE    schedule 16.11.2010
comment
Кто бы ни был даунвотером, объясните, пожалуйста. В отличие от других ответов, этот ответ дает точный усеченный целочисленный результат вычисления без каких-либо угловых случаев из-за переполнения или потери точности. Насколько я могу судить, это единственный правильный ответ здесь. - person R.. GitHub STOP HELPING ICE; 16.11.2010

В порядке старшинства операция:

60 / (12 * 50) 

выполняется перед умножением на 1000.

Это значение меньше 1 и приводится к int, что усекает его до 0. И 0 раз все равно 0.

Используйте float или сначала умножьте на 1000, чтобы убедиться, что вы не в конечном итоге распространяете 0 в своих вычислениях.

person Alex Reynolds    schedule 16.11.2010

Все операции в вашем выражении выполняются в целочисленной арифметике, что означает, что дробная часть каждого промежуточного результата усекается. Это означает, что если вы разделите меньшее целое число на большее, вы всегда получите 0.

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

int o = (60.0 / (i * 50.0)) * 1000.0;

должно быть о = 100.

person Dima    schedule 16.11.2010

Я думаю, вам нужно использовать float здесь вместо int. Он будет работать так, как вы хотите! Даст вам ответ в десятичных дробях, а также.

person Mosib    schedule 03.04.2012
comment
Хотя float будет работать, это не устраняет недоразумение, которое изначально вызвало проблему! - person Andrew; 29.10.2012