Следующий код Python3+ пытается скомпилировать сценарий Cpp и использовать его для преобразования из числа с плавающей запятой в целое число, оставляя память нетронутой; это выглядит следующим образом:
import sys, os
import numpy as np
import matplotlib.pyplot as plt
# Build a C++ Script that accepts a str,
# converts it to a float, and prints
# the result of the operation
def build():
script = """
#include<iostream>
int main(int argc, char *argv[]){
float f = std::stof(argv[1]);
int i = *(short *)&f;
std::cout << f << " " << i <<std::endl;
return 0;
}
"""
with open('script.cpp', 'w') as f:
f.write(script)
return 1
# Loads the results from the C++ script
def load_results():
x,y = [],[]
with open('results-ctest.txt', 'r') as f:
result = f.readlines()
for _ in result:
local = _.split(' ')
x.append(float(local[0]))
y.append(int(local[1][:-2]))
return x,y
# Plots the results from the C++ script
def show_results(x,y):
# Define a figure
f,ax = plt.subplots()
# Plot results
ax.scatter(x,y)
# Format the axis according to the shown figure
ax.set_xticks(np.linspace(min(x), max(x), 20))
ax.set_yticks(np.linspace(min(y), max(y), 20))
plt.show()
if __name__=='__main__':
# build the C++ script
build()
# Compile the C++ script
# and clean the previous results
# by removing "results-ctest.txt"
os.system(f'g++ script.cpp')
os.system('rm results-ctest.txt')
# Generate 500 floats between -1.000.000 and 1.000.000
# and pass them to the C++ script
numbers=np.linspace(-1e6, 1e6, 500)
for number in numbers:
os.system(f'./a.out {number}>> results-ctest.txt')
# Open the results of the C++ script and
# split the input from the output
x,y = load_results()
# Produce the figure and open
# a window for it
show_results(x,y)
Очевидная проблема заключается в том, что (выходные) целые числа против (входных) чисел с плавающей запятой следующие:
Тем не менее, если и "int", и "float" реализованы с 4 байтами согласно следующему рисунку, тогда вход и выход должны иметь одинаковый знак.
Резюме состоит в том, что то, что следует ниже, используется для создания int с использованием C++, и его знак не сохраняется в соответствии с тем, что показано на первом рисунке.
float f = std::stof(argv[1]);
int i = *(short *)&f;
Спасибо
Обновлено: Суть в том, что была опечатка. Я редактирую вопрос, чтобы показать «правильный» сюжет
Как указано в комментариях, проблема заключалась в следующей строке:
int i = *(short *)&f;
что должно было быть:
int i = *(int *)&f;
и, таким образом, получить следующую диаграмму:
Билл Линч дал реальный ответ на ваш вопрос. Кроме того, вы вызываете неопределенное поведение, см., например. en.cppreference.com/w/c/language/object#Strict_aliasing. Если вы хотите добиться того же, но без неопределенного поведения, используйте memcpy
.
Это не то, как вы конвертируете числа с плавающей запятой в целые числа в С++. Какой учебник C++ научил вас этому? Что бы это ни было, вам нужен лучший учебник C++.
@SamVarshavchik Я сомневаюсь, что речь идет о числовом преобразовании, я думаю, он действительно хочет посмотреть, как байтовое представление чисел с плавающей запятой будет отображаться в целые числа.
Какой TF ваш вопрос о Python? Пожалуйста, убедитесь, что вы сократили код в своих вопросах до минимального воспроизводимого примера , прежде чем спрашивать здесь! Как новый пользователь, также пройдите тур и прочитайте Как спросить.
Краткий поверхностный ответ — это то, на что указали BillLynch и PaulMcKenzie: в преобразовании есть опечатка (см. редактирование). Вопрос к опытным пользователям: стоит ли исправлять опечатку в заголовке? Думаю, нет.
@UlrichEckhardt Я отредактировал его, чтобы сделать код более читаемым. Я надеюсь, что теперь качество кода лучше соответствует правилам сайта.
Первая проблема с вашим кодом заключается в том, что это неопределенное поведение при чтении данных одного типа как другого несвязанного типа без определенных свойств, таких как общий префикс, или с использованием нескольких типов, таких как std::byte
или char
.
std::bit_cast
— это c++20 способ сделать это правильно.
Вторая проблема заключается в том, что то, что означают точные биты, зависит от таких вещей, как порядок следования байтов на вашем компьютере и используемый вами стандарт операций с плавающей запятой. Сейчас они относительно стандартны, но не полностью.
Ваша третья проблема заключается в том, что размер short, int и float зависит от платформы и компилятора. Вы можете использовать целочисленные типы фиксированного размера, например std::int32_t
, и вам следует использовать их вместо int
или short
. Часто short
— 16 бит, а int
— 32, но это далеко не универсально. float
быть 32-битным очень часто.
Так:
std::int32_t i = std::bit_cast<std::int32_t>(f);
std::cout << f << " " << i <<std::endl;
по крайней мере избавится от большинства безумных проблем.
Я не знаю навскидку, каковы проблемы преобразования порядков байтов при преобразовании из с плавающей запятой в целые числа. Что бы я хотел, так это:
std::uint32_t ui = std::bit_cast<std::uint32_t>(f);
std::cout << f << " 0b";
for (int i = 0; i < 32; ++i)
std::cout << (ui&(1<<i));
std::int32_t i = std::bit_cast<std::int32_t>(f);
std::cout << " " << i <<std::endl;
чтобы также выгрузить биты f
в соответствии с порядком байтов вашей архитектуры. Затем возьмите значение с плавающей запятой, чье битовое представление вам известно (и оно не равно нулю), и посмотрите, что оно генерирует.
Это отличный комментарий, который расширяет опечатку (пожалуйста, прочитайте редактирование) и добавляет больше информации. Я приму это, надеясь, что вы найдете время, чтобы добавить упоминание о том, как int i = *(int *)&f
сохраняет знак, вероятно, в большинстве реализаций vainilla, насколько я понимаю. Спасибо
short
скорее всего 2 байта, а не 4.