2014-10-11 4 views
1

Я пишу функцию для класса, которая берет сумму денег и сообщает пользователю, сколько монет добавляется к этой сумме. Кажется, у меня все работает нормально, кроме пенни. Моя петля иногда останавливается и прерывается, прежде чем добавлять правильную сумму. Обычно он останавливается на 1 пенни, но иногда он дает мне правильный ответ (одно значение, которое я нашел, дает правильный ответ .09). Я попытался изменить float на double, и у меня такая же проблема. Я вытаскиваю свои волосы, пытаясь понять, что я делаю неправильно.Почему моя петля ломается раньше?

void change(float total) 
{ 
int quarters, dimes, nickels, pennies; 
quarters = 0; 
dimes = 0; 
nickels = 0; 
pennies = 0; 

printf("\nTotal value entered: %.2f", total); 

while (total >= .25) 
{ 
    quarters += 1; 
    total -= .25; 
} 
while (total >= .10) 
{ 
    dimes += 1; 
    total -= .10; 
} 
while (total >= .05) 
{ 
    nickels += 1; 
    total -= .05; 
} 
while (total >= .01) 
{ 
    pennies += 1; 
    total -= .01; 
} 
printf("\nQuarters: %d \nDimes: %d \nNickels: %d \nPennies: %d\n\n", quarters, dimes, nickels, pennies); 

} 
+0

Вы можете сказать нам, для которых значения 'total' является проблема воспроизводится, пожалуйста? –

+1

Множество десятичных чисел (включая 0,1, 0,05 и 0,01) не точно представлены в плавающей точке, а ошибки складываются. –

+0

Это может быть связано с точностью 'float'. То, что вы можете сделать, - это обрабатывать деньги в «центах», а не «долларах». – wendelbsilva

ответ

6

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

Возможно, вы обнаружите, что достигли точки, где оставшееся значение - это что-то вроде 0.009999942, а не 0.1, и именно поэтому вы уходите рано.

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

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

Вы можете сделать это с чем-то вроде:

int itotal = total * 100 + 0.2; 

затем с помощью itotal для расчетов:

while (itotal >= 25) { 
    quarters++; 
    itotal -= 25; 
} 
// and so on ... 
+4

Урок Учимся: никогда не используйте с плавающей точкой деньги. – Deduplicator

+2

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

+1

Это сработало отлично. Спасибо! – Gotz

0

Математика с плавающей точкой не является точной. См. http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

+3

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

+1

@ArmenTsirunyan: научите человека ловить рыбу, кричать в Интернете! –

+0

Мне нравится один из Discworld (я думаю): дайте человеку огонь, он будет теплым на всю ночь, поджег его, он будет теплым до конца своей жизни :-) – paxdiablo

0

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

1

У меня был подобный вопрос некоторое время назад для одной из моих лабораторий. Вместо цикла while для каждой номинальной монеты, у меня был один ... с каскадированными if-операторами. В моем случае максимальная стоимость элемента составляла $ 1, и я решил работать в ints, но вы можете отформатировать окончательный результат позже.

int price, remainder, quarters, dime, nickel, pennies; 

printf("Enter the price of the item you bought:>"); 
scanf("%d", &price); 

remainder = 100 - price; 

do { 
    if (remainder >= 25) 
    { 
     quarters++; 
     remainder -= 25; 
    } 

    else if (remainder >= 10) 
    { 
     dime++; 
     remainder -= 10; 
    } 

    else if (remainder >= 5) 
    { 
     nickel ++; 
     remainder -=5; 
    } 
    else if (remainder >= 1) 
    { 
     pennies ++; 
     remainder -=1; 
    } 
} while (remainder > 0); 

printf("\nYour change will be dispensed as:\n Quarters: %d \n Dimes: %d \n Nickel: %d \n Pennies: %d \n\n", quarters, dime, nickel,pennies); 

Надеюсь, это поможет.

0

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

int_total = round(total * 100.0 );   //include math.h 

Теперь измените цикл, как,

while (total >= 25) 
{ 
    quarters += 1; 
    total -= 25; 
} 
Смежные вопросы