Sdscompany.ru

Компьютерный журнал
0 просмотров
Рейтинг статьи
1 звезда2 звезды3 звезды4 звезды5 звезд
Загрузка...

Double nan java

В Java году что означает NaN?

У меня есть программа, которая пытается сжать double до нужного числа. На выходе я получаю NaN .

Что означает NaN в Java?

11 Ответов

«NaN» означает «not a number». «Nan» производится, если Плавающая точка операция имеет некоторые входные параметры которые заставляют операцию производить какой-то неопределенный результат. Например, 0.0, деленное на 0.0, арифметически не определено. Извлечения квадратного корня отрицательное число также не определено.

NaN означает “Not a Number” и в основном представляет собой представление специального значения с плавающей запятой в стандарте IEE 754 с плавающей запятой . NaN обычно означает, что значение является чем-то, что не может быть выражено с помощью допустимого числа с плавающей запятой.

Преобразование приведет к этому значению, когда преобразуемое значение является чем-то другим, например, при преобразовании строки, которая не представляет собой число.

NaN означает «Not a Number» и является результатом неопределенных операций с числами с плавающей запятой, таких как, например, деление нуля на ноль. (Обратите внимание, что хотя деление ненулевого числа на ноль также обычно не определено в математике, это приводит не к NaN, а к положительной или отрицательной бесконечности).

NaN означает «Not a number.» это специальное значение с плавающей запятой, которое означает, что результат операции не был определен или не может быть представлен в виде реального числа.

Смотрите здесь для более подробного объяснения этого значения.

NaN означает не число. Он используется для обозначения любого математически неопределенного значения. Например, разделить 0.0 на 0.0. Вы можете посмотреть здесь для получения дополнительной информации: https://web.archive.org/web/20120819091816/http://www.concentric.net/

Разместите свою программу здесь, Если вам нужна дополнительная помощь.

Значит, не число. Это общее представление для невозможного числового значения во многих языках программирования.

Не Java парень, но в JS и других языках, которые я использую, это «не число», то есть какая-то операция привела к тому, что оно стало не действительным числом.

Это буквально означает «Not a Number.» я подозреваю, что что-то не так с вашим процессом преобразования.

Проверьте раздел Not a Number в этой ссылке

Недопустимое значение с плавающей запятой (например, результат деления на ноль)

Минимальный работоспособный пример

Первое, что вы должны знать, это то, что концепция NaN реализована непосредственно на аппаратном обеспечении CPU.

Все основные современные CPUs, похоже, следуют за IEEE 754 , который определяет форматы с плавающей запятой, и NaNs, которые являются просто специальными значениями с плавающей запятой, являются частью этого стандарта.

Поэтому концепция будет очень похожа на любой язык, включая Java, который просто выдает код с плавающей запятой непосредственно в CPU.

Прежде чем продолжить, Вы можете сначала прочитать следующие ответы, которые я написал:

  • краткое обновление формата IEEE 754 с плавающей запятой: что такое субнормальное число с плавающей запятой?
  • некоторые основы нижнего уровня NaN, описанные с помощью C / C++: в чем разница между Тихим NaN и сигнальным NaN?

Теперь для некоторых действий Java. Большинство функций интереса, которые не находятся в основном языке, живут внутри java.lang.Float .

Так что из этого мы узнаем несколько вещей:

странные плавающие операции, которые не имеют никакого разумного результата, дают NaN:

В _75 на самом деле можно запрашивать сигналы, которые будут подняты при таких операциях с feenableexcept , чтобы обнаружить их, но я не думаю, что это выставлено в Java: почему целочисленное деление на ноль 1/0 дает ошибку, но с плавающей точкой 1/0.0 возвращает «Inf»?

странные операции, которые находятся на пределе либо плюс, либо минус бесконечности, однако дают + — бесконечность вместо NaN

0.0 почти попадает в эту категорию, но, вероятно, проблема заключается в том, что он может либо перейти в плюс, либо в минус бесконечность, поэтому он был оставлен как NaN.

если NaN является входом плавающей операции, то выход также имеет тенденцию быть NaN

существует несколько возможных значений для NaN 0x7fc00000 , 0x7fc00001 , 0x7fc00002 , хотя x86_64, похоже, генерирует только 0x7fc00000 .

NaN и бесконечность имеют схожее двоичное представление.

Давайте разберем несколько из них:

Из этого мы подтверждаем то, что указывает IEEE754:

  • как NaN, так и бесконечности имеют показатель степени = = 255 (все единицы)
  • бесконечности имеют мантиссу = = 0. Таким образом, существует только две возможные бесконечности: + и -, различающиеся по знаку бита
  • NaN имеет мантиссу != 0. Таким образом, существует несколько возможностей, за исключением мантиссы = = 0, которая является бесконечностью

NaNs может быть как положительным, так и отрицательным (верхний бит), хотя это никак не влияет на нормальные операции

Проверено в Ubuntu 18.10 amd64, OpenJDK 1.8.0_191.

Похожие вопросы:

Я новичок в Java году. Меня смущает переносимость Java. Если Java язык переносим, то почему enum неизвестен в J2ME? Я программист C++. В C++ не важно, какая платформа или библиотека используется.

Что означает этот код?? этот код написан в matlab. citra=imresize(citra,[400 NaN]); я просто знаю, что изображение (citra)изменяется на 400. Но что насчет Нэн? Что это значит?

Когда я посмотрел на историю версий Java, я увидел, что 8-я версия будет коммерческой в 2019 году и будет некоммерческой в 2020 году. Я не понял, что они означают. И кроме того, Пожалуйста.

В чем разница между NaN и Infinity ? Когда появляется NaN ? Что это?

Я создал 2-мерный массив в Java году и искал способ распечатать его на консоли, чтобы подтвердить, что материал, который я делал, был правильным. Я нашел в интернете код, который выполнял эту задачу.

В Python году. r^[w*]$ что это значит?

Что означает () — > <> в java?. Любая помощь будет высоко оценена.

Из того, что я смог найти, IEEE определяет NaN, чтобы не равняться самому себе в системе счисления с плавающей точкой. Единственный раз, когда мне приходилось сравнивать NaN с NaN, это сравнение.

Я был удивлен количеством объектов C++, представляющих значение NaN. Я нашел три стандартных способа: std::numeric_limits ::quiet_NaN() -общий, и я думал, что это был избранный std::nan .

Что означает объявление коллекции окончательной в Java году? Может быть, к нему больше нельзя добавить никаких элементов? Не потому ли, что уже существующие элементы не могут быть изменены? Это.

Double nan java

&copy 2008 Наталия Македа
Все материалы блога защищены авторским правом. Любая перепечатка или использование материалов этого блога в коммерческих целях возможна лишь с письменного согласия автора. При некоммерческом использовании ссылка на блог обязательна.

суббота, 3 мая 2008 г.

Сложение и вычитание, NaN (Выпуск 6)

Сложение и вычитание (+ и -)

За сложение и вычитание отвечают соответсвенно операторы + и -. Они применимы для операндов любых численных типов. Помимо этого, операция + применяется и для операндов типа String . Когда один из операндов типа String , то результат будет объектом типа String .

Особенности оператора +

В Java не допускается определять перегрузку операторов (то есть перепрограммирование операторов в зависимости от типа), как в С/С++. Но сам язык Java перегружает операторы автоматически. И это в общем-то не ново, поскольку многие языки программирования, которые поддерживают множественные арифметические типы, определяют автоматическую перегрузку арифметических операторов для примитивных типов. Java, помимо этого, имеет перегрузку оператора + для строкового типа ( String ). И результат действия оператора + в данном случае — это конкатенация, сцепление строк. Если один из операндов не строковый, то к нему будет предварительно применено приведение типа к строке.

Перегрузка (overloading) — это термин, упоминаемый в том случае, когда одна и та же операция (оператор) используется для операндов (аргументов) различного типа. При этом поведение операции (оператора) определяется типом операндов, к котором она должна быть применена. Например, метод println() может применяться как для операндов строкового, так и целочисленного типа. Эти два использования, на самом деле, относятся к совершенно различным методам. Просто было использовано одно и то же имя операции. Аналогично сложение (+) используется, как для целочисленных, так и для дробных типов. Но код, реализующий это сложение совершенно различный.

Вы все знаем, что произойдёт при сложении двух знацений численного типа: операнды будут суммированы. Конечно, может случится и переполнение, если мы суммироем слишком большие числа. Но в общем и целом, поведение арифметических операторов не изобилует сюрпризами.

Если переполнение происходит при арифметическом сложении или вычитании, никаких сообщений об ошибках или исключений мы не получаем. Дело обстоит совсем по-другому, если мы конкатенируем строки. Если оба оператора строки, то результат — это «скленная» строка. Если один из операторов строка, а второй — число, то число будет приведено к типу String .

Читать еще:  Node js серверный javascript

Приведение численных типов к строке

Для объектных типов приобразование к типу String происходит посредством вызова метода toString() . Этот метод определён в классе java.lang.Object , который является прародителем всех остальных классов. Поэтому все объекты наследуют от Object метод toString() . Однако, этот метод выдаёт некое зашифрованное значение объекта. Например, если мы рассмотрим такой код:

То результат, выводимый 13-й строкой будет такого вида: Test$MyClass@c17164 . Здесь мы видим имена классов объекта (разделённые символом $) и какой-то идентификатор после символа @. Идентификатор — это, как правило, ссылка на объект.

Это не очень удобно для понимания, но может быть использовано, например, для отладки программы. С другой строны, для предания читаемости результату операции toString() необходимо перегрузить эту операцию. Например, если в 9-й строке вставить код:

то результат будет Natalia Macheda 28 . Заметьте, что втрой аргумент — целочисленного типа. Как же произошло его преобразование в строку?

Преобразование чесленных аргументов к строчному типу неявно использует метод toString() классов-оболочек (wrapper). Например, значение типа int конвертируется вызовом статической функции Integer.toString() .

Резюмируем сведения о сложении. Сложение двух числовых значений примтивных типов даёт результат:

  • примитивного численного типа;
  • по крайней мере самого длинного типа среди типов операндов, участвующих в сложении;
  • вычисленный приведением обоих операндов к типу результата и их дальнейшим суммированием.
  • Результат может переполнить тип и точность вычисления может потеряться.

Если один из операндов — НЕпримитивного типа:

  • второй операнд должен быть строкового типа. Иначе операция нелегальна;
  • операнд НЕстрокового типа приводится к типу String и результат — сцепление двух строк.

Приведение операнда объектного типа к строке производится путём вызова метода toString() .

Приведение операнда примитивного типа к строке производится неявно путём вызова статичского метода класса-оболочки.

Если вы хотите держать под контролем форматирование результата приведения типа, обратитесь к коду пакета java.text .

Теперь, когда мы разобрались с арифметическими операциями, приведениями типов и конкатенацией строк при помощи оператора +, мы должны вспомнить, что имеем дело с компьютером, который ограничер в своих возможностях представления неких математических абстракций. А значит, всем известные арифметические операции могут работать неправильно и выдавать ошибки.

Арифметические ошибки

Мы ожидаем от арифметических операций результата, который выдаёт знаяение с математическим смыслом. Но, как уже было сказано выше, результат может не соответсвовать нашим ожиданиям в виду ограниченности ресурсов компьютера. В частности:

  • Деление на ноль (в том числе и для операции %) выдаёт ArithmeticException ;
  • Никаких других исключений для арифметических операций не выдаётся, но результат может быть арифметически неправильным ввиду переполнения.
  • Дробные типы представляют абстрактные значения при помощи следующих значений: IEEE 754 бесконечности, минус бесконечности и Не-числа NaN (Not a Number). Именные константы, представляющие эти значения, объявлены в классах Float и Double .

NaN сигнализирует о том, что вычисление не имеет результата в математическом понимании. Например, бесконечность или вычисление квадратного корня из отрицательного числа.

Ideas, Solutions, Work in progress

and other things

Java Double and NaN Weirdness

Jan 11 th , 2017 1:08 am

We learn something everyday. We don’t always realise it, but we do. Sometimes the thing you learn isn’t new at all, but something you sort of knew but never really thought about too much.

I recently learned that my understanding of what causes a NaN value in Java’s double was wrong.

The story

I was working on an integration project and received a bug report on one of my services. The report said that my service is returning an HTTP code ‘500’ for a specific input message.

During my investigation I found the cause of the exception was an unexpected value returned from a down stream service. It was a SOAP service which returned something like the following in its XML response:

I was a bit surprised to see the NaN there since I would expect them to either leave the field off or set it to null if they don’t have a value. This looked like a calculation bug since we all know that, in Java and C# at least, dividing a double with 0 results in a NaN . (Spoiler: It doesn’t)

However, this got me thinking and I tried to remember what I know about double and NaN . This resulted in an embarrisingly deep spiral down the rabbit hole.

Well if you think about it NaN is kind of like a number in this case, even though NaN means Not-a-Number. It exists to enable calculations with indeterminate results to be represented as a “number” in the set of valid double values. Without NaN you could get completely wrong results or you’ll get an exception, which isn’t ideal either. NaN is defined, same as Infinity , to be part of the set of valid doubles.

I played around with double a bit and I thought to share it in a post, because I think the various edge cases of double are interesting.

I started with the following experiment:

Wait. What?

Turns out that I have lived with this misconception about what happens when you divide a double by zero. I seriously expected that a double divided by 0 is NaN . Well it turns out I was wrong. You get:

I can sort of rationalise that the answer could be infinity because you are dividing something largish with something much much smaller. In fact, dividing it by nothing so you could argue the result of that should be infitely large. Although, mathematically this does not make any sense. x/0 is undefined since there is no number that you can multiply with 0 to get back to x again. (for x <> 0)

Anyway lets play with NaN a bit.

Well no surprises here. Once a NaN always a NaN.

I used Double.NaN above to be sure I have a NaN but if you want to make one yourself then calculating the square root of a negative number is an easy way:

Max and Min value

Before we get to infinity let take a quick look at Double.MAX_VALUE and Double.MIN_VALUE . These are special constants defined on Double which you can use to check if a number is at the maximum of what a double can represent. If a number is equal to Double.MAX_VALUE it means that it is about to overflow into Double.POSITIVE_INFINITY . The same goes for Double.MIN_VALUE except that it will overflow to Double.NEGATIVE_INFINITY .

Something to note about double is that it can represent ridiculously large numbers using a measly 64 bits. The maximum value is larger than 1.7*10^308 !

It can represent these large numbers because it encodes numbers as a small real number multiplied by some exponent. See the IEEE spec

Let’s see what it takes to make Double.MAX_VALUE overflow to infinity.

This ability to represent seriously large numbers comes at a price of accuracy. After a while only changes in the most significant parts of the number can be reflected. As seen in the following code snippet:

At large integer values the steps between numbers are very very large since the double has no place to record the change if it doesn’t affect its most 16 most significant digits. As shown above 1000 plus a very large number is still that same very large number.

Infinity

Java’s double supports two kinds of infinity. Positive and negative inifity. The easiest to make those are by dividing by 0.

In maths infinity is a numerical concept representing the idea of an infinitly large number. It is used, for example in calculus, to describe an unbounded limit — some number that can grow without bound.

In this case things are pretty much the same as in maths, where POSITIVE_INFINITY and NEGATIVE_INFINITY are used to represent numbers that are infinitely large. However they function more as a way to know something went wrong in your calculation. You are either trying to calculate something that is too large to store in a double or there is some bug in the code.

There are once again some interesting things to note when playing with positive and negative infinity.

Once the value is infinity it stays there even if you add or substract rediculously large numbers. However there is one interesting case, when you substract infinity from infinity:

Читать еще:  Javascript json decode

Subtracting infinity from infinity yields NaN and as you would expect adding or subtracting NaN yields a NaN again.

In closing

Both Java’s float and double types follow the IEEE 754-1985 standard for representing floating point numbers. I am not going to go into great detail on the internals of double , but it suffice to say that double and float are not perfectly accurate when you use them to perform arithmetic. The Java primitive type documentation says:

This data type should never be used for precise values, such as currency. For that, you will need to use the java.math.BigDecimal class instead.

If precision is you main concern then it is generally better to stick with good old java.math.BigDecimal . BigDecimal is immutable which makes it nice to work with, but the most important thing is precision. You have absolute control over number precision, without the rounding or overflow surprises you get with double and float . However, if performance is the main concern it is better to stick with float or double and live with the inaccuracies.

For more information on how Java handles NaN, infinity and rouding read the documentation here.

Posted by Dirk van Rensburg Jan 11 th , 2017 1:08 am java

About Me

I am a software developer using various programming languages. I live in Sydney and my twitter handle is @pappanyn

Recent Posts

Copyright © 2017 — Dirk van Rensburg — Powered by Octopress

Java example source code file (Double.java)

This example Java source code file (Double.java) is included in the alvinalexander.com «Java Source Code Warehouse» project. The intent of this project is to help you «Learn Java by Example» TM .

Learn more about this Java project at its project page.

Java — Java tags/keywords

class, double, min_exponent, min_value, nan, negative_infinity, neither, numberformatexception, positive_infinity, size, string, stringbuilder, suppresswarnings, values

The Double.java Java example source code

also has the value <@code true>. However, there are two * exceptions: *

    *
  • If <@code d1>and <@code d2>both represent * <@code Double.NaN>, then the <@code equals>method * returns <@code true>, even though * <@code Double.NaN==Double.NaN>has the value * <@code false>. *
  • If <@code d1>represents <@code +0.0>while * <@code d2>represents <@code -0.0>, or vice versa, * the <@code equal>test has the value <@code false>, * even though <@code +0.0==-0.0>has the value <@code true>. *

* This definition allows hash tables to operate properly. * @param obj the object to compare with. * @return <@code true>if the objects are the same; * <@code false>otherwise. * @see java.lang.Double#doubleToLongBits(double) */ public boolean equals(Object obj) < return (obj instanceof Double) && (doubleToLongBits(((Double)obj).value) == doubleToLongBits(value)); >/** * Returns a representation of the specified floating-point value * according to the IEEE 754 floating-point «double * format» bit layout. * *

Bit 63 (the bit that is selected by the mask * <@code 0x8000000000000000L>) represents the sign of the * floating-point number. Bits * 62-52 (the bits that are selected by the mask * <@code 0x7ff0000000000000L>) represent the exponent. Bits 51-0 * (the bits that are selected by the mask * <@code 0x000fffffffffffffL>) represent the significand * (sometimes called the mantissa) of the floating-point number. * *

If the argument is positive infinity, the result is * <@code 0x7ff0000000000000L>. * *

If the argument is negative infinity, the result is * <@code 0xfff0000000000000L>. * *

If the argument is NaN, the result is * <@code 0x7ff8000000000000L>. * *

In all cases, the result is a <@code long>integer that, when * given to the <@link #longBitsToDouble(long)>method, will produce a * floating-point value the same as the argument to * <@code doubleToLongBits>(except all NaN values are * collapsed to a single «canonical» NaN value). * * @param value a <@code double>precision floating-point number. * @return the bits that represent the floating-point number. */ public static long doubleToLongBits(double value) < long result = doubleToRawLongBits(value); // Check for NaN based on values of bit fields, maximum // exponent and nonzero significand. if ( ((result & DoubleConsts.EXP_BIT_MASK) == DoubleConsts.EXP_BIT_MASK) && (result & DoubleConsts.SIGNIF_BIT_MASK) != 0L) result = 0x7ff8000000000000L; return result; >/** * Returns a representation of the specified floating-point value * according to the IEEE 754 floating-point «double * format» bit layout, preserving Not-a-Number (NaN) values. * *

Bit 63 (the bit that is selected by the mask * <@code 0x8000000000000000L>) represents the sign of the * floating-point number. Bits * 62-52 (the bits that are selected by the mask * <@code 0x7ff0000000000000L>) represent the exponent. Bits 51-0 * (the bits that are selected by the mask * <@code 0x000fffffffffffffL>) represent the significand * (sometimes called the mantissa) of the floating-point number. * *

If the argument is positive infinity, the result is * <@code 0x7ff0000000000000L>. * *

If the argument is negative infinity, the result is * <@code 0xfff0000000000000L>. * *

If the argument is NaN, the result is the <@code long>* integer representing the actual NaN value. Unlike the * <@code doubleToLongBits>method, * <@code doubleToRawLongBits>does not collapse all the bit * patterns encoding a NaN to a single «canonical» NaN * value. * *

In all cases, the result is a <@code long>integer that, * when given to the <@link #longBitsToDouble(long)>method, will * produce a floating-point value the same as the argument to * <@code doubleToRawLongBits>. * * @param value a <@code double>precision floating-point number. * @return the bits that represent the floating-point number. * @since 1.3 */ public static native long doubleToRawLongBits(double value); /** * Returns the <@code double>value corresponding to a given * bit representation. * The argument is considered to be a representation of a * floating-point value according to the IEEE 754 floating-point * «double format» bit layout. * *

If the argument is <@code 0x7ff0000000000000L>, the result * is positive infinity. * *

If the argument is <@code 0xfff0000000000000L>, the result * is negative infinity. * *

If the argument is any value in the range * <@code 0x7ff0000000000001L>through * <@code 0x7fffffffffffffffL>or in the range * <@code 0xfff0000000000001L>through * <@code 0xffffffffffffffffL>, the result is a NaN. No IEEE * 754 floating-point operation provided by Java can distinguish * between two NaN values of the same type with different bit * patterns. Distinct values of NaN are only distinguishable by * use of the <@code Double.doubleToRawLongBits>method. * *

In all other cases, let s, e, and m be three * values that can be computed from the argument: * * * * Then the floating-point result equals the value of the mathematical * expression s·m·2 e-1075 . * *

Где ваша точка?

Уловки и ловушки, связанные с плавающей точкой и десятичными числами

Серия контента:

Этот контент является частью # из серии # статей: Теория и практика Java

Этот контент является частью серии: Теория и практика Java

Следите за выходом новых статей этой серии.

Хотя практически каждый процессор и язык программирования и поддерживают арифметику с плавающей точкой, но большинство программистов почти не обращают на это внимания. И это понятно — мало кому из нас часто приходиться использовать дробные числовые типы. За исключением научных вычислений и редких случаев тестирования времени или оценки производительности, до этого просто не доходит дело. Десятичные числа с произвольной разрядностью, предоставляемые java.math.BigDecimal , также не используются большинством разработчиков, так как основная масса приложений не нуждается в их применении. Тем не менее, превратности представления дробных чисел все-таки иногда проникают и в программы, базирующиеся на целых числах. Например, JDBC использует BigDecimal как предпочтительный формат обмена для колонок DECIMAL SQL.

Плавающая точка IEEE

Язык Java поддерживает два простых типа с плавающей точкой: float и double , и их прототип — класс-оболочка Float и Double . Они основаны на стандарте IEEE 754, определяющем двоичный стандарт для двоично-десятичных чисел с 32-битовой плавающей точкой и 64-битовой плавающей точкой удвоенной точности.

IEEE 754 представляет числа с плавающей точкой как десятичные числа с основанием 2 в экспоненциальном формате. В IEEE числе с плавающей точкой выделяется 1 бит на знак, 8 бит на порядок и 23 бита на мантиссу, или дробную часть числа. Порядок расшифровывается как целое число со знаком, допускающее как положительный, так и отрицательный экспоненты. Дробь представляется как двоично-десятичное (основание 2) число, где самый старший бит соответствует значению ½ (2 -1 ), следующий бит ¼ (2 -2 ), и так далее. Для плавающей точки с удвоенной точностью на порядок выделяется 11 бит, а на мантиссу — 52 бит. Формат значений плавающей точки IEEE показан на рисунке 1.

Рисунок 1. Формат плавающей точки IEEE 754

Так как любое заданное число может быть представлено в экспоненциальном формате различными способами, то числа с плавающей точкой нормализуются таким образом, чтобы они могли быть представлены как десятичные числа с основанием 2, с 1 слева от десятичной точки, подбирая порядок таким образом, чтобы соблюсти данное требование. Следовательно, например, число 1.25 будет представлено с мантиссой 1.01 и порядком 0:
(-1)

Число 10.0 будет представлено с мантиссой 1.01 и порядком 3:
(-1)

Специальные числа

Помимо стандартного множества значений, разрешенного кодированием (от 1.4e-45 до 3.4028235e+38 для float ), существуют и специальные значения, представляющие бесконечность, минус бесконечность, -0 , и NaN (обозначает «не число»). Эти значения существуют для того, чтобы в случае возникновения ошибок, например, арифметического переполнения, извлечения квадратного корня из отрицательного числа и деления на 0 , можно было получить результат, представленный в диапазоне значений с плавающей точкой.

У таких специальных чисел есть несколько необычных характеристик. Например, 0 и -0 являются четко различимыми значениями, но при сравнении на тождественность они считаются равными. Деление ненулевого числа на бесконечность дает 0 . Специальное число NaN является неупорядоченным, а любое сравнение между NaN и другими значениями с плавающей точкой, с использованием операций == , и > выдаст неверно . Даже (f == f) выдаст ложь , если f является NaN. Если Вы хотите сравнить значение с плавающей точкой с NaN, то используйте метод Float.isNaN() . В таблице 1 показаны некоторые особенности бесконечности и NaN.

Таблица 1. Особенности специальных значений с плавающей точкой

Простой тип с плавающей точкой и класс-оболочка с плавающей точкой по-разному выполняют сравнение

Еще более усугубляет положение еще и то, что правила сравнения NaN и -0 у простого типа с плавающей точкой и класса-оболочки Float различаются. Для значений float , сравнение на тождественность двух значений NaN выдаст ложь , но при сравнении двух NaN объектов Float при использовании Float.equals() , будет выдано истина . Обоснованием этому может служить то, что иначе будет невозможно использовать NaN объект Float в качестве ключа в HashMap . Аналогично, хотя 0 и -0 и считаются равными, но в случае представления их в качестве значений с плавающей точкой, сравнение 0 и -0 как объектов Float при помощи Float.compareTo() указывает на то, что -0 является меньшей величиной, чем 0 .

Непредвиденные обстоятельства использования плавающей точки

В связи с особым поведением бесконечности, NaN и 0 определенные трансформации и оптимизации могут показаться безвредными, но при применении к числам с плавающей точкой будут приводить к ошибкам. Например, несмотря на то, что равенство 0.0-f и -f кажется очевидным, но оно ложно, если f равно 0 . Существуют и другие подобные проблемы, некоторые из которых показаны в таблице 2.

Таблица 2. Ошибочные представления о числах с плавающей точкой

Ошибки округления

Арифметика чисел с плавающей точкой не отличается особой точностью. Тогда как некоторые числа, например, 0.5 , можно точно представить как двоично-десятичные (основание 2) (поскольку 0.5 равно 2 -1 ), но другие числа, например, 0.1 — невозможно. В итоге операции над числами с плавающей точкой могут привести к ошибкам округления, выдавая результат, близкий — но не равный — тому результату, который можно было ожидать. Например, простое вычисление, приведенное ниже, равняется 2.600000000000001 , а не 2.6 :

Аналогично, умножение .1*26 выдает результат, отличный от прибавления .1 к самому себе 26 раз. Ошибки округления могут оказаться даже более серьезными при преобразовании типа от вещественного к целому, поскольку при преобразовании к целому типу нецелая часть отбрасывается, даже для вычислений, которые «выглядят похожими», они должны иметь целые значения. Например, следующие выражения:

на выходе дадут:

что не совсем соответствует первоначально ожидаемому результату.

Рекомендации по сравнению чисел с плавающей точкой

В связи с необычным поведением сравнения NaN и ошибок округления, которые фактически гарантированы практически при всех вычислениях с плавающей точкой, расшифровывание результатов, выдаваемых операторами сравнения значений с плавающей точкой, является весьма запутанным.

Лучше всего вообще стараться избегать сравнений чисел с плавающей точкой. Конечно же, это не всегда возможно, но Вы должны понимать ограничения сравнения чисел с плавающей точкой. Если Вам нужно сравнить числа с плавающей точкой для того, чтобы узнать, тождественны ли они, то вместо этого следует сравнивать абсолютное значение их разности с каким-либо предварительно выбранным значением эпсилон. То есть, таким образом, Вы проверяете насколько они «близки». (Если Вам не известен коэффициент масштабирования основных измерений, то использование проверки «abs(a/b — 1) BigDecimal . BigDecimal является стандартным классом, без специальной поддержки в компиляторе, для представления двоичных чисел произвольной разрядности и выполнения арифметических действий над ними. По сути дела BigDecimal представлен как недифференцированное значение произвольной разрядности и коэффициент масштабирования, который указывает насколько сдвинуть влево десятичную точку для получения значения, разбитого на дробную и целую части. Итак, число, представленное BigDecimal является unscaledValue*10 -scale .

Арифметические действия над значениями BigDecimal реализуются с помощью методов для сложения, вычитания, умножения и деления. Поскольку объекты BigDecimal являются неизменяемыми, то каждый из этих методов производит новый объект BigDecimal . В итоге BigDecimal не слишком хорошо подходит для интенсивных вычислений из-за затрат на создание объекта, но он спроектирован для представления точных десятичных чисел. Если Вам необходимо представить такие точные величины как денежные суммы, то BigDecimal прекрасно подойдет для этой задачи.

Все методы сравнения не созданы равными

Как и у всех типов с плавающей точкой, у BigDecimal тоже есть несколько особенностей. А именно, будьте осторожны с использованием метода equals() для проверки тождественности чисел. Два значения BigDecimal , представляющих одно и то же число, но имеющих различные коэффициенты масштабирования (например, 100.00 и 100.000 ) не будут равными при использовании метода equals() . Тем не менее, метод compareTo() будет считать их равными, а, следовательно, Вы должны использовать compareTo() вместо equals() при количественном сравнении двух значений BigDecimal .

Существуют некоторые случаи, когда для получения точных результатов недостаточно двоичной арифметики с произвольной разрядностью. Например, деление 1 на 9 выдает бесконечную периодическую двоичную дробь .111111. По этой причине BigDecimal позволяет Вам явно контролировать округление при выполнении операций деления. Точное деление на показатель степени 10 поддерживается методом movePointLeft() .

Используйте BigDecimal в качестве типа обмена

В SQL-92 есть тип данных DECIMAL , который является точным числовым типом для представления десятичных чисел с фиксированной точкой и выполняет основную арифметическую операцию над двоичными числами. В некоторых диалектах SQL предпочитают называть этот тип NUMERIC , а другие также содержат тип данных MONEY , который определяется как десятичное число с двумя знаками справа от десятичной дроби.

Если Вы хотите сохранить число в поле DECIMAL в базе данных или извлечь значение из поля DECIMAL , то как можно убедиться в том, что число будет передано точно? Вы не хотите использовать методы setFloat() и getFloat() , обеспечиваемые JDBC классами PreparedStatement и ResultSet , поскольку конвертация из числа с плавающей точкой в десятичное число может привести к потере точности. Вместо этого, используйте setBigDecimal() и getBigDecimal() методы PreparedStatement и ResultSet .

Аналогично, XML-инструменты привязки данных, например, Castor, сгенерируют механизмы получения и установки для десятичных атрибутов и элементов (которые поддерживаются в качестве основного типа данных в XSD-схеме), используя BigDecimal .

Построение чисел BigDecimal

Существует несколько конструкторов для BigDecimal . Один в качестве входной информации использует плавающую точку удвоенной точности, второй — целое число и коэффициент масштабирования, а третий — String — представление десятичного числа. Вам нужно быть осторожными с конструктором BigDecimal(double) , поскольку он может позволить ошибкам округления появиться в Ваших вычислениях раньше, чем вы об этом узнаете. Вместо этого используйте целое число или конструкторы, основанные на String .

Ненадлежащее использование конструктора BigDecimal(double) может проявляться в кажущихся странными ошибках в JDBC-драйверах при передаче в JDBC setBigDecimal() метод. Например, рассмотрим следующий JDBC-код, который хочет сохранить число 0.01 в десятичном поле:

В зависимости от Вашего драйвера JDBC, этот кажущийся безобидным код может при выполнении выдать некоторые запутанные ошибки, потому что приближенная величина с удвоенной точностью 0.01 приведет к большому дифференцированному значению, которое может запутать JDBC-драйвер и базу данных. Ошибка появится в JDBC драйвере, но весьма маловероятно, что она проявит себя, и Вы не будете знать, что не так с Вашим кодом, до тех пор, пока Вы не поймете ограничения двоичных чисел с плавающей точкой. Чтобы избежать возникновения таких проблем, сконструируйте BigDecimal , используя BigDecimal(«0.01») или BigDecimal(1, 2) , так как каждый из них, в конечном счете, приведет к точному десятичному представлению.

Заключение

Использование чисел с плавающей точкой и десятичных чисел в программах Java чревато трудностями. Числа с плавающей точкой и десятичные числа ведут себя совсем не так хорошо как целые, и Вы не можете предположить, что в действительности делают вычисления с плавающей точкой, у которых «должны» быть целочисленные или точные результаты. Лучше всего приберечь использование арифметики с плавающей точкой для вычислений, которые требуют принципиально неточных значений, например измерений. Если Вам необходимо представить величины с фиксированной точкой, например, доллары и центы, то используйте BigDecimal .

Ссылка на основную публикацию
Adblock
detector