Преобразования типа чисел с фиксированной точкой.
- Войдите на сайт для отправки комментариев
Число с фиксированной точкой - почти такое же, как целое, но отмасштабированное на число кратное 2^n.
Например, для 32-разрядного числа формата 16.16 старшие 16 разрядов хранят целую часть, а младшие - дробную, например, значение "2.5" будет представлено как 0x00028000.
Хотелось бы иметь быстрые (!) преобразования 32-разрядных чисел форматов 16.16, 32.0, 24.8 друг в друга а также в 16-разрядные форматы 16.0 и 8.8, а также в 8-разрядный 8.0. Н у и не забыть про 24-разрядные числа, поддерживаемые AVR.
Форматы 32.0, 16.0 и 8.0 - обычные целые числа unsigned long, unsigned int и byte.
На Паскале что-то похожее (но очень неоптимальное) может выглядеть так:
uses strings; type tb2 = record b : array[0..1]of byte; end; type tb4 = record b : array[0..3]of byte; end; function p32_16r8(d : longint) : smallint; begin (tb2(result)).b[0] := (tb4(d)).b[1]; (tb2(result)).b[1] := (tb4(d)).b[2]; end; function p32_16r0(d : longint) : smallint; begin (tb2(result)).b[0] := (tb4(d)).b[0]; (tb2(result)).b[1] := (tb4(d)).b[1]; end; function p32_16r16(d : longint) : smallint; begin (tb2(result)).b[0] := (tb4(d)).b[2]; (tb2(result)).b[1] := (tb4(d)).b[3]; end; var i : smallint; d : longint; begin d := $12345678; i := p32_16r8(d); writeln(hex(d)); writeln(hex(i)); i := p32_16r0(d); writeln(hex(i)); i := p32_16r16(d); writeln(hex(i)); end.
а что-то похожее написать на С++ у меня не получилось (это фрагмент, строки 11, 13 и 16)
union BytesInLong { unsigned long d; byte b[4]; };
union BytesInInt { unsigned int i; byte b[2]; };
inline unsigned int c32_16r8(unsigned long d) { unsigned int r; ((BytesInInt)(r)).b[0] = ((BytesInLong)d).d[1]; return r; }
сообщает:
Arduino: 1.6.5 (Windows XP), Плата"Arduino Mega or Mega 2560, ATmega2560 (Mega 2560)" F:\Arduino\arduino-1.6.5-r2\hardware\tools\avr/bin/avr-g++ -c -g -Os -w -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -MMD -mmcu=atmega2560 -DF_CPU=16000000L -DARDUINO=10605 -DARDUINO_AVR_MEGA2560 -DARDUINO_ARCH_AVR -IF:\Arduino\arduino-1.6.5-r2\hardware\arduino\avr\cores\arduino -IF:\Arduino\arduino-1.6.5-r2\hardware\arduino\avr\variants\mega C:\DOCUME~1\ANDRIA~1.CNI\LOCALS~1\Temp\build5207322130411912797.tmp\byte_pr.cpp -o C:\DOCUME~1\ANDRIA~1.CNI\LOCALS~1\Temp\build5207322130411912797.tmp\byte_pr.cpp.o byte_pr.ino: In function 'unsigned int c32_16r8(long unsigned int)': byte_pr.ino:16:81: error: no matching function for call to 'BytesInInt::BytesInInt(unsigned int&)' byte_pr.ino:16:81: note: candidates are: byte_pr.ino:13:7: note: BytesInInt::BytesInInt() byte_pr.ino:13:7: note: candidate expects 0 arguments, 1 provided byte_pr.ino:13:7: note: BytesInInt::BytesInInt(const BytesInInt&) byte_pr.ino:13:7: note: no known conversion for argument 1 from 'unsigned int' to 'const BytesInInt&' byte_pr.ino:16:105: error: no matching function for call to 'BytesInLong::BytesInLong(long unsigned int&)' byte_pr.ino:16:105: note: candidates are: byte_pr.ino:11:7: note: BytesInLong::BytesInLong() byte_pr.ino:11:7: note: candidate expects 0 arguments, 1 provided byte_pr.ino:11:7: note: BytesInLong::BytesInLong(const BytesInLong&) byte_pr.ino:11:7: note: no known conversion for argument 1 from 'long unsigned int' to 'const BytesInLong&' no matching function for call to 'BytesInInt::BytesInInt(unsigned int&)'
насколько я понял, класс требует соответствующего конструктора, в котором, опять же, потребуется доступа к отдельным байтам, т.е. как раз того, что хочется получить в результате. Ну и эффективность такого подхода внушает сомнения в оптимальности.
Хочется, чтобы в теле программы, например, преобразование 16.16->8.8 выглядело просто как чтение двух байт из серединки числа с перезаписью их в новое число. Т.е 4 ассемблерные инструкции.
Аналогичная диагностика полдучается и при
typedef struct b2 { byte b[2]; };
typedef struct b4 { byte b[4]; };
inline unsigned int c32_16r8(unsigned long d) { unsigned int r; ((struct b2)(r)).b[0] = ((struct b4)d).d[1]; return r; }
сейчас компилятор разворачивает такие конструкци как деление:
(unsigned int)i = (unsigned long)d/65536; // 16.16->16.0 16e: 80 91 24 02 lds r24, 0x0224 172: 90 91 25 02 lds r25, 0x0225 176: a0 91 26 02 lds r26, 0x0226 17a: b0 91 27 02 lds r27, 0x0227 17e: bd 01 movw r22, r26 180: 88 27 eor r24, r24 182: 99 27 eor r25, r25 184: 70 93 23 02 sts 0x0223, r23 188: 60 93 22 02 sts 0x0222, r22 (unsigned int)i = (unsigned long)d/65536; // 16.16->8.8 16e: 80 91 24 02 lds r24, 0x0224 172: 90 91 25 02 lds r25, 0x0225 176: a0 91 26 02 lds r26, 0x0226 17a: b0 91 27 02 lds r27, 0x0227 17e: 69 2f mov r22, r25 180: 7a 2f mov r23, r26 182: 8b 2f mov r24, r27 184: 99 27 eor r25, r25 186: 70 93 23 02 sts 0x0223, r23 18a: 60 93 22 02 sts 0x0222, r22
в идеале приведенные выше примеры должны выглядеть так:
a0 91 26 02 lds r26, 0x0226 ; число long по расположено адресам 0x0224-0x0227 b0 91 27 02 lds r27, 0x0227 70 93 23 02 sts 0x0223, r27 ; число int по расположено адресам 0x0222-0x0223 60 93 22 02 sts 0x0222, r26 90 91 25 02 lds r25, 0x0225 ; число long по расположено адресам 0x0224-0x0227 a0 91 26 02 lds r26, 0x0226 70 93 23 02 sts 0x0223, r26 60 93 22 02 sts 0x0222, r25
Мне в свое время не нашлось способа уговорить компилятор так делать .. ещё один момент, который отписывал разрабам gcc, повторю тут:
long арифметика компиляется так: загрузка всех операндов в 8 регистров (или 4 если с константой), словная операция с 2-я младшими (если такая есть) и побайтная со старшими байтами. В то время как поочередное выполнение с каждым байтом .. освобождает 3(6) регистро РОН вообще как класс и соответственно улучшает перераспределение регистров особенно в случае получения/передачи long в параметрах/результатах .. и вообще, компилятор слабо учитывает что Меги - 8-и разрядные микроконтроллеры и все числа большей разрядности обрабатываются побайтно .. нафига занимать лишние регистры - я так и не понял.
обещали посмотреть.. прошел год. :)
Кстати, переход на побайтное вычисление времени в обработчике прерывания от таймера - существенно разгружает задачу сохранения регистров в стеке .. что сильно сказывается на качестве его работы ..
Ну ладно, а вообще, задача перенести 1-й и 2-й байты четырехбайтового числа в 0-й и 1-й байты двухбайтового числа соответственно решается только путем записи деления на 256 и больше никак?
А чем >>8 и >>16 не устраивает. Компилятор их вроде правильно сптимизирует в байтовые перемещения.
Можно по указателю на long + 1 (+2) переписать куда надо.
Существенно дольше будет. Компилятор верно обрабатывает >> и << на значения кратные 8, только что проверил.
Версия компилятора? Уже интересно ..
Проверял на ИДЕ 1.0.6. Настройки gcc по умолчанию. Могу еще на ИДЕ 1.6.5 глянуть, но не думаю что отломали.
У меня на 1.6.4 компилятор не обрабатывает верно << и >> при кратности 8. Из-за чего в своем arhat.h и делал реализацию некоторых функций на асм-вставках. Проверял многажды и не нашел способа тогда заяснить ему что надо просто взять 2 иных регистра из результата..
Может я не так их пользовал? Приведите код сдвиговой операции для получения микросекунд в виде long из регистра счетчика таймера + 3 байта от счетчика миллисекунд в wiring, очень интересно получится у вас считать регистр счетчика и вернуть 3 байта из одного места и считанный байт вместо пересылок или явных сдвигов..
А нефиг компилятор учить оптимизировать ;) Работает - не трогай.
Код.
ассемблер
ef6: 0e 94 f4 0a call 0x15e8 ; 0x15e8 <millis> efa: 7b 01 movw r14, r22 efc: 8c 01 movw r16, r24 efe: b8 01 movw r22, r16 f00: a7 01 movw r20, r14 f02: 75 95 asr r23 f04: 67 95 ror r22 f06: 57 95 ror r21 f08: 47 95 ror r20 f0a: 88 ef ldi r24, 0xF8 ; 248 f0c: 92 e0 ldi r25, 0x02 ; 2 f0e: 2a e0 ldi r18, 0x0A ; 10 f10: 30 e0 ldi r19, 0x00 ; 0 f12: 0e 94 89 0f call 0x1f12 ; 0x1f12 <_ZN5Print7printlnEli> f16: b8 01 movw r22, r16 f18: a7 01 movw r20, r14 f1a: f2 e0 ldi r31, 0x02 ; 2 f1c: 75 95 asr r23 f1e: 67 95 ror r22 f20: 57 95 ror r21 f22: 47 95 ror r20 f24: fa 95 dec r31 f26: d1 f7 brne .-12 ; 0xf1c <setup+0xa2> f28: 88 ef ldi r24, 0xF8 ; 248 f2a: 92 e0 ldi r25, 0x02 ; 2 f2c: 2a e0 ldi r18, 0x0A ; 10 f2e: 30 e0 ldi r19, 0x00 ; 0 f30: 0e 94 89 0f call 0x1f12 ; 0x1f12 <_ZN5Print7printlnEli> f34: b8 01 movw r22, r16 f36: a7 01 movw r20, r14 f38: e7 e0 ldi r30, 0x07 ; 7 f3a: 75 95 asr r23 f3c: 67 95 ror r22 f3e: 57 95 ror r21 f40: 47 95 ror r20 f42: ea 95 dec r30 f44: d1 f7 brne .-12 ; 0xf3a <setup+0xc0> f46: 88 ef ldi r24, 0xF8 ; 248 f48: 92 e0 ldi r25, 0x02 ; 2 f4a: 2a e0 ldi r18, 0x0A ; 10 f4c: 30 e0 ldi r19, 0x00 ; 0 f4e: 0e 94 89 0f call 0x1f12 ; 0x1f12 <_ZN5Print7printlnEli> f52: 77 27 eor r23, r23 f54: 17 fd sbrc r17, 7 f56: 7a 95 dec r23 f58: 61 2f mov r22, r17 f5a: 50 2f mov r21, r16 f5c: 4f 2d mov r20, r15 f5e: 88 ef ldi r24, 0xF8 ; 248 f60: 92 e0 ldi r25, 0x02 ; 2 f62: 2a e0 ldi r18, 0x0A ; 10 f64: 30 e0 ldi r19, 0x00 ; 0 f66: 0e 94 89 0f call 0x1f12 ; 0x1f12 <_ZN5Print7printlnEli> f6a: 59 e0 ldi r21, 0x09 ; 9 f6c: 15 95 asr r17 f6e: 07 95 ror r16 f70: f7 94 ror r15 f72: e7 94 ror r14 f74: 5a 95 dec r21 f76: d1 f7 brne .-12 ; 0xf6c <setup+0xf2> f78: 88 ef ldi r24, 0xF8 ; 248 f7a: 92 e0 ldi r25, 0x02 ; 2 f7c: b8 01 movw r22, r16 f7e: a7 01 movw r20, r14 f80: 2a e0 ldi r18, 0x0A ; 10 f82: 30 e0 ldi r19, 0x00 ; 0 f84: 0e 94 89 0f call 0x1f12 ; 0x1f12 <_ZN5Print7printlnEli> f88: 15 95 asr r17 f8a: 07 95 ror r16 f8c: f7 94 ror r15 f8e: e7 94 ror r14 f90: 88 ef ldi r24, 0xF8 ; 248 f92: 92 e0 ldi r25, 0x02 ; 2 f94: b8 01 movw r22, r16 f96: a7 01 movw r20, r14 f98: 2a e0 ldi r18, 0x0A ; 10 f9a: 30 e0 ldi r19, 0x00 ; 0 f9c: 0e 94 89 0f call 0x1f12 ; 0x1f12 <_ZN5Print7printlnEli>Как при 1 - просто видно сдвиг всех байт, сдвиги на 2-7 - циклы таких сдвигов, сдвиг на 8 (стр.43-53 ассембера) - просто пересылки (!!!) сдвиг на 9 - сново аж 9 циклов, я ожидал не такого тут, сдвиг на 10 - я воще не "распарсил" что оно делает, ну в общем и не важно. Это 1.0.6. Если че и на 1.6.5 повторю.
Вот совсем не то, что Вы накоментировали к этому коду .. если бы "просто пересылки", вопросов не было. А то вот что тут делают первые 3 команды, "знак" размножают .. да от "предыдущего результата", а почему не взять тупо 3 байта от переменной, так как явно прописано в коде?
Сдвиг на 10 точно также "оптимизирован" .. от предыдущего результата.
Мне это напоминает древний анекдот про ИИ (ещё в эпоху БЭСМ-6):
Идет экзамен решения задач Оптимизирующим Исскуственным Интеллектом.
-"Условие: Кухня, на плите стоит пустой, чистый чайник, рядом мойка и кран с холодной водой. Задача: вскипятить воды"
(+3мин) -"О, есть простое решение: берем чайник, снимаем крышку, подставляем в мойку под кран, открываем кран, ждем заполнения, закрываем кран, ставим на плиту, включаем, ждем закипания, выключаем. Всё."
-"2 задача, Условие: Кухня, на плите чайник с водой. Задача таже, вскипятить воды"
(+10мин) -"О, найдена существенная оптимизация: выливаем воду из чайника и тем самым сводим задачу к предыдущей".
В догонку, еще один анекдот того же периода (навеяло):
Идет совещание руководства металопрокатного комплекса на предмет автоматизации круглосуточного непрерывного производства .. звонок от секретарши:
-"К Вам тут срочно изобретатель какой-то просится на совещание. Говорит что у него есть решение проблемы"
-"Срочно!" ..
-"Каковы первоначальные затраты на закупку и внедрение?" -"Ну ... в размере 3 мес. зарплаты троих квалифицированных сотрудников" -"Хм .. заманчиво"
-"Каков начальный период монтажа и настройки?" -"Примерно 1-3 недели" -"Вы, ничего не путаете? Как-то сильно заманчиво"
-"Каковы расходы на поддержание, ремонт?" -"Примерно 3 оклада" .. -"ВСЕГО_О?!?"
-"Каковы плановые простои при обслуживании?" -"таких может не быть вовсе" ...
... -"Молодой человек, да Вам ЦЕНЫ НЕТ, где и что это за система такая?"
-"Человек, сэр." :)
Вот совсем не то, что Вы накоментировали к этому коду .. если бы "просто пересылки", вопросов не было. А то вот что тут делают первые 3 команды, "знак" размножают .. да от "предыдущего результата", а почему не взять тупо 3 байта от переменной, так как явно прописано в коде?
А четвертый чем заполнить? Т.к. переменная знаковая, то и расширяется знаком. И кстати очень неплохо сделано. Была бы беззнаковая то осталась бы очистка старшего байта и пересылка 3-х младших. Сдвигов нет - это факт, о чем я и писал. Отрицать безполезно, полное отсутствие ror очевидно, вместо них mov.
В продолжение: в Паскале существует, минимум, 3 способа осуществить преобразовангие 16.16->8.8 :
В С/С++ пока получается следующее.
unsigned long nn[3] = {0x01020304, 0x05060708, 0x090a0b0c}; #define MEM_R_1(argument) (*((unsigned short*)((char*)(& argument) + 1))) void loop() { unsigned short m0 = *((unsigned short*)(&nn[1] + 1)); Serial.println(m0); unsigned short m1 = *((unsigned short*)((char*)(&nn[1]) + 1)); Serial.println(m1); unsigned short m2 = MEM_R_1(nn[1]); Serial.println(m2); }код:
т.е. лишние байты не загружаются.
Такое, правда, не получится, если хотя бы одна из переменных объявлениа как register или используется передаваемый в стеке параметр функции.
//лишние байты не загружаются.
А какие могут быть "лишние" и "загружатся" при преобразовании long в short? Из 4 байт в 2. там только отбрасыватся 2 байта могут. Но вот эта тема из 16.16 в 8.8 сама по себе очень плохая. Старший и младший байт теряются, если младший еще ладно (но надо бы округлять его), то потеря старшего, оч серезно. Уж лучше 16.16 в 16.0. И прибавить 0,5 перед преобразованием для округления дробной.
16.16->8.8 лишь пример. Реально скорее всего понадобятся 16.16->16.0, 16.16->8.0, 16.16-16.8, 8.16->16.16 и пр.
И все-таки вопрос остается: как сделать подобное без привлесения адресов, например для переменных register?
long L;
short a=L>>16; //16.16->16.0
bytet a=L>>16; //16.16->8.0
long a=L>>8 ; //16.16->16.8
long a=L ; //8.16->16.16
Типа такого, но надо учитывать знаковые или беззнаковые данные, особенно в последнем, остальные должны верно обработать. Например 8.16->16.16 для беззнаковых просто копирование 3 байт ав старший 0, для знакового не ноль а расширение знаком делать.
Правда я не понимаю, в чем ценность 24-битных представлений, имхо 8.16 и подобные - просто геморой себе. Нету у нас подходящих целых типов.
1. При таком подходе зачем-=то читаются ненужные байты (которые не попадают в финальное число).
2. Целый 24-разрядеый тип для AVR есть, называется __int24.
1. При таком подходе зачем-=то читаются ненужные байты (которые не попадают в финальное число).
2. Целый 24-разрядеый тип для AVR есть, называется __int24.
Logic,
1. Дизассемблированный код приведен в исходном (0-м) сообщении темы.
2. Узнал от ЕП. Проверяется экспериментально.
Logic,
1. Дизассемблированный код приведен в исходном (0-м) сообщении темы.
2. Узнал от ЕП. Проверяется экспериментально.
1. Не катит. Там нет вообще >>.
2. Экспериментально тоже отсутствует.
Код __int24 iii;
Ошибка.
Вобще, если это такая большая проблема, что даже служба поддержки год не может решить, то всегда можна обратится к более тяжелому оружию. Я его обычно использую для перестановки байтов местами. Юнионы
union jj { unsigned long L; char b[4]; }; union js { unsigned short s; char b[2]; }; void setup(void) { jj k; js d; k.L=Serial.read(); Serial.println(k.L); d.b[0]=k.b[1]; d.b[1]=k.b[2]; Serial.println(d.s); d.s++; Serial.println(d.s);Ну и дизасс.
42c: c5 01 movw r24, r10 42e: b7 01 movw r22, r14 430: a6 01 movw r20, r12 432: 2a e0 ldi r18, 0x0A ; 10 434: 30 e0 ldi r19, 0x00 ; 0 436: 0e 94 54 05 call 0xaa8 ; 0xaa8 <_ZN5Print7printlnEmi> 43a: 0d 2d mov r16, r13 43c: 1e 2d mov r17, r14 43e: c5 01 movw r24, r10 440: b8 01 movw r22, r16 442: 4a e0 ldi r20, 0x0A ; 10 444: 50 e0 ldi r21, 0x00 ; 0 446: 0e 94 68 05 call 0xad0 ; 0xad0 <_ZN5Print7printlnEji> 44a: 0f 5f subi r16, 0xFF ; 255 44c: 1f 4f sbci r17, 0xFF ; 255 44e: c5 01 movw r24, r10 450: b8 01 movw r22, r16 452: 4a e0 ldi r20, 0x0A ; 10 454: 50 e0 ldi r21, 0x00 ; 0 456: 0e 94 68 05 call 0xad0 ; 0xad0 <_ZN5Print7printlnEji>Как видно пересылки из стр.26-27 идеально записались в 7-8. Ну и математика правильно отрабатывает далее.
ПС. в первом посте, кстати не совсем верно описана идеальная пересылка, для регистрового хранения еще лучше movw.
ППС. подумал что и до movw можна оптимизировать с более хитрым юнионом. Если не догадаетесь сами - завтра напишу.
Ну ладно, а вообще, задача перенести 1-й и 2-й байты четырехбайтового числа в 0-й и 1-й байты двухбайтового числа соответственно решается только путем записи деления на 256 и больше никак?
на ассемблере моv 0,1 и mov 1, 2 ))) ну или как-то так, уже не помню ))
Да. На ассемблере тоже возможно, но на юнионах проще и понятней.
С юнионами, естественно все работает. Но хотелось бы объявлять переменные как обычные целые, а для доступа к байтам использовать переопределение типа на ходу примерно так, как у меня в 13 посте написано на Паскале в строках 3-10.
Т.е. для доступа к байтам на ходу подменить тип. Надеюсь, на С такое возможно?
Что касается Ассемблера, то не знаю я ассамблера AVR. Да и писать на нем целиком большую функцию утомительно, а делать вставку - значит, мешать оптимизатору.
Так делайте приведение к типу. Юнион такой же тип как и все остальные. А все что получится потом в макрос загнать для удобства. А можна еще попробовать перегрузку сделать. В общем варианты есть, но их надо пробовать, на предмет оптимального результата. Т.е. чтоб не получать код длиней. Так, сразу сказать, как повлияет (в плане не добавит ли пару или пару десятков лишних команд), таже перегрузку я не возьмусь. Факт, ваша задача имеет решение, а остальное - подробности;)
Так а что там с __int24? В какой версии ИДЕ оно обнаружилось? И я бы сним был оч. осторожный, неизвестно как там с реализацией даже простых действий. И оптимизации. Нестандарт одним словом.
И снова интересна версия компилятора. У меня локально объявленные юнионы версия gcc 4.8.1 прямиком отправляла в память вместо регистров .. что я делал не так? В общем-то это первое что приходило тогда в голову..
//прямиком отправляла в память вместо регистров
Может ему регистров в конкретно том коде не хватало? Так "карта лягла" у компилятора по его замыслу оптимизации.
"тот код" Вы можете посмотреть или освежить в памяти самостоятельно. Файл arhat.c из моей библиотеки, функция micros().
Arhat109-2, Вам уже много раз говорили куда пойти с рекламой своего барахла. Вам надоело нормальное общение по теме и хочется срача? ОК, получите. Либо конкретный код с дизассом, либо не трындите.
Так делайте приведение к типу.
Собственно, в этом и вопрос.
Проблема в том, что я не настолько хорошо знаю С/С++, чтобы сообразить, как это сделать.
В Паскале аналогичные по виду конструкции ничего не преобразовывают, а просто трактуют байты в указаном месте как другой тип. В С же происходит именно преобразование. Например, Паскале вещественноая единица трактуется как какое-то дикое целое число, тогда как в С происходит именно преобразование в целую единицу.
При попытке преобразования в юнион С не знает, как это сделать, и сообщает о том, что отсутствует подходящий конструктор.
Так а что там с __int24? В какой версии ИДЕ оно обнаружилось? И я бы сним был оч. осторожный, неизвестно как там с реализацией даже простых действий. И оптимизации. Нестандарт одним словом.
У меня в 1.6.5 работает. Ссылка: https://gcc.gnu.org/wiki/avr-gcc
Есть, правда, проблема, что иногда код для инт24 получается длиннее и рабротает дольше, чем для инт32.
Ну и далеко не все это понимают, например, вывексти в последовательный порт так просто не получается.
Меня интересует способ реализации этой функции средствами С, а не ассемблером. Только и всего. Лазить и искать где оно, дабы выдрать из него ассемблерные вставки, а тем более компилять код с С для того чтобы показать что "не проходит" у меня сейчас нет ни доступной возможности, ни времени ни желания. К сожалению. Но интересен Ваш опыт .. думал сможете помочь.
reinterpret_cast//При попытке преобразования в юнион С не знает, как это сделать, и сообщает о том, что отсутствует подходящий конструктор.Беде должно помочь нахальство ))
reinterpret_cast
Самое нахальное приведение типов. Не портируемо, результат может быть некорректным, никаких проверок не делается. Считается, что вы лучше компилятора знаете как на самом деле обстоят дела, а он тихо подчиняется. Не может быть приведено одно значение к другому значению. Обычно используется, чтобы привести указатель к указателю, указатель к целому, целое к указателю. Умеет также работать со ссылками.
reinterpret_cast<whatever *>(some *)reinterpret_cast<integer_expression>(some *)
reinterpret_cast<whatever *>(integer_expression)
Чтобы использовать
reinterpret_castнужны очень и очень веские причины. Используется, например, при приведении указателей на функции.///У меня в 1.6.5 работает. Ссылка: https://gcc.gnu.org/wiki/avr-gcc
Есть, правда, проблема, что иногда код для инт24 получается длиннее и рабротает дольше, чем для инт32.
Ну и нафиг она тогда такая нужна. ))
Вероятно, я неправильно записываю:
typedef struct b4 { byte b[4]; }; void setup() { Serial.begin(115200); } unsigned long nn[3] = {0x01020304, 0x05060708, 0x090a0b0c}; void loop() { unsigned short m3 = reinterpret_cast<short>((reinterpret_cast<b4>(nn[1])).b[1]); Serial.println(m3); } Arduino: 1.6.5 (Windows XP), Board: "Arduino Mega or Mega 2560, ATmega2560 (Mega 2560)" D:\Arduino\arduino-1.6.5-r2\hardware\tools\avr/bin/avr-g++ -c -g -Os -w -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -MMD -mmcu=atmega2560 -DF_CPU=16000000L -DARDUINO=10605 -DARDUINO_AVR_MEGA2560 -DARDUINO_ARCH_AVR -ID:\Arduino\arduino-1.6.5-r2\hardware\arduino\avr\cores\arduino -ID:\Arduino\arduino-1.6.5-r2\hardware\arduino\avr\variants\mega c:\Tmp\build4054785896209395205.tmp\byte_pr.cpp -o c:\Tmp\build4054785896209395205.tmp\byte_pr.cpp.o byte_pr.ino: In function 'void loop()': byte_pr.ino:101:76: error: invalid cast from type 'long unsigned int' to type 'b4' invalid cast from type 'long unsigned int' to type 'b4'Шото оно не похоже. Скорей так.
union jj { unsigned long L; char b[4]; struct { byte b1; unsigned short ss; }; }; #define Get1and2Byte(L) (reinterpret_cast<jj*>(&L))->ss .......... unsigned short r1= Get1and2Byte(L1);Старое
charb[4];можна убрать, т.к. не используется, правда и не влияет.Шото оно не похоже. Скорей так.
union jj { unsigned long L; char b[4]; struct { byte b1; unsigned short ss; }; }; #define Get1and2Byte(L) (reinterpret_cast<jj*>(&L))->ss .......... unsigned short r1= Get1and2Byte(L1);Старое
charb[4];можна убрать, т.к. не используется, правда и не влияет.Как-то оно все равно не то, что хочется.
Что получилось:
unsigned long nn[3] = {0x01020304, 0x05060708, 0x090a0b0c}; #define MEM_R_1(argument) (*((unsigned short*)((char*)(& argument) + 1))) void loop() { unsigned short m0 = *((unsigned short*)(&nn[1] + 1)); Serial.println(m0); unsigned short m1 = *((unsigned short*)((char*)(&nn[1]) + 1)); Serial.println(m1); unsigned short m2 = MEM_R_1(nn[1]); Serial.println(m2); unsigned short m3 = reinterpret_cast<U1*>(&nn[1])->i; Serial.println(m3); }транслируется в:
и что имеем:
1. От адресов уйти так и не удалось - если я объявлю переменную как register (а это вполне желательно, т.к. это результат промежуточных вычислений), то все адресные операции пойдут лесом.
2. Ну и по мелочам: когда мы работаем напрямую с адресом, база грузится в регистровую пару R29:R28 и затем байты извлекаются по смещению двухбайтовой операцией, а в случае reinterpret_cast каждый байт извлекается по абсолютному адресу, что приводи к тому, что загрузка каждого байта осуществляется 4-байтовой операцией, которая, вероятнее всего, еще и выполняется вдвое дольше.
В общем, вопрос остается: как средствами C/C++ переписать 2 средних байта четырехбайтовой переменной в двухбайтовую без использования адресов (считая, что 4-байтовая переменная имеет квалификатор register)?
1. От адресов уйти так и не удалось - если я объявлю переменную как register (а это вполне желательно, т.к. это результат промежуточных вычислений), то все адресные операции пойдут лесом.
в случае reinterpret_cast каждый байт извлекается по абсолютному адресу, что приводи к тому, что загрузка каждого байта осуществляется 4-байтовой операцией,
Так Вы именно так хотели. Вот в стартовом сообщении:
в идеале приведенные выше примеры должны выглядеть так: