Ошибка преобразования ColdFusion double в long

#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
 

Вы можете просмотреть результаты здесь.