Depois de olhar para todas as respostas, acho que a resposta curta é não? :)
Marsellus Wallace
4
@Gevorg Você me fez rir. Eu sou novo no Scala por causa de outras linguagens pesadas em números, e meu queixo quase caiu no chão lendo este tópico. Este é um estado de coisas insano para uma linguagem de programação.
Muito provável, eu diria. Qualquer coisa que envolva redes ou finanças pode exigir arredondamento e também desempenho.
Rex Kerr
27
Suponho que há pessoas para quem uma longa chamada para uma biblioteca desajeitada é mais compreensível do que a simples matemática. Eu recomendo "%.2f".format(x).toDoublenesse caso. Apenas 2x mais lento e você só precisa usar uma biblioteca que já conhece.
Rex Kerr
6
@RexKerr, você não está arredondando neste caso .. simplesmente truncando.
@RexKerr Eu prefiro seu modo string.format, mas em locales sa mine (finlandês), deve-se tomar cuidado para corrigir o locale ROOT. Por exemplo, "% .2f" .formatLocal (java.util.Locale.ROOT, x) .toDouble. Parece que o formato usa ',' por causa do local, enquanto toDouble não é capaz de aceitá-lo e lança uma NumberFormatException. É claro que isso se baseia em onde seu código está sendo executado , não onde ele é desenvolvido.
akauppi
77
Aqui está outra solução sem BigDecimals
Truncar:
(math floor 1.23456789*100)/100
Volta:
(math rint 1.23456789*100)/100
Ou para qualquer duplo n e precisão p:
def truncateAt(n:Double, p:Int):Double={val s = math pow (10, p);(math floor n * s)/ s }
Semelhante pode ser feito para a função de arredondamento, desta vez usando currying:
def roundAt(p:Int)(n:Double):Double={val s = math pow (10, p);(math round n * s)/ s }
que é mais reutilizável, por exemplo, ao arredondar valores em dinheiro, o seguinte pode ser usado:
roundAt2 deve ser def roundAt2 (n: Double) = roundAt (2) (n) no?
C4stor de
isso parece retornar um resultado incorreto para NaN, não é?
jangorecki
o problema com flooré que truncateAt(1.23456789, 8)retornará 1.23456788enquanto roundAt(1.23456789, 8)retornará o valor correto de1.23456789
Todor Kolev
35
Como ninguém mencionou a %operadora ainda, aí vem. Ele apenas faz o truncamento e você não pode contar com o valor de retorno para não ter imprecisões de ponto flutuante, mas às vezes é útil:
Não recomendaria isso, embora seja minha própria resposta: os mesmos problemas de imprecisão mencionados por @ryryguy no comentário de outra resposta afetam aqui também. Use string.format com a localidade Java ROOT (comentarei sobre isso lá).
akauppi
isso é perfeito se você precisa apenas renderizar o valor e nunca usá-lo em operações subsequentes. obrigado
Alexander Arendar
3
aqui está algo engraçado: 26.257391515826225 - 0.057391515826223094 = 26.200000000000003
kubudi
12
E se :
val value =1.4142135623730951//3 decimal places
println((value *1000).round /1000.toDouble)//4 decimal places
println((value *10000).round /10000.toDouble)
solução muito limpa. Aqui está o meu truncamento: ((1.949 * 1000).toInt - ((1.949 * 1000).toInt % 10)) / 1000.toDoublemas não testei muito. Este código faria 2 casas decimais.
robert
7
Edit: corrigido o problema que @ryryguy apontou. (Obrigado!)
Se você quer que seja rápido, Kaito tem a ideia certa. math.powé lento, no entanto. Para qualquer uso padrão, é melhor você usar uma função recursiva:
def trunc(x:Double, n:Int)={def p10(n:Int, pow:Long=10):Long=if(n==0) pow else p10(n-1,pow*10)if(n <0){val m = p10(-n).toDouble
math.round(x/m)* m
}else{val m = p10(n).toDouble
math.round(x*m)/ m
}}
Isso é cerca de 10x mais rápido se você estiver na faixa de Long(ou seja, 18 dígitos), então você pode arredondar em qualquer lugar entre 10 ^ 18 e 10 ^ -18.
Cuidado, multiplicar pelo recíproco não funciona de forma confiável, porque pode não ser representável de forma confiável como um duplo: scala> def r5(x:Double) = math.round(x*100000)*0.000001; r5(0.23515)==> res12: Double = 0.023514999999999998. Em vez disso, divida pelo significado:math.round(x*100000)/100000.0
ryryguy
Também pode ser útil substituir a p10função recursiva por uma pesquisa de array: o array aumentará o consumo de memória em cerca de 200 bytes, mas provavelmente salvará várias iterações por chamada.
Levi Ramsey
4
Você pode usar classes implícitas:
import scala.math._
objectExtNumberextendsApp{implicitclassExtendedDouble(n:Double){def rounded(x:Int)={val w = pow(10, x)(n * w).toLong.toDouble / w
}}// usageval a =1.23456789
println(a.rounded(2))}
O truncamento é o mais rápido, seguido por BigDecimal. Lembre-se de que esses testes foram feitos executando a execução do norma scala, sem usar nenhuma ferramenta de benchmarking.
objectTestFormatters{val r = scala.util.Randomdef textFormatter(x:Double)=new java.text.DecimalFormat("0.##").format(x)def scalaFormatter(x:Double)="$pi%1.2f".format(x)def bigDecimalFormatter(x:Double)=BigDecimal(x).setScale(2,BigDecimal.RoundingMode.HALF_UP).toDouble
def scalaCustom(x:Double)={val roundBy =2val w = math.pow(10, roundBy)(x * w).toLong.toDouble / w
}def timed(f:=>Unit)={val start =System.currentTimeMillis()
f
val end =System.currentTimeMillis()
println("Elapsed Time: "+(end - start))}def main(args:Array[String]):Unit={
print("Java Formatter: ")val iters =10000
timed {(0 until iters) foreach { _ =>
textFormatter(r.nextDouble())}}
print("Scala Formatter: ")
timed {(0 until iters) foreach { _ =>
scalaFormatter(r.nextDouble())}}
print("BigDecimal Formatter: ")
timed {(0 until iters) foreach { _ =>
bigDecimalFormatter(r.nextDouble())}}
print("Scala custom Formatter (truncation): ")
timed {(0 until iters) foreach { _ =>
scalaCustom(r.nextDouble())}}}}
Caro scalaCustom não está terminando, está apenas truncando
Ravinder Payal
hmm, OP não era específico para arredondamento ou truncamento; ...truncate or round a Double.
cevaris
Mas, na minha opinião, comparar a velocidade / tempo de execução da função de truncamento com as funções de arredondamento é inadequado. É por isso que pedi para você esclarecer ao leitor que o recurso personalizado apenas trunca. E a função truncar / customizada mencionada por você pode ser simplificada ainda mais. val doubleParts = double. toString.split (".") Agora pegue os primeiros dois caracteres de doubleParts.taile concat com as strings "." and doubleParts. headand parse para dobrar.
Ravinder Payal
1
atualizado, parece melhor? também sua sugestão toString.split(".")e doubleParts.head/tailsugestão podem sofrer de alocação de array extra mais concatenação de string. precisaria testar para ter certeza.
cevaris
3
Recentemente, enfrentei um problema semelhante e resolvi usando a seguinte abordagem
Eu usei esse problema do SO. Eu tenho algumas funções sobrecarregadas para opções Float \ Double e implícita \ explícita. Observe que você precisa mencionar explicitamente o tipo de retorno no caso de funções sobrecarregadas.
Eu não usaria BigDecimal se você se preocupa com desempenho. BigDecimal converte números em string e, em seguida, analisa novamente:
/** Constructs a `BigDecimal` using the decimal text representation of `Double` value `d`, rounding if necessary. */def decimal(d:Double, mc:MathContext):BigDecimal=newBigDecimal(newBigDec(java.lang.Double.toString(d), mc), mc)
Vou me limitar às manipulações matemáticas, conforme sugerido por Kaito .
Você pode fazer: Math.round(<double precision value> * 100.0) / 100.0
Mas Math.round é mais rápido, mas se decompõe mal em casos de canto com um número muito alto de casas decimais (por exemplo, redondo (1000.0d, 17)) ou parte inteira grande (por exemplo, redondo (90080070060.1d, 9) )
Use Bigdecimal, é um pouco ineficiente, pois converte os valores em string, mas mais alívio:
BigDecimal(<value>).setScale(<places>, RoundingMode.HALF_UP).doubleValue()
use o modo de arredondamento de sua preferência.
Se você estiver curioso e quiser saber mais detalhes por que isso acontece, você pode ler isto:
Respostas:
Você pode usar
scala.math.BigDecimal
:Existem vários outros modos de arredondamento , que infelizmente não estão muito bem documentados no momento (embora seus equivalentes em Java estejam ).
fonte
"%.2f".format(x).toDouble
nesse caso. Apenas 2x mais lento e você só precisa usar uma biblioteca que já conhece.scala> "%.2f".format(0.714999999999).toDouble
res13: Double = 0.71
masscala> "%.2f".format(0.715).toDouble
res14: Double = 0.72
.Aqui está outra solução sem BigDecimals
Truncar:
Volta:
Ou para qualquer duplo n e precisão p:
Semelhante pode ser feito para a função de arredondamento, desta vez usando currying:
que é mais reutilizável, por exemplo, ao arredondar valores em dinheiro, o seguinte pode ser usado:
fonte
NaN
, não é?floor
é quetruncateAt(1.23456789, 8)
retornará1.23456788
enquantoroundAt(1.23456789, 8)
retornará o valor correto de1.23456789
Como ninguém mencionou a
%
operadora ainda, aí vem. Ele apenas faz o truncamento e você não pode contar com o valor de retorno para não ter imprecisões de ponto flutuante, mas às vezes é útil:fonte
26.257391515826225 - 0.057391515826223094 = 26.200000000000003
E se :
fonte
((1.949 * 1000).toInt - ((1.949 * 1000).toInt % 10)) / 1000.toDouble
mas não testei muito. Este código faria 2 casas decimais.Edit: corrigido o problema que @ryryguy apontou. (Obrigado!)
Se você quer que seja rápido, Kaito tem a ideia certa.
math.pow
é lento, no entanto. Para qualquer uso padrão, é melhor você usar uma função recursiva:Isso é cerca de 10x mais rápido se você estiver na faixa de
Long
(ou seja, 18 dígitos), então você pode arredondar em qualquer lugar entre 10 ^ 18 e 10 ^ -18.fonte
scala> def r5(x:Double) = math.round(x*100000)*0.000001; r5(0.23515)
==>res12: Double = 0.023514999999999998
. Em vez disso, divida pelo significado:math.round(x*100000)/100000.0
p10
função recursiva por uma pesquisa de array: o array aumentará o consumo de memória em cerca de 200 bytes, mas provavelmente salvará várias iterações por chamada.Você pode usar classes implícitas:
fonte
Para os interessados, aqui ficam alguns horários para as soluções sugeridas ...
O truncamento é o mais rápido, seguido por BigDecimal. Lembre-se de que esses testes foram feitos executando a execução do norma scala, sem usar nenhuma ferramenta de benchmarking.
fonte
...truncate or round a Double
.doubleParts.tail
e concat com as strings "." anddoubleParts. head
and parse para dobrar.toString.split(".")
edoubleParts.head/tail
sugestão podem sofrer de alocação de array extra mais concatenação de string. precisaria testar para ter certeza.Recentemente, enfrentei um problema semelhante e resolvi usando a seguinte abordagem
Eu usei esse problema do SO. Eu tenho algumas funções sobrecarregadas para opções Float \ Double e implícita \ explícita. Observe que você precisa mencionar explicitamente o tipo de retorno no caso de funções sobrecarregadas.
fonte
Na verdade, é muito fácil de manusear usando o
f
interpolador Scala - https://docs.scala-lang.org/overviews/core/string-interpolation.htmlSuponha que queremos arredondar até 2 casas decimais:
fonte
Eu não usaria BigDecimal se você se preocupa com desempenho. BigDecimal converte números em string e, em seguida, analisa novamente:
Vou me limitar às manipulações matemáticas, conforme sugerido por Kaito .
fonte
Um pouco estranho, mas legal. Eu uso String e não BigDecimal
fonte
Você pode fazer:
Math.round(<double precision value> * 100.0) / 100.0
Mas Math.round é mais rápido, mas se decompõe mal em casos de canto com um número muito alto de casas decimais (por exemplo, redondo (1000.0d, 17)) ou parte inteira grande (por exemplo, redondo (90080070060.1d, 9) )Use Bigdecimal, é um pouco ineficiente, pois converte os valores em string, mas mais alívio:
BigDecimal(<value>).setScale(<places>, RoundingMode.HALF_UP).doubleValue()
use o modo de arredondamento de sua preferência.Se você estiver curioso e quiser saber mais detalhes por que isso acontece, você pode ler isto:
fonte