#coldfusion #type-conversion
Вопрос:
Может ли кто-нибудь объяснить мне, почему ColdFusion (протестирован в 2016,2018 и 2021 годах) делает неправильное двойное преобразование в длинное? Я знаю, что это может испортить ситуацию для дробных значений, но в данном примере это явно целое значение.
Это и есть код:
<cfoutput>
<cfset a = 69.35>
#getMetadata(a)# #a#</br>
<cfset b = a * 100>
#getMetadata(b)# #b#</br>
<cfset c = int(b)>
#getMetadata(c)# #c#</br>
</cfoutput>
И это результат:
class coldfusion.runtime.CFDouble 69.35
class java.lang.Double 6935
class java.lang.Long 6934
Это «в некотором роде» можно исправить, сделав это:
<cfset d = int(javacast("string", b))>
#getMetadata(d)# #d#</br>
Возвращающийся
class java.lang.Long 6935
Но я не очень доволен этим «решением»…
Спасибо!
ПРАВКА: Поскольку ColdFusion работает поверх java, я предполагаю, что это ответственно за это:
public static void main(String[] args)
{
double a = 69.35;
double b = a * 100;
System.out.println(b);
long c = (int)b;
System.out.println(c);
long d = Math.round(b);
System.out.println(d);
}
Выход:
6934.999999999999
6934
6935
И, скорее всего, ColdFusion использует int (), а не round() для преобразования двойного значения в long… Это один из «приятных» побочных эффектов языка программирования без типов, который внутренне портит его. Заставляет меня думать о javascript 😉
ПРАВКА 2:
Как отметил Кэмерон, существует разница между #b# и #b.toString()#. Первый возвращает 6935, в то время как второй возвращает 6934,99999999999999. На мой взгляд, это сбивает с толку, но я буду держать это в голове на случай, если столкнусь с еще одной странной проблемой с двойными/длинными значениями 🙂
И чтобы сделать это еще более запутанным: int(toString(b)) возвращает 6935, в то время как int(b.toString()) возвращает 6934…
<cfset a = 69.35>
#getMetadata(a)# #a#</br>
<cfset b = a * 100>
#getMetadata(b)# #b#</br>
#b.toString()# #ToString(b)#</br>
Возвращается:
class java.lang.String 69.35
class java.lang.Double 6935
6934.999999999999 6935
Поэтому не думайте, что b.toString () — это то же самое, что toString(b) …
Комментарии:
1.Да, к сожалению, то, что что-то выглядит как целое число, не всегда означает, что оно одно; -). Если вы выведете
#b.toString()#
результат на самом деле6934.999999999999
. Посколькуint()
«..вычисляет ближайшее целое число, которое меньше числа…», результат правильный,. Не уверен, почему вывод только#b#
(без toString()) не дает того же результата, но … все это часть удовольствия от языка без шрифтов, я думаю.
Ответ №1:
Как отмечает @SOS в своем комментарии (не уверен, почему они не сделали это «ответом»?), проблема не в конверсии. Проблема в том , что ColdFusion отображается 69.35 * 100
как равный 6935
, а это не так. И даже ColdFusion на самом деле так не думает.
Что касается большинства вычислительных языков, 69.35 * 100
это 6934.999999999999
(проверьте JS, Python, Ruby и т. Д., Если хотите) Из-за проблем с присущей неточностью представления десятичных дробных значений в системе, которая хранит данные в двоичном формате. Я уже писал об этом раньше: Арифметика с плавающей запятой с десятичными дробями.
Внутренне ColdFusion сохраняет результат в виде 6934.999999999999
:
<cfset f = 69.35 * 100>
<cfoutput>#f.toString()#</cfoutput>
Это дает:
6934.999999999999
Поэтому , когда вы используете int
, чтобы взять целочисленную часть 6934.999999999999
, вы получаете 6934
. Эта часть на самом деле выполняет свою работу правильно! 😉
Комментарии:
1. Я решил не беспокоиться, так как они, казалось, уже решили эту проблему 😉 Хотя мне всегда было любопытно, какие именно манипуляции Adobe выполняет при оценке
<cfoutput>#f#</cfoutput>
, чтобы произвести6935
вместо отображения базовой ценности6934.999999999999
— и, конечно, почему. Интересно, что Люси выбирает более разумный курс imo и отображает реальную базовую ценность.2. Да, это странно. Для меня при выводе такого выражения он должен просто вызывать соответствующий
toString
метод. Странно — и бесполезно, я думаю, — как он делает здесь что-то еще :-/3. «Итак, когда вы используете int для получения целочисленной части» да, это я понимаю, но в реальном коде я вставлял значение в базу данных, и, похоже, оно внутренне использует int(). И часть путаницы заключалась в том, что отображение значения непосредственно перед вставкой бд, например #b#, возвращало 6935, а чтение значения базы данных сразу после возврата 6934… На мой взгляд, #b# должен возвращать то же самое, что и #b.toString()#. Спасибо, что указали на разницу.
4.
.toString()
Вызов выполняется с использованием внутреннегоDouble.toString()
метода Java, поэтому он был реализован компетентным ppl. Функции были написаны кем-то @ Allaire / Macromedia, так что бог знает, что это было за «мышление»; -)5. FWIW, какую бы логику ни использовал CF, это не int (), поскольку она возвращает дробные числа …в некоторых случаях. Хотя, как сказал Адам, понятия не имею, что такое «логика» CF за пределами этого, лол
Ответ №2:
Я знаю, что немного опоздал с этим ответом, но вот что я использовал в прошлом, сталкиваясь с проблемами точности в ColdFusion при работе с математическими расчетами с использованием валюты. Чтобы избежать ошибок точности, я всегда связывал свои математические вычисления с precisionEvaluate()
функцией. Используйте его с вашим примером кода
<cfoutput>
<cfset a = 69.35>
#getMetadata(a)# #a#</br>
<cfset b = precisionEvaluate(a * 100)>
#getMetadata(b)# #b#</br>
<cfset c = int(b)>
#getMetadata(c)# #c#</br>
</cfoutput>
Результирующий результат выглядит следующим образом. Как вы можете видеть, он преобразует его из двойного в десятичное число и позволяет избежать проблем с точностью.
class coldfusion.runtime.CFDouble 69.35
class java.math.BigDecimal 6935.00
class java.lang.Long 6935
Вы можете просмотреть результаты здесь.