Как отмечали некоторые другие, правильным ответом является использование либо DecimalFormat
, либо BigDecimal
. Плавающая точка не имеет десятичных мест , поэтому вы не можете округлить / усечь до определенного числа из них в первую очередь. Вы должны работать в десятичном радиксе, и это то, что делают эти два класса.
Я отправляю следующий код в качестве встречного примера для всех ответов в этом потоке и, действительно, по всему StackOverflow (и в другом месте), которые рекомендуют умножение с последующим усечением, за которым следует разделение. Для сторонников этой методики необходимо объяснить, почему следующий код производит неправильный вывод в более чем 92% случаев.
public class RoundingCounterExample
{
static float roundOff(float x, int position)
{
float a = x;
double temp = Math.pow(10.0, position);
a *= temp;
a = Math.round(a);
return (a / (float)temp);
}
public static void main(String[] args)
{
float a = roundOff(0.0009434f,3);
System.out.println("a="+a+" (a % .001)="+(a % 0.001));
int count = 0, errors = 0;
for (double x = 0.0; x < 1; x += 0.0001)
{
count++;
double d = x;
int scale = 2;
double factor = Math.pow(10, scale);
d = Math.round(d * factor) / factor;
if ((d % 0.01) != 0.0)
{
System.out.println(d + " " + (d % 0.01));
errors++;
}
}
System.out.println(count + " trials " + errors + " errors");
}
}
Вывод этой программы:
10001 trials 9251 errors
EDIT: Чтобы ответить на некоторые комментарии ниже, я отредактирую часть модуля тестового цикла с помощью BigDecimal
и new MathContext(16)
для работы модуля следующим образом:
public static void main(String[] args)
{
int count = 0, errors = 0;
int scale = 2;
double factor = Math.pow(10, scale);
MathContext mc = new MathContext(16, RoundingMode.DOWN);
for (double x = 0.0; x < 1; x += 0.0001)
{
count++;
double d = x;
d = Math.round(d * factor) / factor;
BigDecimal bd = new BigDecimal(d, mc);
bd = bd.remainder(new BigDecimal("0.01"), mc);
if (bd.multiply(BigDecimal.valueOf(100)).remainder(BigDecimal.ONE, mc).compareTo(BigDecimal.ZERO) != 0)
{
System.out.println(d + " " + bd);
errors++;
}
}
System.out.println(count + " trials " + errors + " errors");
}
Результат:
10001 trials 4401 errors
Вероятно, вы просто получаете переполнение стека. Массив слишком велик, чтобы вставлять адресное пространство стека вашей программы.
Если вы выделяете массив в куче, вы должны быть в порядке, если у вас достаточно памяти.
int* array = new int[1000000];
Но помните, что это потребует от вас delete[]
массива. Лучшим решением было бы использовать std::vector<int>
и изменить его размер до 1000000 элементов.
В C или C ++ локальные объекты обычно выделяются в стеке. Вы выделяете большой массив в стеке, больше, чем может обрабатывать стек, поэтому вы получаете stackoverflow.
Не выделяйте его локально в стеке, используйте некоторые другие вместо этого. Этого можно достичь либо путем создания объекта global , либо выделения его в глобальной куче . Глобальные переменные прекрасны, если вы не используете их из любой другой единицы компиляции. Чтобы убедиться, что это не происходит случайно, добавьте спецификатор статического хранилища, иначе просто используйте кучу.
Это будет выделяться в сегменте BSS, который является частью кучи:
static int c[1000000];
int main()
{
cout << "done\n";
return 0;
}
Это будет выделяться в сегменте DATA, который также является частью кучи:
int c[1000000] = {};
int main()
{
cout << "done\n";
return 0;
}
Это будет выделяться в каком-то неуказанном месте в куче:
int main()
{
int* c = new int[1000000];
cout << "done\n";
return 0;
}
delete
, которую вы выделяете с помощью new
. Но если вы уверены, что вы выделяете память только один раз (как в основном), она строго не нужна - гарантируется, что память будет освобождена при выходе из основного, даже без явного delete
.
– Gunther Piez
5 September 2012 в 09:11
Поскольку вы храните массив в стеке. Вы должны хранить его в куче. См. эту ссылку , чтобы понять концепцию кучи и стека.
Ваш массив выделяется в стеке, в этом случае попытайтесь выделить массив того же размера с помощью alloc.
Кроме того, если вы работаете в большинстве UNIX & amp; Linux, вы можете временно увеличить размер стека с помощью следующей команды:
ulimit -s unlimited
Но будьте осторожны, память - это ограниченный ресурс и с большой силой приходят большие обязанности:)