Модуль целого числа

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

Однако моя проблема возникает, когда я преобразовал ввод из числа с плавающей запятой в целое число и пытаюсь выделить цифры после десятичной точки, используя модуль; по какой-то причине он не возвращает правильную цифру.

Итак, скажем, для 321,78, извлекающего 1-й десятичный знак (или 8), он возвращает 7. Я не слишком уверен, почему это происходит и как это исправить.

Вот мой код для первой части

float number;
int digit1, digit2, digit3, decimals, decimal1, decimal2, int_number;

cout << "Enter a dollar amount from 0-1000: ";
cin >> number;

int_number = number * 100;

digit1 = (int_number % 1000)/100; //ones
digit2 = (int_number % 10000)/1000; //tens
digit3 = (int_number % 100000)/10000; // hundreds

decimal1 = int_number % 10;
decimal2 = (int_number % 100)/10;

Decimal1 должен возвращать 8, если введено значение 321,78. Я что-то пропустил? Любая помощь будет оценена по достоинству.


person KingKong    schedule 18.04.2013    source источник
comment
Попробуйте распечатать int_number.   -  person Mats Petersson    schedule 18.04.2013
comment
Я тоже подозреваю, что это проблема точности. Лучше использовать double, чем float   -  person syam    schedule 18.04.2013
comment
Если вы хотите, чтобы пользователь ввел 321.78, но в качестве int значения 32178, я рекомендую не использовать float, чтобы добраться туда. На самом деле вы хотите прочитать его как строку, проанализировать ее, чтобы удалить . и проверить на наличие ошибок формата, а затем преобразовать строку 32178 в int. Принятие float в качестве входных данных в основном говорит пользователю, что он должен ввести представляемое значение с плавающей запятой.   -  person Joseph Mansfield    schedule 18.04.2013
comment
если ввод 321,78, int_number становится 32178, что является целым числом, поэтому я мог бы использовать модуль. Даже тогда он не должен возвращать 8? 32178 % 10 = 8?   -  person KingKong    schedule 18.04.2013
comment
@KingKong Ты действительно пробовал это распечатать? Я подозреваю, что это 32177.   -  person Joseph Mansfield    schedule 18.04.2013
comment
@sftrabbit, к сожалению, мне не разрешено использовать строки в этом упражнении   -  person KingKong    schedule 18.04.2013
comment
@sftrabbit ты был прав, это 32177. не очень понимаю, почему это происходит 0.0   -  person KingKong    schedule 18.04.2013
comment
@сям нет. двойники могут быть менее подвержены проблемам с точностью, но они у них все же есть.   -  person Arne Mertz    schedule 18.04.2013
comment
@ArneMertz да, но поскольку у OP так много ограничений на то, что он может использовать, я вряд ли вижу другое решение.   -  person syam    schedule 18.04.2013


Ответы (6)


Это похоже на преобразование 1,5 в целое число: тип результата не имеет достаточной точности, чтобы точно представить исходное значение. То же самое с 321.78: значения с плавающей запятой не могут точно представлять это значение, и вы получите значение, которое немного меньше, чем вы ожидали, то есть 321.77####, где #### представляет дополнительные десятичные цифры, которые слишком утомительны. здесь вычислить. Когда это значение умножается на 100 и преобразуется в int, оно усекается, и 32177.#### становится 32177.

РЕДАКТИРОВАТЬ: забыл упомянуть решение.

int dollars;
int cents;
char dp;
std::cin >> dollars;
std::cin >> dp;
std::cin >> cents;
cents += dollars * 100;

Проверка ошибок оставлена ​​в качестве упражнения для читателя.

person Pete Becker    schedule 18.04.2013
comment
Огромное спасибо, я не знал, что это произошло. Спасибо за объяснение! - person KingKong; 18.04.2013

Прежде всего, добавьте в закладки Что должен знать каждый компьютерный ученый о плавающих Арифметика точек.

Ваша проблема связана с точностью с плавающей запятой. Попробуйте следующий простой пример:

#include <iostream>

int main()
{
    float f;
    std::cin >> f;
    int x = f * 100;
    std::cout << x;
}

Вы увидите, что x имеет значение 32177. Это вызвано тем, что типы с плавающей запятой имеют только определенную точность. Ближайшее представимое значение к 321.78, которое может иметь float, — это 321.779998779296875. Как видите, это чуть меньше 321.78. Когда вы умножаете это на 100, у вас есть что-то немного меньшее, чем 32178, которое при преобразовании в int имеет значение 32177.

Проблема здесь в том, что вы запрашиваете у пользователя значение float. Поскольку вы извлекаете непосредственно в float, вы накладываете на пользователя ограничение, что они могут вводить только те значения, которые могут быть представлены float. Очевидно, это очень глупо, так как вы в конечном итоге преобразуете это значение только в int. Представьте, что это сделал банкомат! Было бы много потерянных копеек. Вместо этого правильный подход здесь состоит в том, чтобы прочитать ввод в виде строки и проанализировать соответствующее значение int.

person Joseph Mansfield    schedule 18.04.2013
comment
хорошо, это объясняет это ха-ха. Большое спасибо за вашу помощь. - person KingKong; 18.04.2013

Вот мой ответ, основанный на комментарии sftrabbit:

#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

int main(){

  string str1;
  cout << "enter number:";
  cin  >> str1;

  //READ AS A STRING AND REMOVE '.'

  str1.erase(std::remove(str1.begin(), str1.end(), '.'), str1.end());

  //CAST STRING TO VALUE
  int value = atoi(str1.c_str());

  //LOOP THROUGH THE PROCESS YOUR WERE DOING BEFORE
  int divisor = 10;
  int  counter = 1;
  while(divisor < 10*value){

    cout << "Digit " << counter << " = " << value%divisor/(divisor/10) << endl;
    divisor *=10;
    counter++;
  }

  return 0;
}
person James    schedule 18.04.2013

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

Существуют недоработанные решения, такие как floor (number * 100 + 0.5), но они по-прежнему страдают от ошибок округления в особых случаях и/или могут давать разные результаты на разных платформах.

В случае валют с ограниченным десятичным числом я бы не стал использовать числа с плавающей запятой и возиться с ними - просто прочитайте ввод в виде строки и в первую очередь правильно проанализируйте его, опуская все, что связано с float/double и их проблемами. Они просто не являются правильным типом данных для работы.

person Arne Mertz    schedule 18.04.2013

чтобы извлечь цифру одну за другой, вы можете сделать это: int_number = number * 100;

hundreds = int_number / 10000;
tens = (int_number - hundreds*10000) / 1000;
ones = (int_number - hundreds*10000 - tens * 1000) / 100
decimal1= (int_number - hundreds*10000 - tens * 1000 - ones * 100) / 10;
decimal2= int_number - hundreds*10000 - tens * 1000 - ones * 100 - decimal1 * 10;
person Nagasaki    schedule 18.04.2013
comment
я не думаю, что есть проблема с плавающей точностью для значения с 2 цифрами после запятой - person Nagasaki; 18.04.2013
comment
Уверяю вас, что есть. 2 цифры в степенях 10 могут быть бесконечными периодическими числами в степенях 2. 321,78 равно 321,7799987792968750... как число с плавающей запятой и 321,77999999999999727... как двойное. - person Arne Mertz; 18.04.2013

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

int_number = number * 100;

просто удаляет последние цифры и не округляет. пытаться

int_number = static_cast<int> (floor (number * 100 + 0.5f))

вместо.

person eel76    schedule 18.04.2013
comment
так что это проблема точности, потому что мое выражение не округляло числа должным образом? не могли бы вы также помочь объяснить, почему +0,5? - person KingKong; 18.04.2013
comment
@KingKong трюк с 0,5 может срабатывать часто, но не во всех случаях. Все еще будут случаи, когда вы ошибаетесь на одну цифру. Просто не используйте числа с плавающей запятой для вычислений с фиксированной точкой. - person Arne Mertz; 18.04.2013