Есть ли функция, которая может обрезать или округлять Double? В какой-то момент в моем коде я хотел бы, чтобы число вроде: 1.23456789было округлено до1.23
Посмотрев на все ответы, я предполагаю, что короткий ответ - нет? :)
Марселлус Уоллес
4
@Gevorg Ты меня рассмешил. Я новичок в Scala из других языков с большим количеством чисел, и моя челюсть почти упала на пол, читая эту ветку. Это безумное положение вещей для языка программирования.
Я бы сказал, что вполне вероятно. Все, что связано с сетями или финансами, может потребовать округления, а также производительности.
Рекс Керр
28
Полагаю, есть люди, для которых долгий вызов неуклюжей библиотеки более понятен, чем простая математика. Я рекомендую "%.2f".format(x).toDoubleв таком случае. Только в 2 раза медленнее, и вам нужно использовать только уже знакомую библиотеку.
Рекс Керр
6
@RexKerr, в этом случае вы не округляете .. просто усекаете.
@RexKerr Я предпочитаю использовать ваш вариант string.format, но в других (финских) языках нужно позаботиться о том, чтобы исправить локаль ROOT. Например, "% .2f" .formatLocal (java.util.Locale.ROOT, x) .toDouble. Похоже, что формат использует ',' из-за локали, тогда как toDouble не может принять его и выдает исключение NumberFormatException. Это, конечно , основывается на том, где ваш код в настоящее время работать не там , где это развито.
akauppi
77
Вот еще одно решение без BigDecimals
Обрезание:
(math floor 1.23456789*100)/100
Круглый:
(math rint 1.23456789*100)/100
Или для любого двойного n и точности p:
def truncateAt(n:Double, p:Int):Double={val s = math pow (10, p);(math floor n * s)/ s }
То же самое можно сделать и с функцией округления, на этот раз с использованием каррирования:
def roundAt(p:Int)(n:Double):Double={val s = math pow (10, p);(math round n * s)/ s }
который является более многоразовым, например, при округлении денежных сумм можно использовать следующее:
roundAt2 должно быть def roundAt2 (n: Double) = roundAt (2) (n) no?
C4stor 08
похоже, это возвращает неверный результат NaN, не так ли?
jangorecki
проблема в floorтом, что truncateAt(1.23456789, 8)вернет, 1.23456788а roundAt(1.23456789, 8)вернет правильное значение1.23456789
Тодор Колев
35
Так как никто еще не упомянул %оператора, вот оно. Он выполняет только усечение, и вы не можете полагаться на возвращаемое значение, чтобы не иметь неточностей с плавающей запятой, но иногда это удобно:
Не рекомендую это, хотя это мой собственный ответ: здесь также сказываются те же проблемы с неточностью, которые упоминались @ryryguy в другом комментарии ответа. Используйте string.format с локалью Java ROOT (я прокомментирую это там).
akauppi
это идеально, если вам просто нужно отобразить значение и никогда не использовать его в последующих операциях. спасибо
Александр Арендар 03
3
вот что-то смешное: 26.257391515826225 - 0.057391515826223094 = 26.200000000000003
kubudi 09
12
Как насчет :
val value =1.4142135623730951//3 decimal places
println((value *1000).round /1000.toDouble)//4 decimal places
println((value *10000).round /10000.toDouble)
довольно чистое решение. Вот мой для усечения: ((1.949 * 1000).toInt - ((1.949 * 1000).toInt % 10)) / 1000.toDoubleхотя и не слишком много тестировал. В этом коде будет два десятичных знака.
Если вы хотите, чтобы все было быстро, у Кайто есть правильная идея. math.powхотя медленный. Для любого стандартного использования вам лучше использовать рекурсивную функцию:
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
}}
Это примерно в 10 раз быстрее, если вы находитесь в пределах диапазона Long(т.е. 18 цифр), поэтому вы можете округлить до любого значения от 10 ^ 18 до 10 ^ -18.
Будьте осторожны, умножение на обратное не работает надежно, потому что оно не может быть надежно представлено как двойное: scala> def r5(x:Double) = math.round(x*100000)*0.000001; r5(0.23515)==> res12: Double = 0.023514999999999998. Вместо этого разделите по значимости:math.round(x*100000)/100000.0
ryguy
Также может быть полезно заменить рекурсивную p10функцию поиском по массиву: массив увеличит потребление памяти примерно на 200 байт, но, вероятно, сэкономит несколько итераций на вызов.
Леви Рэмси
4
Вы можете использовать неявные классы:
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))}
Усечение является самым быстрым, за ним следует BigDecimal. Имейте в виду, что этот тест был выполнен при выполнении norma scala, без использования каких-либо инструментов тестирования.
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())}}}}
Уважаемый scalaCustom не округление, это просто усечения
Ravinder Payal
хм, OP не предназначен для округления или усечения; ...truncate or round a Double,
cevaris 08
Но, на мой взгляд, сравнение скорости / времени выполнения функции усечения с функциями округления неадекватно. Вот почему я просил вас объяснить читателю, что пользовательская функция только усекает. Упомянутая вами функция truncate / custom может быть дополнительно упрощена. val doubleParts = двойной. toString.split (".") Теперь получите первые два символа doubleParts.tailи объедините их со строками "." и doubleParts. headи разобрать на удвоение.
Ravinder Payal
1
обновили, лучше посмотрите? также ваше предложение toString.split(".")и doubleParts.head/tailпредложение могут пострадать от дополнительного выделения массива плюс конкатенации строк. для уверенности потребуется тестирование.
cevaris
3
Недавно я столкнулся с подобной проблемой и решил ее, используя следующий подход
Я использовал эту проблему SO. У меня есть несколько перегруженных функций для параметров Float \ Double и implicit \ explicit. Обратите внимание, что вам нужно явно указать тип возвращаемого значения в случае перегруженных функций.
Я бы не стал использовать BigDecimal, если вам важна производительность. BigDecimal преобразует числа в строку, а затем снова анализирует ее:
/** 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)
Я собираюсь придерживаться математических манипуляций, как предложил Кайто .
Вы можете сделать: Math.round(<double precision value> * 100.0) / 100.0
Но Math.round самый быстрый, но он плохо работает в угловых случаях с очень большим количеством десятичных знаков (например, round (1000.0d, 17)) или большой целой частью (например, round (90080070060.1d, 9) ).
Используйте Bigdecimal, это немного неэффективно, так как преобразует значения в строку, но более средневековое:
BigDecimal(<value>).setScale(<places>, RoundingMode.HALF_UP).doubleValue()
используйте свой предпочтительный режим округления.
Если вам интересно и вы хотите узнать более подробно, почему это происходит, вы можете прочитать это:
Ответы:
Вы можете использовать
scala.math.BigDecimal
:Существует ряд других режимов округления , которые, к сожалению, в настоящее время не очень хорошо документированы (хотя их эквиваленты в Java есть ).
источник
"%.2f".format(x).toDouble
в таком случае. Только в 2 раза медленнее, и вам нужно использовать только уже знакомую библиотеку.scala> "%.2f".format(0.714999999999).toDouble
res13: Double = 0.71
ноscala> "%.2f".format(0.715).toDouble
res14: Double = 0.72
.Вот еще одно решение без BigDecimals
Обрезание:
Круглый:
Или для любого двойного n и точности p:
То же самое можно сделать и с функцией округления, на этот раз с использованием каррирования:
который является более многоразовым, например, при округлении денежных сумм можно использовать следующее:
источник
NaN
, не так ли?floor
том, чтоtruncateAt(1.23456789, 8)
вернет,1.23456788
аroundAt(1.23456789, 8)
вернет правильное значение1.23456789
Так как никто еще не упомянул
%
оператора, вот оно. Он выполняет только усечение, и вы не можете полагаться на возвращаемое значение, чтобы не иметь неточностей с плавающей запятой, но иногда это удобно:источник
26.257391515826225 - 0.057391515826223094 = 26.200000000000003
Как насчет :
источник
((1.949 * 1000).toInt - ((1.949 * 1000).toInt % 10)) / 1000.toDouble
хотя и не слишком много тестировал. В этом коде будет два десятичных знака.Изменить: исправлена проблема, указанная @ryryguy. (Спасибо!)
Если вы хотите, чтобы все было быстро, у Кайто есть правильная идея.
math.pow
хотя медленный. Для любого стандартного использования вам лучше использовать рекурсивную функцию:Это примерно в 10 раз быстрее, если вы находитесь в пределах диапазона
Long
(т.е. 18 цифр), поэтому вы можете округлить до любого значения от 10 ^ 18 до 10 ^ -18.источник
scala> def r5(x:Double) = math.round(x*100000)*0.000001; r5(0.23515)
==>res12: Double = 0.023514999999999998
. Вместо этого разделите по значимости:math.round(x*100000)/100000.0
p10
функцию поиском по массиву: массив увеличит потребление памяти примерно на 200 байт, но, вероятно, сэкономит несколько итераций на вызов.Вы можете использовать неявные классы:
источник
Для тех, кому интересно, вот несколько вариантов предлагаемых решений ...
Усечение является самым быстрым, за ним следует BigDecimal. Имейте в виду, что этот тест был выполнен при выполнении norma scala, без использования каких-либо инструментов тестирования.
источник
...truncate or round a Double
,doubleParts.tail
и объедините их со строками "." иdoubleParts. head
и разобрать на удвоение.toString.split(".")
иdoubleParts.head/tail
предложение могут пострадать от дополнительного выделения массива плюс конкатенации строк. для уверенности потребуется тестирование.Недавно я столкнулся с подобной проблемой и решил ее, используя следующий подход
Я использовал эту проблему SO. У меня есть несколько перегруженных функций для параметров Float \ Double и implicit \ explicit. Обратите внимание, что вам нужно явно указать тип возвращаемого значения в случае перегруженных функций.
источник
На самом деле с этим очень легко справиться с помощью
f
интерполятора Scala - https://docs.scala-lang.org/overviews/core/string-interpolation.htmlПредположим, мы хотим округлить до двух знаков после запятой:
источник
Я бы не стал использовать BigDecimal, если вам важна производительность. BigDecimal преобразует числа в строку, а затем снова анализирует ее:
Я собираюсь придерживаться математических манипуляций, как предложил Кайто .
источник
Немного странно, но приятно. Я использую String, а не BigDecimal
источник
Вы можете сделать:
Math.round(<double precision value> * 100.0) / 100.0
Но Math.round самый быстрый, но он плохо работает в угловых случаях с очень большим количеством десятичных знаков (например, round (1000.0d, 17)) или большой целой частью (например, round (90080070060.1d, 9) ).Используйте Bigdecimal, это немного неэффективно, так как преобразует значения в строку, но более средневековое:
BigDecimal(<value>).setScale(<places>, RoundingMode.HALF_UP).doubleValue()
используйте свой предпочтительный режим округления.Если вам интересно и вы хотите узнать более подробно, почему это происходит, вы можете прочитать это:
источник