Числа с плавающей точкой.
Автор: Ильдар Валеев
Стандарт регламентирует тип данных с плавающей запятой "float" (32 бита или 4 байта).
Такое кодирование используется в стандарте IEEE для нормализованных чисел с плавающей запятой одинарной точности.
Следующие три поля описывают число с плавающей запятой одинарной точности:
S: Знак числа. Значения 0 и 1 представляют положительные и отрицательные значения, соответственно. Один бит.
E: Экспонента числа, основание 2. Этому полю отводится 8 бит. Экспонента уменьшается на 127.
F: Дробная часть мантиссы числа, основание 2. 23 бита отведено этому полю.
Таким образом, значение числа с плавающей запятой вычисляется так:
(-1)^S * 2^(E-Bias) * F1
Описание формата:
float identifier;
+-------+-------+-------+-------+ |byte 0 |byte 1 |byte 2 |byte 3 | Число с плавающей точкой S| E | F | одинарной точности +-------+-------+-------+-------+ 1|<- 8 ->|<-------23 bits------>| <------------32 bits------------>
Смещения начальных битов S, E, и F - это 0, 1, и 9, соответственно. Имейте в виду, что это математические смещения.
F1 - дробное значение, вычисляется следующим образом:
Просматриваются значения битов F.
Если первый бит равен 1, то прибавляется 0.5
Если второй бит равен 1, то прибавляется 0.25
Если третий бит равен 1, то прибавляется 0.125
Т.е. прибавляемые значения уменьшаются на единицу. Этим и ограничивается точность чисел с плавающей точкой.
Примечание:
- К этим 23 битам обычно в начало добавляют бит 1 - символизирующий целую часть F1=1.0. Далее происходит прибавление половин от предыдущих номиналов.
- Экспоненты 0x00 и 0xFF зарезервированы.
0x00 используется для представления нуля и ненормализованных величин.
0xFF используется для представления неопределенных чисел (переполнение) и NaN (Not a Number).
Спецификация IEEE описывает также числа знаковый ноль, знаковая бесконечность (переполнение), и ненормализованные значения (переполнение в обратную сторону). По спецификациям IEEE, "NaN" (не число) является системно-зависимой и не должна интерпретироваться иначе, как "NaN".
Эти выкладки помогут при разработке программного обеспечения для компьютеров и промышленных контроллеров, а также для разработки ПО для АСУТП.
Рассмотрим пример кода на Delphi, реально применяемом и эксплуатируемом в разработанной программе:
Был разработан пример, который переводит десятичное представление такого числа в его дробное значение. В десятичное представление можно привести заранее при помощи кода:
function NumToFloat(dwNum: DWORD): String;
const
EXP_BIAS = 127;
var
sFloat: String;
dwFloat: Cardinal;
snglFloat: Single; // 4 байта
bySign, byExp: Byte;
dwFractPart: Cardinal;
snglFractPart: Single;
begin
dwFloat:= dwNum;
bySign:= (dwFloat shr 31) and $1;
byExp:= Byte((dwFloat shr 23) and $FF);
if byExp = $FF then
begin
// ShowMessage('Infinity or Nan');
Result:= 'Infinity or Nan';
end
else
if byExp = $00 then
begin
// ShowMessage('0 (Zero) or Denormal');
Result:= '0 (Zero) or Denormal';
end
else
begin
dwFractPart:= Cardinal( ( Cardinal(dwFloat shl 9) shr 9 ) );
// 10 бит точность - тысячные доли - итоговая точность сотые доли
snglFractPart:= 1.0 +
((dwFractPart shr 22) and $1) * 0.5 +
((dwFractPart shr 21) and $1) * 0.25 +
((dwFractPart shr 20) and $1) * 0.125 +
((dwFractPart shr 19) and $1) * 0.0625 +
((dwFractPart shr 18) and $1) * 0.03125 +
((dwFractPart shr 17) and $1) * 0.015625 +
((dwFractPart shr 16) and $1) * 0.0078125 +
((dwFractPart shr 15) and $1) * 0.00390625 +
((dwFractPart shr 14) and $1) * 0.001953125 +
((dwFractPart shr 13) and $1) * 0.0009765625 +
// Максимальная точность
((dwFractPart shr 12) and $1) * 0.00048828125 +
((dwFractPart shr 11) and $1) * 0.000244140625 +
((dwFractPart shr 10) and $1) * 0.0001220703125 +
((dwFractPart shr 9) and $1) * 0.00006103515625 +
((dwFractPart shr 8) and $1) * 0.000030517578125 +
((dwFractPart shr 7) and $1) * 0.0000152587890625 +
((dwFractPart shr 6) and $1) * 0.00000762939453125 +
((dwFractPart shr 5) and $1) * 0.000003814697265625 +
((dwFractPart shr 4) and $1) * 0.0000019073486328125 +
((dwFractPart shr 3) and $1) * 0.00000095367431640625 +
((dwFractPart shr 2) and $1) * 0.000000476837158203125 +
((dwFractPart shr 1) and $1) * 0.0000002384185791015625 +
( dwFractPart and $1) * 0.00000011920928955078125;
snglFloat:= Power(-1, bySign) * Power(2.0, (byExp - EXP_BIAS)) * snglFractPart;
sFloat:= FloatToStr(snglFloat);
// ShowMessage(sFloat);
end;
Result:= sFloat;
end;
dwFloat:= (Byte(#$41) shl 24) + (Byte(#$F1) shl 16) + (Byte(#$EB) shl 8) + Byte(#$85);где числа могут быть взяты путем побайтного доступа к строке, содержащей число, например, сырое значение из контроллера. Скриншот программы для получения значения чисел с плавающей запятой:
Скачать пример с исходниками можно по ссылке: Пример с исходниками