Плавающие с точки зрения исходного кода JDK

Java задняя часть JVM исходный код
Плавающие с точки зрения исходного кода JDK

Об IEEE 754

Прежде чем рассматривать Float, вам необходимо понять стандарт IEEE 754. Этот стандарт определяет формат чисел с плавающей запятой и некоторые специальные значения, а также формат и метод преобразования двоичных и десятичных чисел с плавающей запятой в компьютерах. Определены четыре метода представления значений с плавающей запятой: одинарная точность (32 бита), двойная точность (64 бита), расширенная одинарная точность (более 43 бит) и расширенная двойная точность (более 79 бит). Большинство языков программирования поддерживают одинарную и двойную точность, а обсуждаемый здесь Float — это реализация одинарной точности в Java.

Представление чисел с плавающей запятой

Число с плавающей запятой состоит из трех частей, как показано на рисунке ниже, бита знака s, показателя степени e и мантиссы f.

这里写图片描述
напишите сюда описание фото

Для вычисления у нас есть соответствующая ей формула.По этой формуле будет проще.Значение числа с плавающей запятой:

Можно видеть, что старший бит из 32 битов является идентификатором символа, 1 представляет отрицательное число, а 0 представляет положительное число. Показатель степени составляет 8 бит, что на самом деле может быть от 0 до 255, но чтобы быть положительным или отрицательным, реальный показатель необходимо вычесть на 127, а основание фиксируется на 2. Остальные 23 бита представляют собой мантиссу, но по умолчанию впереди добавляется 1. Таким образом, с помощью вышеизложенного можно представить число с плавающей запятой.

Давайте возьмем пример, что такое число с плавающей запятой, представленное «010000010011011000000000000000000» в двоичном формате?

  1. Знаковый бит равен 0, что указывает на положительное число.
  2. Показатель степени «10000010», что равно 3 после вычитания 127.
  3. Значение, соответствующее мантиссе, равно «1,011011».
  4. Таким образом, окончательное число с плавающей запятой — «1011,011», которое преобразуется в «11,375» в десятичном виде.

Обзор поплавка

Основная функция класса Float в Java состоит в том, чтобы инкапсулировать базовый тип float и предоставить некоторые методы для работы с типами float, такие как метод преобразования из типа float в тип String или метод преобразования из типа String в тип float, и, конечно же, он также включает в себя другие типы метода преобразования.

структура наследования

--java.lang.Object
  --java.lang.Number
    --java.lang.Float

главный атрибут

public static final float POSITIVE_INFINITY = 1.0f / 0.0f;
public static final float NEGATIVE_INFINITY = -1.0f / 0.0f;
public static final float NaN = 0.0f / 0.0f;
public static final float MAX_VALUE = 0x1.fffffeP+127f;
public static final float MIN_NORMAL = 0x1.0p-126f;
public static final float MIN_VALUE = 0x0.000002P-126f;
public static final int MAX_EXPONENT = 127;
public static final int MIN_EXPONENT = -126;
public static final int SIZE = 32;
public static final int BYTES = SIZE / Byte.SIZE;
public static final Class<Float> TYPE = (Class<Float>) Class.getPrimitiveClass("float");
  • POSITIVE_INFINITYИспользуется для представления положительной бесконечности.Согласно стандарту IEEE 754 с плавающей запятой, любое конечное положительное число, деленное на 0, является положительной бесконечностью, а значение положительной бесконечности равно 0x7f800000.
  • NEGATIVE_INFINITYИспользуется для представления отрицательной бесконечности, любое конечное отрицательное число, деленное на 0, является отрицательной бесконечностью, а значение отрицательной бесконечности равно 0xff800000.
  • NaNИспользуется для представления условий ошибки при обработке вычислений, таких как деление 0 на 0 или квадратный корень из отрицательного числа. Для чисел с плавающей запятой одинарной точности стандарт IEEE указывает, что поле экспоненты NaN равно 1, а поле мантиссы не равно нулю. Это не требует определенного поля мантиссы, поэтому NaN на самом деле не одно, а семейство. Значение, определенное здесь Java, равно 0x7fc00000.
  • MAX_VALUEИспользуется для представления наибольшего значения с плавающей запятой, оно определяется как 0x1.fffffeP+127f, где 0x представляет шестнадцатеричное число, 1.fffffe представляет шестнадцатеричное десятичное число, P представляет 2, + представляет несколько раз, здесь 2 127-я степень, последняя f преобразуется в тип с плавающей запятой. Таким образом, окончательное максимальное значение равно 3,4028235E38.
  • MIN_NORMALИспользуется для представления минимального стандартного значения, оно определяется как 0x1.0p-126f, что на самом деле равно 2 в степени -126, а значение равно 1,17549435E-38f.
  • MIN_VALUEИспользуется для представления минимального значения числа с плавающей запятой, оно определяется как 0x0.000002P-126f, а конечное значение равно 1.4e-45f.
  • MAX_EXPONENTИспользуется для представления максимального значения экспоненты, которое здесь установлено на 127, что также соответствует стандарту IEEE 754 для операций с плавающей запятой.
  • MIN_EXPONENTИспользуется для представления минимального значения экспоненты, которое равно -126, как указано в стандарте IEEE 754 с плавающей запятой.
  • SIZEКоличество битов, используемых для представления двоичного значения с плавающей запятой, значение равно 32, статическая переменная и неизменяемая.
  • BYTESКоличество байтов, используемых для представления двоичного значения с плавающей запятой, значение равноSIZEразделить наByte.SIZE, результат 4.
  • TYPEЗначение toString равноfloat.
    СортgetPrimitiveClassявляется родным методом, вClass.cодин изJava_java_lang_Class_getPrimitiveClassМетод ему соответствует, поэтому уровень JVM пройдетJVM_FindPrimitiveClassФункция получает jclass в соответствии со строкой "float" и, наконец, достигает уровня Java какClass<Float>.
