Дробный подсчет через целые числа

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

Например, я получаю целое число 50155, что означает 50 и 15.5/32 доллары. Я затем получаю 10210, который равняется 10 и 21/32 доллара. Так 50 15.5/32 + 10 21/32 = 61 4.5/32, таким образом:

50155 + 10210 = 61045

Снова, я хочу избежать этого:

int a = 50155;
int b = a / 1000;
float c = a % 1000;
float d = b;
d += c / 320f;
// d = 50.484375

Я очень предпочел бы это:

int a = 50155;
int b = 10210;
int c = MyClass.Add(a.b); // c = 61045
...
public int Add(int a, int b)
{
    // ?????
}

Заранее спасибо за справку!

6
задан Steve H. 9 February 2010 в 19:01
поделиться

7 ответов

Я не думаю, что вам нужно использовать числа с плавающей запятой ...

public static int Add(int a, int b)
{
    int firstWhole = a / 1000;
    int secondWhole = b / 1000;
    int firstFraction = a % 1000; 
    int secondFraction = b % 1000;
    int totalFraction = firstFraction + secondFraction;
    int totalWhole = firstWhole + secondWhole + (totalFraction / 320);
    return totalWhole * 1000 + (totalFraction % 320);
}

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

РЕДАКТИРОВАТЬ: Если вы вынуждены придерживаться формата «одно целое число», но можете немного его отрегулировать, вы можете рассмотреть возможность использования 512 вместо 1000. Таким образом вы можете использовать простую маску и сдвиг:

public static int Add(int a, int b)
{
    int firstWhole = a >> 9;
    int secondWhole = b >> 9;
    int firstFraction = a & 0x1ff
    int secondFraction = b & 0x1ff;
    int totalFraction = firstFraction + secondFraction;
    int totalWhole = firstWhole + secondWhole + (totalFraction / 320);
    return (totalWhole << 9) + (totalFraction % 320);
}

Там все еще возиться с 320, но он, по крайней мере, несколько лучше.

4
ответ дан 8 December 2019 в 18:35
поделиться

Разделите строку на части, представляющие целые доллары, и части, представляющие доли долларов. Для последнего, вместо того, чтобы рассматривать его как 10,5 тридцати секунд доллара, вероятно, проще рассматривать его как 105 триста двадцатых доллара (т.е. умножить оба на десять, чтобы числитель всегда был целым числом).

Отсюда делать математические вычисления довольно просто (хотя писать несколько утомительно): складывайте дроби. Если это превышает целый доллар, перенесите доллар (и вычтите 320 из дробной части). Затем добавьте все доллары. Точно так же вычитание - хотя в этом случае вам нужно принимать во внимание заимствование, а не перенос.

3
ответ дан 8 December 2019 в 18:35
поделиться

В качестве точки для обучения это представление называется « фиксированная точка ». Есть ряд реализаций, на которые вы можете взглянуть. Я настоятельно рекомендую вам НЕ использовать int в качестве типа данных верхнего уровня, а вместо этого создать тип с именем Fixed, который инкапсулирует операции. Он будет вести отсчет вашей ошибки, когда вы по ошибке добавляете простой int к числу с фиксированной точкой без предварительного масштабирования или масштабируете число и забываете его масштабировать.

2
ответ дан 8 December 2019 в 18:35
поделиться

Изменить :
Этот ответ предполагает, что человек «держится подальше» от арифметики с плавающей запятой. Удивительно, но OP указал, что его логика на основе чисел с плавающей запятой (не показана по причинам собственности) была в два раза быстрее, чем решение с целочисленным модулем ниже! Это показывает, что FPU в конце концов не так уж и плохи ...

Определенно, держитесь подальше от поплавков (для этой конкретной проблемы). Целочисленная арифметика более эффективна и не вызывает ошибок округления.

Что-то вроде следующего должно помочь
Примечание. Как написано, предполагается, что A и B положительны.

int AddMyOddlyEncodedDollars (int A, int B) {
  int sum;
  sum = A + B
  if (sum % 1000 < 320);
     return sum
  else
     return sum + 1000 - 320;
}

Правка : Эффективность оператора по модулю в C
я очень сильно зависит от компилятора ... Поскольку значение по модулю известно во время компиляции, я бы ожидал, что большинство современные компиляторы переходят на подход «умножение [на обратное] и сдвиг», и это быстро.
Эта озабоченность по поводу производительности (с этим довольно надуманным форматом) является призывом к преждевременной оптимизации, но, опять же, я видел, как программное обеспечение в финансовой индустрии сильно оптимизировано (мягко говоря) и оправданно так.

3
ответ дан 8 December 2019 в 18:35
поделиться

По-моему, странная кодировка.

В любом случае, если формат имеет 10-базовый Nxxx, где N - целое число, обозначающее целые доллары, а xxx интерпретируется как

(xxx / 320)

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

int a = ..., b = ...; // dollar amounts
int c = (a + b); // add together
// Calculate carry
int carry = (c % 1000) / 320; // integer division
c += carry * 1000;
c -= carry * 320;
// done

Примечание: это работает, потому что, если a и b закодированы правильно, дробные части складываются максимум до 638 ​​и, таким образом, нет «переполнения» для целой части долларов .

1
ответ дан 8 December 2019 в 18:35
поделиться

ОСТОРОЖНО : это сообщение неверно, неверно , неверно . Я удалю его, как только перестану чувствовать себя дураком, пытаясь это сделать.

Вот мой ход: вы можете обменять пространство на время .

Создайте отображение для первых 10 бит кортежа: количество долларов, количество частей из 32. Затем используйте битовые манипуляции с вашим целым числом:

  • игнорируйте биты 11 и выше, примените карту.
  • сдвиньте целое число 10 раз, добавьте небольшие изменения в долларах из сопоставления выше
  • теперь у вас есть доллар и 32 части
  • добавьте оба
    • переместить переполнение к сумме в долларах

Затем, чтобы преобразовать обратно в «каноническую» нотацию, вам понадобится карта обратного просмотра для ваших частей 32 и «займ» долларов, чтобы заполнить биты. Сдвиньте доллары 10 раз и сложите 32 штуки.

РЕДАКТИРОВАТЬ: Я должен удалить это, но мне слишком стыдно. Конечно, это не может работать. Я такой глупый: (

Причина в том, что сдвиг на 10 вправо аналогичен делению на 1024 - это не значит, что некоторые из младших битов имеют сумму в долларах, а некоторые - 32 штуки. Десятичные и двоичные нотация просто плохо разделяется. Вот почему мы используем шестнадцатеричную нотацию (группировка по 4 бита). Облом.

0
ответ дан 8 December 2019 в 18:35
поделиться

Если вы настаиваете на работе с ints, вы не сможете решить свою проблему без синтаксического анализа - в конце концов, ваши данные не целые. Я привожу в качестве доказательства (пока) 3 ответа, которые все разбирают ваши целые числа на свои компоненты перед выполнением арифметики.

Альтернативой может быть использование рациональных чисел с 2 (целыми) компонентами, одна для целой части и одна для числа 320-х в дробной части. Затем выполните соответствующую рациональную арифметику. Как всегда, тщательно выбирайте представления данных, и ваши алгоритмы станут намного проще реализовать.

Я не могу сказать, что я думаю, что эта альтернатива особенно лучше по любой оси сравнения, но она может удовлетворить ваше желание не анализировать.

0
ответ дан 8 December 2019 в 18:35
поделиться
Другие вопросы по тегам:

Похожие вопросы: