Одним из наиболее сложных аспектов разработки программного обеспечения может быть работа с числами с плавающей запятой. Статья Дэвида Голдберга о компьютерных исследованиях 1991 года, которую должен знать каждый компьютерщик об арифметике с плавающей точкой, является признанным классическим трактатом на эту тему . Эта статья не только дает углубленный взгляд на то, как арифметика с плавающей запятой реализуется в большинстве языков программирования и компьютерных систем, но также, благодаря своей длине и деталям, свидетельствует о нюансах и трудностях этого предмета. Нюансы работы с числами с плавающей точкой в Java и тактики для преодоления этих проблем хорошо документированы в таких источниках, как арифметика JavaWorld с плавающей точкой , новая математика Java для IBM DeveloperWorks , Часть 2. Числа с плавающей точкой и теория Java и практика: где ваша точка зрения? , Прецизионные и фиксированные, плавающие и точные вычисления доктора Добба с Java с помощью Bigdecimal , плавающая точка Java-глоссария, примитивные типы данных Java Tutorial и NUM04-J. Не используйте числа с плавающей точкой, если требуется точное вычисление .
Большинство проблем, встречающихся и обсуждаемых в Java, связанных с представлением с плавающей запятой и арифметикой, вызваны невозможностью точно представлять (обычно) десятичные ( основную десятку ) числа с плавающей запятой с базовым двоичным представлением ( основание два ). В этой статье я сосредоточусь на аналогичных последствиях, которые могут возникнуть в результате смешивания чисел с фиксированной запятой (как хранится в базе данных) с числами с плавающей запятой (как представлено в Java).
База данных Oracle позволяет выражать числовые столбцы типа данных NUMBER двумя целыми числами, которые представляют «точность» и «масштаб». Реализация PostgreSQL числового типа данных очень похожа. Oracle NUMBER(p,s) и PostgreSQL numeric(p,s) позволяют одному и тому же типу данных по существу представлять целое значение (точность указана, но масштаб не указан), число с фиксированной точкой (указана точность и масштаб) или плавающее значение номер точки (не указана ни точность, ни масштаб). Простые примеры на основе Java / JDBC в этом посте продемонстрируют это.
Для примеров в этом посте будет создана простая таблица с именами DOUBLES в Oracle и doubles в PostgreSQL. Операторы DDL для определения этих простых таблиц в двух базах данных показаны далее.
createOracleTable.sql
|
1
2
3
4
5
6
|
CREATE TABLE doubles( int NUMBER(5), fixed NUMBER(3,2), floating NUMBER); |
createPgTable.sql
|
1
2
3
4
5
6
|
CREATE TABLE doubles( int numeric(5), fixed numeric(3,2), floating numeric); |
С таблицей DOUBLES созданной в базе данных Oracle и базе данных PostgreSQL, я затем буду использовать простой JDBC PreparedStatement, чтобы вставить значение java.lang.Math.PI в каждую таблицу для всех трех столбцов. Следующий фрагмент кода Java демонстрирует эту вставку.
Вставка Math.PI в столбцы DOUBLES
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
/** SQL syntax for insertion statement with placeholders. */private static final String INSERT_STRING = "INSERT INTO doubles (int, floating, fixed) VALUES (?, ?, ?)";final Connection connection = getDatabaseConnection(databaseVendor);try (final PreparedStatement insert = connection.prepareStatement(INSERT_STRING)){ insert.setDouble(1, Math.PI); insert.setDouble(2, Math.PI); insert.setDouble(3, Math.PI); insert.execute();}catch (SQLException sqlEx){ err.println("Unable to insert data - " + sqlEx);} |
Запрос столбцов DOUBLES
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
/** SQL syntax for querying statement. */private static final String QUERY_STRING = "SELECT int, fixed, floating FROM doubles";final Connection connection = getDatabaseConnection(databaseVendor);try (final Statement query = connection.createStatement(); final ResultSet rs = query.executeQuery(QUERY_STRING)){ out.println("\n\nResults for Database " + databaseVendor + ":\n"); out.println("Math.PI : " + Math.PI); while (rs.next()) { final double integer = rs.getDouble(1); final double fixed = rs.getDouble(2); final double floating = rs.getDouble(3); out.println("Integer NUMBER: " + integer); out.println("Fixed NUMBER: " + fixed); out.println("Floating NUMBER: " + floating); } out.println("\n");}catch (SQLException sqlEx){ err.println("Unable to query data - " + sqlEx);} |
Результат выполнения вышеуказанной вставки Java и запроса кода к базам данных Oracle и PostgreSQL, соответственно, показан на следующих двух снимках экрана.
Сравнение Math.PI с Math.PI Oracle NUMBER
Сравнение Math.PI с numeric Math.PI PostgreSQL
Простые примеры использования Java, Oracle и PostgreSQL демонстрируют проблемы, которые могут возникнуть при указании точности и масштаба для типов numeric столбцов Oracle NUMBER и PostgreSQL. Хотя существуют ситуации, когда числа с фиксированной запятой желательны, важно признать, что Java не имеет примитивного типа данных с фиксированной запятой, и использовать BigDecimal или библиотеку Java с фиксированной запятой (например, decimal4j или Java Math Fixed Point Library ) надлежащим образом работать с числами с фиксированной запятой, полученными из столбцов базы данных, выраженными в виде фиксированных В примерах, продемонстрированных в этом посте, нет ничего действительно «неправильного», но важно признать различие между числами с фиксированной запятой в базе данных и числами с плавающей запятой в Java, потому что арифметика, которая объединяет эти два значения, может не дать результатов. можно было бы ожидать.
В Java и других языках программирования нужно заботиться не только о влиянии арифметических операций и доступной точности на «правильность» чисел с плавающей точкой. Разработчик также должен знать, как эти числа хранятся в столбцах реляционной базы данных в базах данных Oracle и PostgreSQL, чтобы понять, как точность и масштабирование обозначений в этих столбцах могут влиять на представление сохраненного числа с плавающей запятой. Это особенно применимо, если представления, запрашиваемые из базы данных, должны использоваться в вычислениях с плавающей запятой. Это еще один (из многих) примеров, когда для разработчика Java важно понимать используемую схему базы данных.
| Ссылка: | Фиксированная точка и с плавающей точкой: две вещи, которые не сочетаются друг с другом от нашего партнера JCG Дастина Маркса в блоге Inspired by Actual Events . |

