3 ms·
> Given two operands to a binary operator, find the first type in this list that matches one of the operands, then convert the other operand to that type. Does
by klingonopera 7y ago
> Given two operands to a binary operator, find the first type in this list that matches one of the operands, then convert the other operand to that type.
Does anyone know for sure whether or not the Arduino compiler deviates from this standard, or ever had, in previous versions?
Does multiplication qualify as a binary operator?
IIRC, I think I was compound multiplying the product of an int and a char into a long (see below), and I had to explicitly cast the char as int to get the right results, much to my surprise, as I had expected the char to get promoted to an int. But I could also have made some mistakes, it's been awhile...
longType *= intType * (int)charType;
EDIT: Found my old code snippet, and it kinda invalidates my original question, what I was actually doing was
longType *= charType * charType * charType
which leads the right side to evaluate first without promotion, thus modulating the end result by 256, which was not intended. I then cast all of them as long, but probably (certainly!...?) only one would've been enough.
EDIT2: Or at least, two of them have to be casted, else it might evaluate two chars first, modulating the result, before actually promoting to long to multiply with another long. (Or not, because it promotes to the most significant data type on the right side? Ugh... this is why I prefer to be extra verbose.)
- kccqzy 7y agoThat shouldn't be necessary. The "usual arithmetic conversions" should cover that.
- ftvy 7y agoI have seen this happen with pointers on the x86_64 arch and the comparison operator where they appear to be the same value in the lower 4 bytes but the upper 4 bytes are different. I am not exactly certain, but what I think happens is that when you define your charType variable that the upper bytes in the word are still the same random data, so when you multiply without casting, it is treating the char as an int and multiplying the whole 4 bytes. When you cast, it probably just multiplies the lowest byte and carries if necessary. I am also not certain if the compiler would optimize to using a shift operation if one operand is a multiple of 2. Again, not exactly certain if this is the case, but it's similar to what I've noticed.
- muricula 7y agoIf I recall correctly, the arduino compiler is actually a C++ compiler so it will require more explicit casts, but it's been some time since I used arduino. Also the declaration: char foo; may be signed or unsigned depending on the architecture and compiler, which may lead to surprising results.
- muricula 7y agoMultiplication is a binary operator, here binary just means has two inputs. ~x would be a unary operator and x * y would be a binary one.
- bxparks 7y agoArduino boards (the ones based on AVR chips) use the GCC C++ compiler adapted for the AVR. The current IDE version 1.8.10 uses avr-gcc-7.3.0 (https://github.com/arduino/Arduino/blob/master/hardware/package_index_bundled.json https://github.com/arduino/Arduino/blob/master/hardware/pack...). The previous IDE version 1.8.9 used avr-gcc-5.4.0. The compiler is configured to follow the -std=gnu++11 variant. Here are the flags: https://github.com/arduino/ArduinoCore-avr/blob/master/platform.txt https://github.com/arduino/ArduinoCore-avr/blob/master/platf... If you use other Arduino-compatible boards (e.g. ARM chips, ESP8266 or ESP32), they will use a different C++ compiler and different settings. See for example: * SAMD uses 'arm-none-eabi-g++' (https://github.com/arduino/ArduinoCore-sam/blob/master/platform.txt https://github.com/arduino/ArduinoCore-sam/blob/master/platf...) * ESP8266 uses xtensa-lx106-elf-gcc (https://github.com/arduino/esp8266/blob/master/platform.txt https://github.com/arduino/esp8266/blob/master/platform.txt) * ESP32 uses xtensa-esp32-elf-gcc (https://github.com/espressif/arduino-esp32/blob/master/platform.txt https://github.com/espressif/arduino-esp32/blob/master/platf...) So Arduino programming is "just" C++ with additional libraries and an IDE. Though I wrote "just" in quotes because they've done a lot of work to make things easier and approachable for beginners, so I don't want to diminish their work.
- klingonopera 7y agoThanks, looks like there's an -mmcu flag that gets passed to the compiler as well, telling it what chip the code is intended for. I think the answers I'm looking for are buried in those specifications. Gotta check them out, one of these days... > So Arduino programming is "just" C++ with additional libraries and an IDE. Considering that flag, I think it's something more (or less) than just plain C++...
- int_19h 7y agoWhich side it is doesn't matter. Assuming signed operands, if either one is long, then the other one is also promoted to long; otherwise, both are promoted to int. So bool * bool or char * char or short * short is actually int * int, and the resulting type is also int.
- klingonopera 7y agoDoes the Arduino really promote two chars to int when performing on them? It is an 8-bit chip, and that would take a number of cycles more, than to just leave them...?
- int_19h 7y agoI'm not sure about Arduino specifically, but 8-bit usually refers to memory bus width, not the register size. IIRC Arduino has 16-bit registers? So I'd expect it to have a 16-bit int. And depending on the exact expression, it doesn't have to actually do anything differently - these integer conversions describe the expected result of the operation, not how it's performed in assembly. So when you write something like this: char a, b, c; ... a = b + c the spec requires that b+c is treated as an int addition, but the resulting int is then assigned to a char variable, which basically truncates it. Furthermore, if b+c is larger than can fit in a char, then we have signed overflow (assuming that char is signed, which is usually the case), which is undefined behavior. Altogether, this means that the compiler can actually do an 8-bit addition here while remaining within the bounds of the spec. OTOH if the result is assigned to an int variable, then yeah, it'd have to promote them - but that would also be far less surprising to you than if it didn't, no? Where this can potentially lead to more overhead than you'd expect is use of the temporary as input to another subexpresion. E.g. suppose we have this: a = (b + c) / 2 Suppose b is 200 and c is 100. The spec requires that the result of this operation is 150, because b+c is performed on ints, and thus won't overflow. If it were an 8-bit addition of signed chars, assuming the typical wraparound behavior on overflow, you'd get 22.
- klingonopera 7y agoWhen referring to the Arduino, unless explicitly stating a specific chip, most people are actually referring to the AT328P chip, which is a chip with 8-bit architecture (registers are 8-bit, 16-bit data types are split between a high and a low register). It has 32 KB programmable memory, and 2 KB RAM. Indeed, though, on the Arduino platform, an int is usually 16 bit, but not because that's the chip's native archticture, but because that's the range required. Though I can't say for sure, I highly doubt (and now am actually almost completely certain in my doubts) that it would promote two chars to ints to perform operations on them, and last night also realized why, namely, there are counts for the number of cycles required to do operations on various data types [0]. If what you say about integer promotion is true, there would be no difference in clock cycles between an int and a byte, but there is. So in essence, no, the Arduino is definitely somehow deviating from spec when it comes to promotion. [0]: https://forum.arduino.cc/index.php?topic=92684.msg696420#msg696420 https://forum.arduino.cc/index.php?topic=92684.msg696420#msg... EDIT: I just realized, I think I need to actually look into the (Atmel's) AVR specs, as the Arduino IDE is basically just a wrapper for that.