Перейти к содержимому

Strictfp java что это

  • автор:

Модификатор strictfp

Для чего нужен данный модификатор? И с чем его можно использовать?

Отслеживать

22k 3 3 золотых знака 27 27 серебряных знаков 37 37 бронзовых знаков

задан 21 янв 2017 в 23:16

2,015 4 4 золотых знака 27 27 серебряных знаков 53 53 бронзовых знака

2 ответа 2

Сортировка: Сброс на вариант по умолчанию

strictfp — это модификатор, введенный в java 1.2, ограничивающий точность вычислений с float и double по стандарту IEEE. Для чего это нужно? Чтобы обеспечить переносимость. Дело в том, что JVM использует всю возможную точность процессора, а она на разных системах разная, поэтому и результат может получиться разный.Данный модификатор используется в программах требующих точность вычислений превышающих IEEE (обычно, что-нибудь связное с наукой).

strictfp class StrictFPClass < double num1 = 10e+102; double num2 = 6e+08; double calculate() < return num1 + num2; >> 
strictfp interface StrictFPInterface < double calculate(); strictfp double compute(); // compile error >
class StrictFPMethod < strictfp double computeTotal(double x, double y) < return x + y; >> 

В классе он обозначает, что все методы будут на всех системах возвращать вещественные числа с одинаковой точностью. 0.112 и 0.113 Если эти числа сравнивать с точность до сотых, то они будут одинаковы. Пример, как точность влияет на вычисления. Его можно не использовать т.к. почти все компиляторы современные уже его за нас подставляют (где-то читал такое в мануале) Дополнил.

Software Development

strictfp — это модификатор, введенный в java 1.2, ограничивающий точность вычеслений с float и double по стандарту IEEE. Для чего это нужно? Чтобы обеспечить портабильность (переносимость). Дело в том, что JVM использует всю возможную точность процессора, а она на разных системах разная, поэтому и результат может получиться разный. Например, когда вы сравниваете два double 0.111 и 0.112 с точностью двух знаков после запятой, то они равны, а вот при точности трех знаков — нет.На самом деле, сегодняшние компиляторы сами ставят strictfp, потому что случаи, когда он не нужен, можно пересчитать по пальцам. Речь идет о программах, во-первых требующих точность вычислений превышающих IEEE (обычно, что-нибудь связаное с наукой), а во-вторых имеющих возможность требовать от пользователя определенного hardware.Сколько вы таких программ знаете?

strictfp

strictfp является относительно новым ключевым словом. Когда была выпущена Java 2, модель вычислений с плавающей точкой была слегка упрощена. В частности, новая модель не требовала округления некоторых промежуточных результатов вычислений. В ряде случаев это предотвращает переполнение. Модифицируя класс или метод словом strictfp, вы гарантируете, что вычисления с плавающей точкой будут выполняться точно так, как они выполнялись в ранних версиях Java. Когда класс модифицирован словом strictfp, все его методы автоматически модифицируются strictfp.

Например, следующий фрагмент сообщает Java, что нужно использовать исходную модель вычислений с плавающей точкой при вычислении всех методов, определенных в классе MyClass:

strictfp class MyClass //.

Откровенно говоря, большинству программистов никогда не понадобится использовать strictfp, поскольку оно касается лишь небольшого класса проблем.

Вещи, которые вы [возможно] не знали о Java

Эта статья разбавит мой поток сознания о производительности. Поговорим о забавных вещах в яве и околояве, о которых вы возможно не знали. О некоторых из перечисленных я сам узнал недавно, так что считаю, что большинство читателей найдёт для себя хотя бы пару-тройку любопытных моментов.

assert может принимать 2 аргумента

Обычно assert используется для проверки некоторого условия и бросает AssertionError если условие не удовлетворяется. Чаще всего проверка выглядит так:

assert list.isEmpty();

Однако, она может быть и такой:

assert list.isEmpty() : list.toString();

Сообразительный читатель уже догадался, что второе выражение (кстати, оно ленивое) возвращает значение типа Object , которое передаётся в AssertionError и несёт пользователю дополнительные сведения об ошибке. Более формальное описание см. в соответствующем разделе спецификации языка: https://docs.oracle.com/javase/specs/jls/se13/html/jls-14.html#jls-14.10

За без малого 6 с половиной лет работы с явой расширенное использование ключевого слова assert я видел лишь однажды.

strictfp

Это не ругательство — это малоизвестное ключевое слово. Если верить документации, его использование включает строгую арифметику для чисел с плавающей запятой:

public interface NonStrict

можно лёгким движением руки превратить в

public strictfp interface Strict

Также это ключевое слово может применятся к отдельным методам:

public interface Mixed

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

continue может принимать аргумент

Узнал об этом на прошлой неделе. Обычно мы пишем так:

for (Item item : items) < if (item == null) < continue; >use(item); >

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

loop: for (Item item : items) < if (item == null) < continue loop; >use(item); >

Однако, вернуться из цикла можно и во внешний цикл, если таковой имеется:

@Test void test() < outer: for (int i = 0; i < 20; i++) < for (int j = 10; j < 15; j++) < if (j == 13) < continue outer; >> > >

Обратите внимание, счётчик i при возвращении в точку outer не сбрасывается, так что цикл является конечным.

При вызове vararg-метода без аргументов всё равно создаётся пустой массив

Когда мы смотрим на вызов такого метода извне, то кажется, что беспокоится не о чем:

@Benchmark public Object invokeVararg()

Мы ведь ничего не передали в метод, не так ли? А вот если посмотреть изнутри, то всё не так радужно:

public Object[] vararg(Object. args)

Опыт подтверждает опасения:

Benchmark Score Error Units invokeVararg 3,715 ± 0,092 ns/op invokeVararg:·gc.alloc.rate.norm 16,000 ± 0,001 B/op invokeVararg:·gc.count 257,000 counts

Избавится от ненужного массива при отсутствии аргументов можно передавая null :

@Benchmark public Object invokeVarargWithNull()

Сборщику мусора действительно полегчает:

invokeVarargWithNull 2,415 ± 0,067 ns/op invokeVarargWithNull:·gc.alloc.rate.norm ≈ 10⁻⁵ B/op invokeVarargWithNull:·gc.count ≈ 0 counts

Код с null выглядит очень некрасиво, компилятор (и «Идея») будет ругаться, так что используйте этот подход в действительно горячем коде и снабдив его комментарием.

Выражение switch-case не поддерживает java.lang.Class

Этот код просто не компилируется:

String to(Class clazz) < switch (clazz) < case String.class: return "str"; case Integer.class: return "int"; default: return "obj"; >>

Смиритесь с этим.

Тонкости присваивания и Class.isAssignableFrom()
int a = 0; Integer b = 10; a = b; // присваивание вполне работоспособно

А теперь подумайте, какое значение вернёт этот метод:

boolean check(Integer b)

Прочитав название метода Class.isAssignableFrom() создаётся обманчивое впечатление, что выражение int.class.isAssignableFrom(b.getClass()) вернёт true . Мы ведь можем присвоить переменной типа int значение переменной типа Integer , не так ли?

Однако метод check() вернёт false , так как в документации чётко прописано, что:

/** * Determines if the class or interface represented by this * object is either the same as, or is a superclass or * superinterface of, the class or interface represented by the specified * parameter. It returns if so; * otherwise it returns . If this // <-- . * object represents a primitive type, this method returns * if the specified parameter is * exactly this object; otherwise it returns * . * */ @HotSpotIntrinsicCandidate public native boolean isAssignableFrom(Class cls);

Хоть int и не является наследником Integer -а (и наоборот) возможное взаимное присваивание — это особенность языка, а чтобы не вводить пользователей в заблуждение в документации сделана особая оговорка.

Мораль: когда кажется — креститься надо надо перечитывать документацию.

Из этого примера проистекает ещё один неочевидный факт:

assert int.class != Integer.class;

Класс int.class — это на самом деле Integer.TYPE , и чтобы убедиться в этом, достаточно посмотреть, во что будет скомпилирован этот код:

Class toClass()
toClass()Ljava/lang/Class; L0 LINENUMBER 11 L0 GETSTATIC java/lang/Integer.TYPE : Ljava/lang/Class; ARETURN

Открыв исходники java.lang.Integer увидим там вот это:

@SuppressWarnings("unchecked") public static final Class TYPE = (Class) Class.getPrimitiveClass("int");

Глядя на вызов Class.getPrimitiveClass("int") может возникнуть соблазн выпилить его и заменить на:

@SuppressWarnings("unchecked") public static final Class TYPE = int.class;

Самое удивительное, что JDK с подобными изменениями (для всех примитивов) соберётся, а виртуальная машина запустится. Правда проработает она недолго:

java.lang.IllegalArgumentException: Component type is null at jdk.internal.misc.Unsafe.allocateUninitializedArray(java.base/Unsafe.java:1379) at java.lang.StringConcatHelper.newArray(java.base/StringConcatHelper.java:458) at java.lang.StringConcatHelper.simpleConcat(java.base/StringConcatHelper.java:423) at java.lang.String.concat(java.base/String.java:1968) at jdk.internal.util.SystemProps.fillI18nProps(java.base/SystemProps.java:165) at jdk.internal.util.SystemProps.initProperties(java.base/SystemProps.java:103) at java.lang.System.initPhase1(java.base/System.java:2002)

Ошибка вылезает вот здесь :

class java.lang.StringConcatHelper < @ForceInline static byte[] newArray(long indexCoder) < byte coder = (byte)(indexCoder >> 32); int index = (int)indexCoder; return (byte[]) UNSAFE.allocateUninitializedArray(byte.class, index >

С упомянутыми изменениями byte.class возвращает null и ломает ансейф.

Spring Data JPA позволяет объявить частично работоспособный репозиторий

Завершу статью курьёзной ошибкой, возникшей на стыке Спринг Даты и Хибернейта. Вспомним, как мы объявляем репозиторий, обслуживающий некую сущность:

@Entity public class SimpleEntity < @Id private Integer id; @Column private String name; >public interface SimpleRepository extends JpaRepository

Опытные пользователи знают, что при поднятии контекста Спринг Дата проверяет все репозитории и сразу валит всё приложение при попытке описать, к примеру, кривой запрос:

public interface SimpleRepository extends JpaRepository  < @Query("слышь, парень, мелочь есть?") OptionalfindLesserOfTwoEvils(); >

Однако, ничто не мешает нам объявить репозиторий с левым типом ключа:

public interface SimpleRepository extends JpaRepository

Этот репозиторий не только поднимется, но и будет частично работоспособен, например, метод findAll() отработает "на ура". А вот методы, использующие ключ ожидаемо упадут с ошибкой:

IllegalArgumentException: Provided id of the wrong type for class SimpleEntity. Expected: class java.lang.Integer, got class java.lang.Long

Всё дело в том, что Спринг Дата не сравнивает классы ключа сущности и ключа привязанного к ней репозитория. Происходит это не от хорошей жизни, а из-за неспособности Хибернейта выдать правильный тип ключа в определённых случаях: https://hibernate.atlassian.net/browse/HHH-10690

В жизни я встретил подобное только один раз: в тестах (трольфейс) самой Спринг Даты, например, используемый в тестах o.s.d.j.r.query.PartTreeJpaQueryIntegrationTests$UserRepository типизирован Long -ом, а в сущности User используется Integer . И это работает!

На этом всё, надеюсь, мой обзор был вам полезен и интересен.

Поздравляю вас с наступившим Новым годом и желаю копать яву глубже и шире!

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *