Об 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» в двоичном формате?
- Знаковый бит равен 0, что указывает на положительное число.
- Показатель степени «10000010», что равно 3 после вычитания 127.
- Значение, соответствующее мантиссе, равно «1,011011».
- Таким образом, окончательное число с плавающей запятой — «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. Код реализации этого метода слишком длинный.
- Определяет, является ли начало знаком "-" или "+", то есть положительным или отрицательным числом.
- Определите, является ли это NaN, и если да, верните NaN.
- Определить, является ли это Infinity, и если да, вернуть Infinity.
- Определите, является ли это шестнадцатеричным числом с плавающей запятой Java, начинающимся с 0x, и если да, преобразуйте шестнадцатеричное число с плавающей запятой в десятичное число с плавающей запятой в соответствии со стандартом IEEE-754. Например, если строка 0x12.3512p+11f, она будет преобразована в 37288.562.
- Определить, содержит ли строка символ E, то есть является ли она экспоненциальной записью, и если да, то ее нужно обработать. Например, если строка 10001.222E+2, она будет преобразована в 1000122.2.
- Нам также нужно иметь дело с точностью чисел с плавающей запятой.Этот процесс более сложный.Мы знаем, что точность чисел с плавающей запятой составляет 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
Помощь, может быть вознаграждена:
Добро пожаловать, чтобы следовать: