Estou realmente muito surpreso por não ter encontrado a resposta para isso aqui, embora talvez esteja apenas usando os termos de pesquisa incorretos ou algo assim. O mais próximo que pude encontrar é esse , mas eles perguntam sobre a geração de um intervalo específico de double
s com um tamanho de etapa específico e as respostas o tratam como tal. Preciso de algo que gere os números com tamanho arbitrário de início, fim e etapa.
Eu acho que já deve haver algum método como esse em uma biblioteca em algum lugar, mas se assim não for possível encontrá-lo facilmente (novamente, talvez eu esteja apenas usando os termos de pesquisa incorretos ou algo assim). Então, aqui está o que eu cozinhei por conta própria nos últimos minutos para fazer isso:
import java.lang.Math;
import java.util.List;
import java.util.ArrayList;
public class DoubleSequenceGenerator {
/**
* Generates a List of Double values beginning with `start` and ending with
* the last step from `start` which includes the provided `end` value.
**/
public static List<Double> generateSequence(double start, double end, double step) {
Double numValues = (end-start)/step + 1.0;
List<Double> sequence = new ArrayList<Double>(numValues.intValue());
sequence.add(start);
for (int i=1; i < numValues; i++) {
sequence.add(start + step*i);
}
return sequence;
}
/**
* Generates a List of Double values beginning with `start` and ending with
* the last step from `start` which includes the provided `end` value.
*
* Each number in the sequence is rounded to the precision of the `step`
* value. For instance, if step=0.025, values will round to the nearest
* thousandth value (0.001).
**/
public static List<Double> generateSequenceRounded(double start, double end, double step) {
if (step != Math.floor(step)) {
Double numValues = (end-start)/step + 1.0;
List<Double> sequence = new ArrayList<Double>(numValues.intValue());
double fraction = step - Math.floor(step);
double mult = 10;
while (mult*fraction < 1.0) {
mult *= 10;
}
sequence.add(start);
for (int i=1; i < numValues; i++) {
sequence.add(Math.round(mult*(start + step*i))/mult);
}
return sequence;
}
return generateSequence(start, end, step);
}
}
Esses métodos executam um loop simples multiplicando step
pelo índice de sequência e adicionando ao start
deslocamento. Isso reduz os erros de composição de ponto flutuante que ocorreriam com incrementos contínuos (como adicionar a step
uma variável em cada iteração).
Adicionei o generateSequenceRounded
método para os casos em que um tamanho de etapa fracionário pode causar erros perceptíveis de ponto flutuante. Requer um pouco mais de aritmética, portanto, em situações extremamente sensíveis ao desempenho como a nossa, é bom ter a opção de usar o método mais simples quando o arredondamento é desnecessário. Eu suspeito que, na maioria dos casos de uso geral, a sobrecarga de arredondamento seria insignificante.
Nota que eu intencionalmente excluídos lógica para lidar com argumentos "anormais", como Infinity
, NaN
, start
> end
, ou um negativo step
tamanho para simplicidade e desejam focar a questão em apreço.
Aqui estão alguns exemplos de uso e saída correspondente:
System.out.println(DoubleSequenceGenerator.generateSequence(0.0, 2.0, 0.2))
System.out.println(DoubleSequenceGenerator.generateSequenceRounded(0.0, 2.0, 0.2));
System.out.println(DoubleSequenceGenerator.generateSequence(0.0, 102.0, 10.2));
System.out.println(DoubleSequenceGenerator.generateSequenceRounded(0.0, 102.0, 10.2));
[0.0, 0.2, 0.4, 0.6000000000000001, 0.8, 1.0, 1.2000000000000002, 1.4000000000000001, 1.6, 1.8, 2.0]
[0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0]
[0.0, 10.2, 20.4, 30.599999999999998, 40.8, 51.0, 61.199999999999996, 71.39999999999999, 81.6, 91.8, 102.0]
[0.0, 10.2, 20.4, 30.6, 40.8, 51.0, 61.2, 71.4, 81.6, 91.8, 102.0]
Existe uma biblioteca existente que já fornece esse tipo de funcionalidade?
Caso contrário, há algum problema com minha abordagem?
Alguém tem uma abordagem melhor para isso?