JNIEXPORT jclass JNICALL
Java_java_lang_Class_getPrimitiveClass(JNIEnv *env,
                                       jclass cls,
                                       jstring name)
{
    const char *utfName;
    jclass result;

    if (name == NULL) {
        JNU_ThrowNullPointerException(env, 0);
        return NULL;
    }

    utfName = (*env)->GetStringUTFChars(env, name, 0);
    if (utfName == 0)
        return NULL;

    result = JVM_FindPrimitiveClass(env, utfName);

    (*env)->ReleaseStringUTFChars(env, name, utfName);

    return result;
}

когдаTYPEПри выполнении toString логика следующая, тогда собственноgetNameфункция определяет его значение,getNameнативным методомgetName0получить имя из уровня JVM,

public String toString() {
        return (isInterface() ? "interface " : (isPrimitive() ? "" : "class "))
            + getName();
    }

getName0Соответствующее имя получается в соответствии с массивом, и JVM может получить индекс массива соответствующего типа в соответствии с классом уровня Java.Например, если индекс здесь равен 6, имя "плавающее".

const char* type2name_tab[T_CONFLICT+1] = {
  NULL, NULL, NULL, NULL,
  "boolean",
  "char",
  "float",
  "double",
  "byte",
  "short",
  "int",
  "long",
  "object",
  "array",
  "void",
  "*address*",
  "*narrowoop*",
  "*conflict*"
};

основной метод

parseFloat

public static float parseFloat(String s) throws NumberFormatException {
        return FloatingDecimal.parseFloat(s);
    }

Преобразование строк осуществляется вызовом метода parseFloat класса FloatingDecimal.Класс FloatingDecimal в основном предоставляет IEEE-754. Код реализации этого метода слишком длинный.

  1. Определяет, является ли начало знаком "-" или "+", то есть положительным или отрицательным числом.
  2. Определите, является ли это NaN, и если да, верните NaN.
  3. Определить, является ли это Infinity, и если да, вернуть Infinity.
  4. Определите, является ли это шестнадцатеричным числом с плавающей запятой Java, начинающимся с 0x, и если да, преобразуйте шестнадцатеричное число с плавающей запятой в десятичное число с плавающей запятой в соответствии со стандартом IEEE-754. Например, если строка 0x12.3512p+11f, она будет преобразована в 37288.562.
  5. Определить, содержит ли строка символ E, то есть является ли она экспоненциальной записью, и если да, то ее нужно обработать. Например, если строка 10001.222E+2, она будет преобразована в 1000122.2.
  6. Нам также нужно иметь дело с точностью чисел с плавающей запятой.Этот процесс более сложный.Мы знаем, что точность чисел с плавающей запятой составляет 7 значащих цифр.Почему здесь 7? Возвращаясь к стандарту IEEE-754, преобразование 32-битного двоичного кода в формат с плавающей запятой основано на формуле$(-1)^s*(1.f)*2^{(e-127)}$Преобразовав, вы можете видеть, что его точность определяется мантиссом, а мантисса имеет 23 бита, тогда$2^{23}=8388608$, значение находится между$10^{6}$прибыть$10^{7}$, поэтому он может гарантировать 6-битное точное число, но 7-битное не обязательно, здесь относительно десятичной точки, поэтому точное значение, соответствующее всему типу с плавающей запятой, составляет 7-битные эффективные цифры, а 8-бит бит не обязательно способен к точному представлению. Вот несколько примеров для сравнения: строка 30.200019 преобразуется в 30.20002 с семью значащими цифрами; строка 30.200001 преобразуется в 30.2 с семью значащими цифрами, но все последние равны 0, поэтому они опущен; строка 30000.2196501 преобразуется в 30000.219 , всего восемь значащих цифр, что может точно представлять восемь цифр.

Конструктор

public Float(String s) throws NumberFormatException {
        value = parseFloat(s);
    }
public Float(float value) {
        this.value = value;
    }
public Float(double value) {
        this.value = (float)value;
    }

Предоставляются три конструктора, все они относительно просты и могут передавать значения типа String, float и double.Тип String вызовет метод parseFloat для преобразования, а тип double будет напрямую преобразован в тип float.

toString

public String toString() {
        return Float.toString(value);
    }
public static String toString(float f) {
        return FloatingDecimal.toJavaFormatString(f);
    }

Два метода toString, в основном второй, преобразуются в строки методом toJavaFormatString класса FloatingDecimal. Этот процесс преобразования также относительно сложен. Код здесь не публикуется. Процесс обработки заключается в том, чтобы сначала преобразовать число с плавающей запятой в двоичную форму стандарта IEEE-754 и определить, является ли оно положительной или отрицательной бесконечностью, и будь то NaN. Затем конвертировать из двоичного в десятичный по стандарту IEEE-754.Этот процесс очень сложен, и нужно учитывать немало моментов. Наконец, генерируется строка, соответствующая числу с плавающей запятой.

метод valueOf

public static Float valueOf(float f) {
        return new Float(f);
    }
public static Float valueOf(String s) throws NumberFormatException {
        return new Float(parseFloat(s));
    }

Существует два метода valueOf. Для типа float возвращается новый объект Float напрямую. Для строки сначала вызывается метод parseFloat, чтобы преобразовать его в float, а затем возвращается новый объект Float.

метод хххвалуе

public byte byteValue() {
        return (byte)value;
    }
public short shortValue() {
        return (short)value;
    }
public int intValue() {
        return (int)value;
    }
public long longValue() {
        return (long)value;
    }
public float floatValue() {
        return value;
    }
public double doubleValue() {
        return (double)value;
    }

В том числе byteValue, shortValue, intValue, longValue, floatValue и doubleValue и другие методы, по сути, преобразуются в соответствующий тип.

Метод floatToRawIntBits

public static native int floatToRawIntBits(float value);

JNIEXPORT jint JNICALL
Java_java_lang_Float_floatToRawIntBits(JNIEnv *env, jclass unused, jfloat v)
{
    union {
        int i;
        float f;
    } u;
    u.f = (float)v;
    return (jint)u.i;
}

floatToRawIntBits — это собственный метод, который в основном преобразует число с плавающей запятой в целое число, соответствующее двоичной форме стандарта IEEE 754. Логика обработки соответствующего локального метода проста и эффективна, то есть преобразование int и float реализовано через объединение, и, наконец, преобразовано в java integer jint.

Метод floatToIntBits

public static native float intBitsToFloat(int bits);

JNIEXPORT jfloat JNICALL
Java_java_lang_Float_intBitsToFloat(JNIEnv *env, jclass unused, jint v)
{
    union {
        int i;
        float f;
    } u;
    u.i = (long)v;
    return (jfloat)u.f;
}

Этот метод соответствует методу floatToRawIntBits, а floatToIntBits также является локальным методом и в основном преобразует целое число, соответствующее двоичной форме стандарта IEEE 754, в число с плавающей запятой. Можно видеть, что его локальная реализация также реализована объединением, которое завершает преобразование int в число с плавающей запятой и, наконец, преобразует его в jfloat с плавающей запятой java.

Метод floatToIntBits

public static int floatToIntBits(float value) {
        int result = floatToRawIntBits(value);
        if ( ((result & FloatConsts.EXP_BIT_MASK) ==
              FloatConsts.EXP_BIT_MASK) &&
             (result & FloatConsts.SIGNIF_BIT_MASK) != 0)
            result = 0x7fc00000;
        return result;
    }

Этот метод в основном получает целое число, соответствующее стандарту IEEE 754, путем вызова floatToRawIntBits, а затем использует две маски FloatConsts.EXP_BIT_MASK и FloatConsts.SIGNIF_BIT_MASK, чтобы определить, является ли оно NaN, а 0x7fc00000 соответствует NaN.

метод hashCode

public int hashCode() {
        return Float.hashCode(value);
    }
public static int hashCode(float value) {
        return floatToIntBits(value);
    }

В основном обратите внимание на второй метод hashCode, который реализуется путем вызова floatToIntBits, поэтому возвращаемый им хеш-код на самом деле является целым числом, соответствующим стандарту IEEE 754 для чисел с плавающей запятой.

isFinite и isInfinite

public static boolean isFinite(float f) {
        return Math.abs(f) <= FloatConsts.MAX_VALUE;
    }
public static boolean isInfinite(float v) {
        return (v == POSITIVE_INFINITY) || (v == NEGATIVE_INFINITY);
    }

Эти два метода используются для определения того, является ли число с плавающей запятой конечным или бесконечным. Логика очень проста.Число, абсолютное значение которого меньше FloatConsts.MAX_VALUE, является конечным числом.Значение FloatConsts.MAX_VALUE равно 3,4028235e+38f, что фактически совпадает с MAX_VALUE, определенным в предыдущем классе Float. Является ли это бесконечным числом, судят по POSITIVE_INFINITY и NEGATIVE_INFINITY.

метод isNaN

public static boolean isNaN(float v) {
        return (v != v);
    }

Он используется, чтобы судить, является ли число с плавающей запятой NaN Логика этого метода очень проста, прямая (v != v), почему это можно сделать? Потому что оговорено, что NaN не равен никакому значению, в том числе самому себе. Так что эта часть логики будет делаться в JVM или локально, так что о ней можно будет судить непосредственно в сравнении.

максимальный и минимальный методы

public static float max(float a, float b) {
        return Math.max(a, b);
    }
public static float min(float a, float b) {
        return Math.min(a, b);
    }

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

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

public static int compare(float f1, float f2) {
        if (f1 < f2)
            return -1;           
        if (f1 > f2)
            return 1;            

        int thisBits    = Float.floatToIntBits(f1);
        int anotherBits = Float.floatToIntBits(f2);

        return (thisBits == anotherBits ?  0 : 
                (thisBits < anotherBits ? -1 : 
                 1));                          
    }

Возвращает -1, если f1 меньше f2, иначе возвращает 1. Если приведенное выше прямое сравнение невозможно, используйте метод floatToIntBits для преобразования f1 и f2 в целые числа, соответствующие стандарту IEEE 754, а затем сравните их. Возвращает 0, если равно, в противном случае возвращает -1 или 1.

Ниже приведенырекламироватьа такжеСвязанное Чтение

========Время рекламы========

Моя новая книга «Анализ дизайна ядра Tomcat» продана на Jingdong, нуждающиеся друзья могут обратиться кitem.JD.com/12185360.Контракт…Зарезервировать. Спасибо друзья.

Зачем писать «Анализ проектирования ядра Tomcat»

=========================

Связанное чтение:

Объект с точки зрения исходного кода JDK
Долго с точки зрения исходного кода JDK
Целое число с точки зрения исходного кода JDK
Достаточно ли изменчив, чтобы гарантировать синхронизацию данных?
Разговор об основных типах данных Java
Оптимизация одновременных блокировок с точки зрения исходного кода JDK
Блокировка и пробуждение потоков с точки зрения исходного кода JDK
С точки зрения исходного кода JDK тайм-аут параллельной конкуренции
Прерывание параллельных потоков Java с точки зрения исходного кода JDK
Справедливость параллелизма Java с точки зрения исходного кода JDK
Как обеспечить атомарность параллелизма Java с точки зрения исходного кода JDK
Байт с точки зрения исходного кода JDK
Логическое значение с точки зрения исходного кода JDK
Кратко с точки зрения исходного кода JDK

Помощь, может быть вознаграждена:

Добро пожаловать, чтобы следовать: