2009-06-15 4 views
9

Я использую следующий код для округления до 2DP:Округление удваивается - 0,5 - Sprintf

sprintf(temp,"%.2f",coef[i]); //coef[i] returns a double 

Он успешно завершает 6,666 до 6,67, но он не работает должным образом, когда округление 5.555. Он возвращает 5.55, тогда как он должен (по крайней мере, на мой взгляд) вернуться 5.56.

Как его получить, чтобы округлить, когда следующая цифра равна 5? т. е. вернуть 5.56.

Редактировать: Теперь я понимаю, что это происходит, потому что, когда я вхожу в 5.555 с cin, он получает , сохраненный как 5.554999997.

Я собираюсь попробовать округлить в два этапа - сначала до 3dp, а затем до 2dp. любые другие (более элегантные) идеи?

ответ

11

Кажется, вам нужно использовать функцию математического округления для правильного округления.

printf("%.2f %.2f\n", 5.555, round(5.555 * 100.)/100.); 

Это дает следующий вывод на моей машине:

5.55 5.56 
+0

Это кажется немного более надежным, чем идея добавления 0,0005, но и будет работать, я думаю. Спасибо за вашу помощь! – Meir

+0

На самом деле это ошибка. 5.5551 округляется правильно! – ypnos

+6

Это не багги, @ypnos, вы просто еще не выходите за пределы плавающей запятой. 5.5551 представлен как 5.555100000000000370 ... который будет округлен, 5.555 представлен как 5.5549999999 ... который округляется вниз. Вы можете увидеть, что это проблема с представлением, попробовав свой код с 5.5500000000000000001 - оба выхода являются неправильным значением 5.55 в этом случае, потому что число IS NOT 5.55000 ... 1 при его сохранении. Это 5.549999 ... – paxdiablo

11

Число 5.555 не может быть представлено как точное число в IEEE754. Распечатка постоянных 5.555 с "%.50f" результатов:

5.55499999999999971578290569595992565155029300000000 

так будет округляются вниз. Попробуйте использовать вместо этого:

printf ("%.2f\n",x+0.0005); 

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

Вам нужно понять ограничения представления с плавающей точкой. Если важно, чтобы вы получали точность, вы можете использовать (или код) BCD или другой десятичный класс, который не имеет недостатка в представлении IEEE754.

+0

Это не влияет на ваш ответ, но мне было просто интересно: на каком языке вы использовали для печати 5.55499999999999971578290569595992565155029300000000? Это на самом деле должно быть 5.55499999999999971578290569595992565155029296875000 –

+0

@Rick, скорее всего, gcc под CygWin, но его трудно запомнить год назад :-) Я получаю тот же результат, что и в Ubuntu10. Но я думаю, что удвоение гарантирует только 15 десятичных цифр точности, поэтому может использоваться более широкий внутренний формат (у меня есть смутное воспоминание о том, что вычисления по крайней мере одной системе использовали 80 битов внутри, но это было давно - получить 48 десятичные цифры, как с вашим номером, вам потребуется около 160 бит точности). – paxdiablo

+0

ОК, спасибо. Я знал, что это не может быть Visual C++ (он не может точно напечатать много цифр), и у него, похоже, слишком мало цифр для gcc/glibc в Linux (что позволяет печатать их все). В любом случае вы можете получить столько цифр со стандартным двойным (53 бита) - это точное десятичное представление того, что делает двойной (даже если это может быть только 15-разрядное приближение того, что ему было присвоено). –

-2

Вы также можете сделать это (сейв умножить/разделить):

printf("%.2f\n", coef[i] + 0.00049999); 
+0

Это будет раунд от 5.549999 до 5.56 ;-) – ypnos

+0

@ypnos, вам действительно нужно проверять вещи перед отправкой - это НЕ дает вам 5.56. – paxdiablo

+1

Извините заблуждение. Вы все равно должны видеть мою мысль. 5.544999 + 0.0005 округляется до 5.55. Он должен быть четко округлен до 5,54. Не принимает наука о ракетах, чтобы понять, что добавление предвзятости не будет загадочно исправлять проблему округления без введения новых. – ypnos

1

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

2

Как об этом для другого возможного решения:

printf("%.2f", _nextafter(n, n*2)); 

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

Например:

double n=5.555; 
printf("%.2f\n", n); 
printf("%.2f\n", _nextafter(n, n*2)); 
printf("%.20f\n", n); 
printf("%.20f\n", _nextafter(n, n*2)); 

С выходами MSVC:

5.55 
5.56 
5.55499999999999970000 
5.55500000000000060000 
Смежные вопросы