|
Деление int на size_t |
|
|
|
Nov 6 2013, 14:20
|

Начинающий профессионал
    
Группа: Свой
Сообщений: 1 215
Регистрация: 25-10-06
Из: СПб
Пользователь №: 21 648

|
Цитата(Xenia @ Nov 6 2013, 15:24)  Результат sizeof() - беззнаковый, оттого и все выражение становится таким. Приведите его явно к знаковому типу, и все устаканится: t /= (int)sizeof(buf); А не боитесь, что sizeof(buf) станет отрицательным. На "малых" значениях sizeof(buf) положительный и знак определяется только t, для "больших" значений sizeof(buf) отрицательный, и результат зависит от обоих знаков.
--------------------
Наука изощряет ум; ученье вострит память. Козьма Прутков
|
|
|
|
|
Nov 6 2013, 16:43
|

Гуру
     
Группа: Модератор FTP
Сообщений: 4 479
Регистрация: 20-02-08
Из: Москва
Пользователь №: 35 237

|
Цитата(mdmitry @ Nov 6 2013, 18:20)  А не боитесь, что sizeof(buf) станет отрицательным. На "малых" значениях sizeof(buf) положительный и знак определяется только t, для "больших" значений sizeof(buf) отрицательный, и результат зависит от обоих знаков. Ничуть не боюсь  , т.к. если делитель вылез из разрядной сетки целого, которая оказалась для него тесновата, а делимое t поместилось, то вряд ли можно получить какое-то частное, кроме нуля. Опять же buf размером в 2 гигабайта на AVR-микроконтроллере едва ли возможен.
|
|
|
|
|
Nov 6 2013, 16:47
|

Знающий
   
Группа: Свой
Сообщений: 723
Регистрация: 29-08-05
Из: Березовский
Пользователь №: 8 065

|
Цитата(sonycman @ Nov 6 2013, 21:11)  Программа и так компилируется в плюсах, какого-то специального варнинга не видел. Если вместо sizeof() использовать const unsigned char size = 20, то проблем со знаком нет, результат получается со знаком. Не глюк ли это компилятора? Как еще это можно обьяснить? здесь у Вас unsigned char (8 бит), а t имеет тип int (16 бит). И надо учитывать, что sizeof возвращает не unsigned char, и даже не unsigned int, а size_t. Думаю, Вам надо посмотреть как конкретно на вашем компиляторе реализуется тип size_t.
--------------------
Хочешь рассмешить Бога -- расскажи ему о своих планах!
|
|
|
|
|
Nov 6 2013, 22:38
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(V_G @ Nov 6 2013, 18:00)  Если перейдете на С++ (язык с более строгим контролем соответствия типов данных) , то компилятор по-хорошему должен выдать сообщение об ошибке и потребовать явного приведения к типу. Всякого рода умолчания тут вредны. Это именно C++. А в чём именно ошибка? Int делим на Short (или любой другой тип без знака). В результате будет Int. Это ошибка? Цитата(zhevak @ Nov 6 2013, 20:47)  И надо учитывать, что sizeof возвращает не unsigned char, и даже не unsigned int, а size_t. Функция у sizeof() - вернуть константу, то есть самое обычное число без знака. Каким образом её результат может влиять на знак выражения типа: -100/х = y? Цитата(AHTOXA @ Nov 6 2013, 20:59)  Если там gcc, то за это предупреждение отвечает флаг -Wsign-compare. Проверил на других компиляторах: IAR и CodeVision не страдают этой ошибкой и генерят код в соответствии с математическими правилами и моими ожиданиями. А вот GCC в AtmelStudio и для ARMов точно также отбрасывает знак и считает, что результат может быть только положительный. Прискорбно
|
|
|
|
|
Nov 7 2013, 03:59
|

фанат дивана
     
Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684

|
Цитата(sonycman @ Nov 7 2013, 04:38)  А вот GCC в AtmelStudio и для ARMов точно также отбрасывает знак и считает, что результат может быть только положительный. Прискорбно  Это не прискорбно, это - в соответствии со стандартом. Вот что говорится в ISO/IEC 9899:1999 6.3.1.8 Usual arithmetic conversions: Цитата Otherwise, the integer promotions are performed on both operands. Then the following rules are applied to the promoted operands: - If both operands have the same type, then no further conversion is needed.
- Otherwise, if both operands have signed integer types or both have unsigned integer types, the operand with the type of lesser integer conversion rank is converted to the type of the operand with greater rank.
- Otherwise, if the operand that has unsigned integer type has rank greater or equal to the rank of the type of the other operand, then the operand with signed integer type is converted to the type of the operand with unsigned integer type.
- Otherwise, if the type of the operand with signed integer type can represent all of the values of the type of the operand with unsigned integer type, then
the operand with unsigned integer type is converted to the type of the operand with signed integer type. - Otherwise, both operands are converted to the unsigned integer type corresponding to the type of the operand with signed integer type.
Ваш вариант - последний (поскольку int не может вместить всех значений size_t, size_t - не может вместить всех значений int, то побеждает size_t, как беззнаковый.)
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Nov 7 2013, 06:09
|

Знающий
   
Группа: Свой
Сообщений: 723
Регистрация: 29-08-05
Из: Березовский
Пользователь №: 8 065

|
... и еще вот тут есть интересные рассуждения про size_thttp://www.embedded.com/electronics-blogs/...-size-t-mattersПереведу-ка я ее на русский, да опубликую не сегодня-завтра в своем блоге. Полезное ведь дело, однако, да.
--------------------
Хочешь рассмешить Бога -- расскажи ему о своих планах!
|
|
|
|
|
Nov 7 2013, 08:47
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(AHTOXA @ Nov 7 2013, 07:59)  Ваш вариант - последний (поскольку int не может вместить всех значений size_t, size_t - не может вместить всех значений int, то побеждает size_t, как беззнаковый.) Тогда обязан был быть ворнинг или даже еррор, так как в результате такого преобразования разрушается значение Int. Но никакого предупреждения компилер не дает. Вероятно, более продвинутые компиляторы понимают, что в моём случае sizeof() даёт размерность, которая умещается в простом char, и, соответственно, не преобразуют Int. Для GCC это непосильная задача
|
|
|
|
|
Nov 7 2013, 09:30
|

фанат дивана
     
Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684

|
Цитата(sonycman @ Nov 7 2013, 14:47)  Вероятно, более продвинутые компиляторы понимают, что в моём случае sizeof() даёт размерность, которая умещается в простом char, и, соответственно, не преобразуют Int. Для GCC это непосильная задача  1. sizeof() возвращает size_t, независимо от значения. 2. int/size_t возвращает size_t. (при условии sizeof(int) == sizeof(size_t) ) Это требование стандарта. Вы считаете, что несоблюдение компилятором стандарта языка говорит о том, что этот компилятор "более продвинутый"?  Кстати, есть вариант, что в этих продвинутых компиляторах int просто длиннее, чем size_t, и поэтому всё получается нормально без нарушения стандарта.
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Nov 7 2013, 09:37
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(AHTOXA @ Nov 7 2013, 13:30)  1. sizeof() возвращает size_t, независимо от значения. 2. int/size_t возвращает size_t. (при условии sizeof(int) == sizeof(size_t) ) Это требование стандарта. Вы считаете, что несоблюдение компилятором стандарта языка говорит о том, что этот компилятор "более продвинутый"?  Кстати, есть вариант, что в этих продвинутых компиляторах int просто длиннее, чем size_t, и поэтому всё получается нормально без нарушения стандарта. А что, Вы будете спорить, что IAR и прочие коммерческие компилеры - более продвинутые? Это видно и на моём примере  А в защиту GCC можно сказать только - что он "блюдёт" стандарты.
|
|
|
|
|
Nov 7 2013, 12:27
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(AHTOXA @ Nov 7 2013, 15:17)  Вот уж не ожидал, что приведённое мной объяснение может вызвать такую реакцию. Подумайте ещё вот о чём. Существует ненулевая вероятность, что в какой-то из следующих версий IAR данное несоответствие стандарту устранят...  Да обычная реакция. Когда в очередной раз слышишь, что маразм из разряда -2/1=2 - это стандарт. Не только IAR "игнорирует" ваш стандарт, но и CodeVisionAVR. Я еще Keil не проверял, возможно, он тоже из разряда "недоделанных"  Может быть, отличия в результатах компиляции - вовсе не различная трактовка стандартов, а работа оптимизации?
|
|
|
|
|
Nov 7 2013, 16:33
|

фанат дивана
     
Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684

|
Это не мой стандарт, это стандарт языка Си. И лучше такой стандарт, чем никакого. Странно, что вы, вроде бы достаточно опытный разработчик, этого не понимаете. А уж ссылки на CodeVisionAVR - это вообще нонсенс, поскольку давно известно, что CodeVisionAVR - это компилятор "языка, похожего на Си":) Вы лучше проверьте "взрослые" компиляторы. (И посмотрите в IAR, чему там равны sizeof(int) и sizeof(size_t), вдруг и IAR соблюдает стандарт).
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Nov 7 2013, 21:31
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(AHTOXA @ Nov 7 2013, 20:33)  (И посмотрите в IAR, чему там равны sizeof(int) и sizeof(size_t), вдруг и IAR соблюдает стандарт). Оба равны двум, что неудивительно. Только у одного 2+2 равно 4, а у второго - 5. Думаете, какой из них больше соблюдает стандарт? ЗЫ: сейчас докачаю Кейл и проверю там, на АРМ. ЗЗЫ: Упс, обновил AtmelStudio на последнюю версию 6.1 (стояла 6.0), ничего в тексте не менял, пересобрал проект - появилось знаковое деление и обработка отрицательных значений. То есть исправили багу и теперь поведение GCC стало аналогичным IAR. Попробуйте теперь объяснить, как необходимо правильно трактовать разные умные стандарты Ставить Кейл пока не стану, уверен, что его код будет аналогичен IAR.
|
|
|
|
|
Nov 8 2013, 04:17
|

фанат дивана
     
Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684

|
Цитата(sonycman @ Nov 8 2013, 03:31)  Оба равны двум, что неудивительно. Да всяко бывает. Бывает 4 (на АРМ-ах). Бывает и 8 (на 64-битных системах). Ещё бывает, что размер int и size_t разный. Цитата(sonycman @ Nov 8 2013, 03:31)  Только у одного 2+2 равно 4, а у второго - 5. Думаете, какой из них больше соблюдает стандарт? Больше соблюдает тот, у которого результат соответствует стандарту. Если по стандарту должно получиться 5, то второй  Цитата(sonycman @ Nov 8 2013, 03:31)  ЗЗЫ: Упс, обновил AtmelStudio на последнюю версию 6.1 (стояла 6.0), ничего в тексте не менял, пересобрал проект - появилось знаковое деление и обработка отрицательных значений. То есть исправили багу и теперь поведение GCC стало аналогичным IAR. Попробуйте теперь объяснить, как необходимо правильно трактовать разные умные стандарты  Очень рад за вас  Не знаю, почему так. Возможно, здесь константа вычисляется во время компиляции, и поэтому она трактуется как нетипизированная. Для интереса попробуйте написать функцию, которая возвращает size_t, и поделить int на результат этой функции.
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Nov 8 2013, 10:19
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Прошу прощения, поторопился я вчера с выводами, поздно было, допустил невнимательность и рано обрадовался  Не заметил приведение типов - нет, версия студии 6.1 всё так же остаётся верна стандартам и сама себе - знак так же отбрасывается, как и раньше. Зато потестил Кейл - как и предполагал, работает по аналогии с IAR - чихает на стандарты, делит со знаком и его дальнейшей обработкой. Наш человек  Подставлял в IAR вместо sizeof() переменную с типом size_t - без разницы, никакого превращения Int в беззнаковый тип не происходит, всё работает как и раньше.
|
|
|
|
|
Nov 8 2013, 13:39
|

Знающий
   
Группа: Свой
Сообщений: 723
Регистрация: 29-08-05
Из: Березовский
Пользователь №: 8 065

|
Цитата(sonycman @ Nov 8 2013, 18:02)  Вы про IAR? Уточните, пожалуйста. Смогу предоставить ассемлерный листинг только ночью или завтра, к сожалению. Честно говоря, я не думал про какой-то конкретный компилятор. Я под Линем сижу, могу притащить сюда листин от gcc. (Если кто-нибудь меня не опередит к этому времени, гы-гы!) Давайте какой-нибудь простенький код обуликуем, откомпилируем и сравним. Внимание нужно будет уделить именно арифметическим командам проца, которые как раз занимаются вычислением деления int на size_t. Ну что, попробуем сделать?
--------------------
Хочешь рассмешить Бога -- расскажи ему о своих планах!
|
|
|
|
|
Nov 9 2013, 19:56
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Несколько устав уже от нюансов по поводу интерпретации стандатров различными компиляторами, нашёл всё таки время "добить" код, прогнав его через IAR и Keil. Результаты не совсем однозначные, но вот они, какие есть. Исходный код: Код volatile int tbuf[20], result; while (1) { int t = 0; for (unsigned char c = 0; c < sizeof(tbuf)/sizeof(int); c++) { t += tbuf[c]; } t /= sizeof(tbuf)/sizeof(int); //деление INT на SIZE_T, приводящее к "тихой" ошибке result = t < 0 ? t/5 : t;
} Генерируется он на всех трёх компиляторах тихо, без ошибок и предупреждений, хотя несёт в себе ошибку, превращающую INT при делении в беззнаковый тип, отбрасывая таким образом все отрицательные значения. При этом GCC полностью удаляет из полученного кода часть, которая обрабатывает отрицательный результат. В IAR и Keil она присутствует, хотя выполнена может быть далеко не всегда. Полученный код на GCC (Atmel Studio 6.1): CODE 6a: 60 e0 ldi r22, 0x00 ; 0 6c: 70 e0 ldi r23, 0x00 ; 0 6e: 80 e0 ldi r24, 0x00 ; 0 70: 90 e0 ldi r25, 0x00 ; 0 72: fb 01 movw r30, r22 74: ee 0f add r30, r30 76: ff 1f adc r31, r31 78: 21 e0 ldi r18, 0x01 ; 1 7a: 30 e0 ldi r19, 0x00 ; 0 7c: 2c 0f add r18, r28 7e: 3d 1f adc r19, r29 80: e2 0f add r30, r18 82: f3 1f adc r31, r19 84: 20 81 ld r18, Z 86: 31 81 ldd r19, Z+1 ; 0x01 88: 82 0f add r24, r18 8a: 93 1f adc r25, r19 8c: 6f 5f subi r22, 0xFF ; 255 8e: 7f 4f sbci r23, 0xFF ; 255 90: 64 31 cpi r22, 0x14 ; 20 92: 71 05 cpc r23, r1 94: 71 f7 brne .-36 ; 0x72 <main+0x1c> 96: 03 d0 rcall .+6 ; 0x9e <__udivmodhi4> 98: 7a a7 std Y+42, r23 ; 0x2a 9a: 69 a7 std Y+41, r22 ; 0x29 9c: e6 cf rjmp .-52 ; 0x6a <main+0x14>
Код IAR (v6.21): CODE \ ??main_1: \ 00000004 E045 LDI R20, 5 \ 00000006 E050 LDI R21, 0 \ 00000008 .... RCALL ?SS_DIVMOD_L02 \ ??main_2: \ 0000000A 8308 ST Y, R16 \ 0000000C 8319 STD Y+1, R17 \ ??main_0: \ 0000000E E000 LDI R16, 0 \ 00000010 E010 LDI R17, 0 \ 00000012 E020 LDI R18, 0 \ ??main_3: \ 00000014 2FEC MOV R30, R28 \ 00000016 5FEE SUBI R30, 254 \ 00000018 2F32 MOV R19, R18 \ 0000001A 0F33 LSL R19 \ 0000001C 0FE3 ADD R30, R19 \ 0000001E 8140 LD R20, Z \ 00000020 8151 LDD R21, Z+1 \ 00000022 0F04 ADD R16, R20 \ 00000024 1F15 ADC R17, R21 \ 00000026 9523 INC R18 \ 00000028 3124 CPI R18, 20 \ 0000002A F3A0 BRCS ??main_3 \ 0000002C E144 LDI R20, 20 \ 0000002E E050 LDI R21, 0 \ 00000030 .... RCALL ?SS_DIVMOD_L02 \ 00000032 2311 TST R17 \ 00000034 F33A BRMI ??main_1 \ 00000036 CFE9 RJMP ??main_2
Здесь уже ошибки нет, применяется деление со знаком. Однако, если скомпилировать не Release код, а Debug, при тех же настройках оптимизации, получается код с беззнаковым делением. Интересно Кейл (uVision v4.72) тоже применяет деление без знака, как и GCC, хотя мне вначале казалось наоборот. Прошу простить за невнимательность  Код 00000c 2314 MOVS r3,#0x14 00000e 466a MOV r2,sp ;33 ;;;37 ;;;38 result = t < 0 ? t/5 : t; 000010 2405 MOVS r4,#5 |L1.18| 000012 2100 MOVS r1,#0 ;30 000014 ea4f0001 MOV.W r0,r1 ;31 |L1.24| 000018 f8525020 LDR r5,[r2,r0,LSL #2] ;33 00001c 1c40 ADDS r0,r0,#1 ;31 00001e 4429 ADD r1,r1,r5 ;33 000020 2814 CMP r0,#0x14 ;31 000022 d3f9 BCC |L1.24| 000024 fbb1f0f3 UDIV r0,r1,r3 ;36 000028 2800 CMP r0,#0 00002a da01 BGE |L1.48| 00002c fb90f0f4 SDIV r0,r0,r4 |L1.48| ;;;39 ;;;40 } 000030 9014 STR r0,[sp,#0x50] 000032 e7ee B |L1.18| Логика несколько неясна, после UDIV проверять значение на отрицательный знак? Это возможно разве? Но не перестаю восхищаться красивым, мощным, ёмким и компактным кодом Cortex ARM. Он в два раза короче, чем код для AVR!
|
|
|
|
|
Nov 28 2013, 20:35
|
Местный
  
Группа: Участник
Сообщений: 313
Регистрация: 2-07-11
Пользователь №: 66 023

|
Цитата(sonycman @ Nov 9 2013, 22:56)  При этом GCC полностью удаляет из полученного кода часть, которая обрабатывает отрицательный результат. Чудеса оптимизации. Умеет определять, что после деления 0...65535 на 20 получится 0...3276, после преобразования к знаковому будет не отрицательным у ветка для t<0 не нужна. Цитата(sonycman @ Nov 9 2013, 22:56)  Код IAR (v6.21): ... Здесь уже ошибки нет, применяется деление со знаком. Однако, если скомпилировать не Release код, а Debug, при тех же настройках оптимизации, получается код с беззнаковым делением. Интересно  Увы, IAR не вызывает доверия. Самый худший вариант, когда результат зависит от включения отладки  .
|
|
|
|
|
Jan 5 2014, 00:20
|

Профессионал
    
Группа: Участник
Сообщений: 1 620
Регистрация: 22-06-07
Из: Санкт-Петербург, Россия
Пользователь №: 28 634

|
Ну написано же в стандарте явно про беззнаковость size_t... Цитата 3.3.3.4 The sizeof operator
Constraints
The sizeof operator shall not be applied to an expression that has function type or an incomplete type, to the parenthesized name of such a type, or to an lvalue that designates a bit-field object.
Semantics
The sizeof operator yields the size (in bytes) of its operand, which may be an expression or the parenthesized name of a type. The size is determined from the type of the operand, which is not itself evaluated. The result is an integer constant.
When applied to an operand that has type char , unsigned char , or signed char , (or a qualified version thereof) the result is 1. When applied to an operand that has array type, the result is the total number of bytes in the array./35/ When applied to an operand that has structure or union type, the result is the total number of bytes in such an object, including internal and trailing padding.
The value of the result is implementation-defined, and its type (an unsigned integral type) is size_t defined in the <stddef.h> header. И про преобразование перед делением: Цитата 3.2.1.5 Usual arithmetic conversions
Many binary operators that expect operands of arithmetic type cause conversions and yield result types in a similar way. The purpose is to yield a common type, which is also the type of the result. This pattern is called the usual arithmetic conversions: First, if either operand has type long double, the other operand is converted to long double . Otherwise, if either operand has type double, the other operand is converted to double. Otherwise, if either operand has type float, the other operand is converted to float. Otherwise, the integral promotions are performed on both operands. Then the following rules are applied: If either operand has type unsigned long int, the other operand is converted to unsigned long int. Otherwise, if one operand has type long int and the other has type unsigned int, if a long int can represent all values of an unsigned int, the operand of type unsigned int is converted to long int ; if a long int cannot represent all the values of an unsigned int, both operands are converted to unsigned long int. Otherwise, if either operand has type long int, the other operand is converted to long int. Otherwise, if either operand has type unsigned int, the other operand is converted to unsigned int. Otherwise, both operands have type int.
The values of operands and of the results of expressions may be represented in greater precision and range than that required by the type; the types are not changed thereby.
Сообщение отредактировал Genadi Zawidowski - Jan 5 2014, 00:21
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|