Предположим, у вас есть десятичное число с q значащие цифры:
d q -1 . д * 1 013 ** ** 1014 1015 * д -2 * +1017 * д д * * -3 тысяча двадцать-две * +1023 * ... d 0 ,
и давайте также сделаем его десятичным числом с плавающей точкой, то есть мы будем масштабировать его до степени десяти:
*** 1 033 1 034 * д * ** 1037 тысяча тридцать-шесть * д * * -1 одна тысяча тридцать восемь * * 1 039. д * ** тысяча сорок две тысячу сорок три * д * * -2 тысячу сорок-четыре * 1 045 *d д -3 ... d 0 • 10 е .
Далее мы конвертируем это число в float
.Многие такие числа не могут быть точно представлены в float
, поэтому мы округляем результат до ближайшего представимого значения.(Если есть связь, мы округляем, чтобы сделать младшую цифру четной.) Результатом (если мы не переполнились или не опустились) будет некоторое число с плавающей запятой x .По определению чисел с плавающей запятой (в C 2018 5.2.4.2.2 3) оно представлено некоторым количеством цифр в некоторой базе, масштабированной этой базой до степени.Предположим, что это база два, x :
b p −1 . б * +1079 ** * р тысячу восемьдесят один * +1082 * -2 * +1083 ** +1084 * B * ** тысяча восемьдесят шесть тысяча восемьдесят семь * р -3 ... B 0 • 2 p .
Далее мы конвертируем float
x обратно в десятичную форму с q значащие цифры.Точно так же значение float
x может быть не совсем точно представлено в виде десятичного числа с q цифрами, поэтому мы можем получить некоторое возможное новое число:
N д -1 . п * ** тысяча сто двадцать-один ** тысяча сто двадцать-дв * д одна тысячи сто двадцать три * * -2 тысяча сто двадцать четыре * * п тысяча сто двадцать-шесть * +1129 * д -3 ... * * п тысяча сто тридцать две * ** +1134 тысяча сто тридцать-три * 0 • 10 * * 1 137 м * +1138 ** * тысяча сто тридцать-девять.
Оказывается, что для любого формата float
существует некоторое число q , такое, что если десятичное число, с которого мы начинали, ограничено q цифры, то результат этого преобразования туда и обратно будет равен исходному числу.Каждая десятичная цифра q цифр, округленная до float
, а затем обратно до q десятичных цифр, приводит к начальному числу.
В стандарте 2018 C, пункт 5.2.4.2.2, пункт 12, говорит нам, что это число q должно быть не менее 6 (реализация C может поддерживать большие значения), и реализация C должна определить для него символ препроцессора (вfloat.h
) называется FLT_DIG
.
Итак, учитывая номер вашего примера 1.4, когда мы преобразуем его в float
в базовом 32-разрядном двоичном формате IEEE-754, мы получим ровно 1,39999997615814208984375 (то естьего математическое значение, показанное в десятичном виде для удобства; фактические биты в объекте представляли его в двоичном виде).Когда мы конвертируем это в десятичную с полной точностью, мы получаем «1.39999997615814208984375».Но если мы преобразуем его в десятичное с округлением до шести цифр, мы получим «1.40000».Таким образом, 1.4 выживает в обоих направлениях.
Другими словами, неверно в целом, что шесть десятичных цифр могут быть представлены в float
без изменений, но верно, что float
несет достаточно информации , чтобы вы могли восстановить шесть десятичных цифр из нее.
Конечно, как только вы начнете делать арифметику, ошибки, как правило, составят, и вы не сможетебольше полагаться на шесть десятичных цифр.