Мой ответ довольно длинный, поэтому я разделил его на три раздела. Поскольку вопрос касается математики с плавающей запятой, я делаю акцент на том, что на самом деле делает машина. Я также сделал это для двойной точности (64 бит), но этот аргумент применим в равной степени к любой арифметике с плавающей запятой.
Преамбула
IEEE 754 номер двойной бинарной с плавающей запятой (binary64) представляет собой номер формы
value = (-1) ^ s * (1.m51m50 ... m2m1m0 ) 2 * 2e-1023
в 64 бит:
- Первый бит является битом знака :
1
, если число отрицательно,0
в противном случае.- Следующие 11 бит являются показателем , который является offset на 1023. Другими словами, после чтение битов экспоненты из числа двойной точности 1023 должно быть вычтено для получения мощности двух.
- Остальные 52 бита представляют собой значение (или мантисса). В мантиссе «подразумеваемый»
1.
всегда пропускается, поскольку самый старший бит любого двоичного значения равен1
.1 - IEEE 754 допускает концепцию с нулевым значением -
+0
и-0
обрабатываются по-разному:1 / (+0)
- положительная бесконечность;1 / (-0)
- отрицательная бесконечность. Для нулевых значений биты мантиссы и экспоненты равны нулю. Примечание: нулевые значения (+0 и -0) явно не классифицируются как denormal2.2 - Это не относится к денормальным номерам , которые имеют показатель смещения нуля (и подразумевается
0.
). Диапазон денормальных чисел двойной точности dmin ≤ | x | ≤ dmax, где dmin (наименьшее представимое ненулевое число) составляет 2-1023 - 51 (≈ 4,94 * 10-324) и dmax (наибольшее денормальное число, для которого мантисса полностью состоит из1
s) составляет 2-1023 + 1 - 2-1023 - 51 (≈ 2.225 * 10-308).Превращение числа двойной точности в двоичный
Существует множество онлайн-конвертеров для преобразования двойной точности (например, в binaryconvert.com ), но здесь приведен пример кода C # для получения представления IEEE 754 для числа двойной точности (я разделяю три части с двоеточиями (
:
):public static string BinaryRepresentation(double value) { long valueInLongType = BitConverter.DoubleToInt64Bits(value); string bits = Convert.ToString(valueInLongType, 2); string leadingZeros = new string('0', 64 - bits.Length); string binaryRepresentation = leadingZeros + bits; string sign = binaryRepresentation[0].ToString(); string exponent = binaryRepresentation.Substring(1, 11); string mantissa = binaryRepresentation.Substring(12); return string.Format("{0}:{1}:{2}", sign, exponent, mantissa); }
Достижение точки: исходный вопрос
(Перейти к нижней части для версии TL; DR)
Катон Джонстон (вопросник) спросил, почему 0.1 + 0.2! = 0.3.
Написано в двоичном (с двоеточиями, разделяющими три части), представления IEEE 754 значений:
0.1 => 0:01111111011:1001100110011001100110011001100110011001100110011010 0.2 => 0:01111111100:1001100110011001100110011001100110011001100110011010
Обратите внимание, что мантисса состоит из повторяющихся цифр
0011
. ключ к тому, почему есть какие-либо ошибки в расчетах - 0,1, 0,2 и 0,3 не могут быть представлены в двоичной форме точно в конечном числе двоичных битов не более 1/9, 1/3 или 1/7 могут быть представлены точно в десятичных разрядах .Преобразование экспонентов в десятичные, удаление смещения и повторное добавление подразумеваемых
1
(в квадратных скобках), 0,1 и 0,2 :0.1 = 2^-4 * [1].1001100110011001100110011001100110011001100110011010 0.2 = 2^-3 * [1].1001100110011001100110011001100110011001100110011010
Чтобы добавить два числа, показатель должен быть одинаковым, т. е.
0.1 = 2^-3 * 0.1100110011001100110011001100110011001100110011001101(0) 0.2 = 2^-3 * 1.1001100110011001100110011001100110011001100110011010 sum = 2^-3 * 10.0110011001100110011001100110011001100110011001100111
Поскольку сумма не имеет вид 2n * 1. { bbb} мы увеличиваем показатель на единицу и сдвигаем десятичную ( двоичную ) точку, чтобы получить:
sum = 2^-2 * 1.0011001100110011001100110011001100110011001100110011(1)
В мантиссе сейчас 53 бит (53-й квадрат скобки в строке выше). Режим округления по умолчанию для IEEE 754 равен ' Round to Nearest ' - то есть, если число x падает между двумя значениями a и b выбрано значение, в котором наименьший значащий бит равен нулю.
a = 2^-2 * 1.0011001100110011001100110011001100110011001100110011 x = 2^-2 * 1.0011001100110011001100110011001100110011001100110011(1) b = 2^-2 * 1.0011001100110011001100110011001100110011001100110100
Обратите внимание, что a и b отличаются только последним битом;
...0011
+1
=...0100
. В этом случае значение с младшим значащим разрядом равно b , поэтому сумма равна:sum = 2^-2 * 1.0011001100110011001100110011001100110011001100110100
TL; DR
Запись
0.1 + 0.2
в двоичном представлении IEEE 754 (с двоеточиями, разделяющими три части) и сравнивая его с0.3
, это (я положил отдельные биты в квадратные скобки):0.1 + 0.2 => 0:01111111101:0011001100110011001100110011001100110011001100110[100] 0.3 => 0:01111111101:0011001100110011001100110011001100110011001100110[011]
Преобразован обратно к десятичной, эти значения:
0.1 + 0.2 => 0.300000000000000044408920985006... 0.3 => 0.299999999999999988897769753748...
Разница в точности равна 2-54, что составляет ~ 5.5511151231258 × 10-17 - незначительно (для многих приложений) по сравнению с исходными значениями.
Сравнение последних нескольких бит числа с плавающей запятой по своей сути опасно, как и любой, кто читает знаменитый «
, что каждый компьютерный ученый должен знать о арифметике с плавающей точкой » (который охватывает все основные части этого ответа).Большинство калькуляторов используют дополнительные охранные цифры , чтобы обойти эту проблему, так как
0.1 + 0.2
даст0.3
: последние несколько биты округлены.
Вы можете использовать конструктор DataFrame
с lists
, созданный путем преобразования в numpy array
с помощью values
с tolist
:
import pandas as pd
d1 = {'teams': [['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG'],
['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG']]}
df2 = pd.DataFrame(d1)
print (df2)
teams
0 [SF, NYG]
1 [SF, NYG]
2 [SF, NYG]
3 [SF, NYG]
4 [SF, NYG]
5 [SF, NYG]
6 [SF, NYG]
df2[['team1','team2']] = pd.DataFrame(df2.teams.values.tolist(), index= df2.index)
print (df2)
teams team1 team2
0 [SF, NYG] SF NYG
1 [SF, NYG] SF NYG
2 [SF, NYG] SF NYG
3 [SF, NYG] SF NYG
4 [SF, NYG] SF NYG
5 [SF, NYG] SF NYG
6 [SF, NYG] SF NYG
И для нового DataFrame
:
df3 = pd.DataFrame(df2['teams'].values.tolist(), columns=['team1','team2'])
print (df3)
team1 team2
0 SF NYG
1 SF NYG
2 SF NYG
3 SF NYG
4 SF NYG
5 SF NYG
6 SF NYG
Решение с apply(pd.Series)
очень медленно:
#7k rows
df2 = pd.concat([df2]*1000).reset_index(drop=True)
In [89]: %timeit df2['teams'].apply(pd.Series)
1 loop, best of 3: 1.15 s per loop
In [90]: %timeit pd.DataFrame(df2['teams'].values.tolist(), columns=['team1','team2'])
1000 loops, best of 3: 820 µs per loop
Более простое решение:
pd.DataFrame(df2.teams.tolist(), columns=['team1', 'team2'])
Выход,
team1 team2
-------------
0 SF NYG
1 SF NYG
2 SF NYG
3 SF NYG
4 SF NYG
5 SF NYG
6 SF NYG
7 SF NYG
Если вы хотите разбить столбец с разделителями, а не на списки, вы также можете сделать следующее:
pd.DataFrame(df.teams.str.split('<delim>', expand=True).values,
columns=['team1', 'team2'])
Кажется, что синтаксически более простой способ и, следовательно, легче запомнить, в отличие от предлагаемых решений. Я предполагаю, что столбец называется «мета» в dataframe df:
df2 = pd.DataFrame(df['meta'].str.split().values.tolist())