sucesivas restas resta por operador numeros entera con algoritmo c++ arduino modulo modulus

c++ - restas - division sin operador



El módulo C++ requiere un molde de resta entre dos bytes*un*signed para funcionar, ¿por qué? (2)

El siguiente código de Arduino (C ++)

void setup() { Serial.begin(115200); byte b1 = 12; byte b2 = 5; const byte RING_BUFFER_SIZE = 64; byte diff = b2 - b1; byte diff2 = (byte)(b2 - b1) % RING_BUFFER_SIZE; //<---NOTE HOW THE (byte) CAST IS *REQUIRED* TO GET THE RIGHT RESULT!!!! Serial.println(b1); Serial.println(b2); Serial.println(RING_BUFFER_SIZE); Serial.println(diff); Serial.println(diff2); } void loop() { }

produce lo esperado:

12 5 64 249 57 //<--correct answer

Mientras que sin el molde "(byte)" como se muestra aquí:

void setup() { Serial.begin(115200); byte b1 = 12; byte b2 = 5; const byte RING_BUFFER_SIZE = 64; byte diff = b2 - b1; byte diff2 = (b2 - b1) % RING_BUFFER_SIZE; //<---(byte) cast removed Serial.println(b1); Serial.println(b2); Serial.println(RING_BUFFER_SIZE); Serial.println(diff); Serial.println(diff2); } void loop() { }

produce:

12 5 64 249 249 //<--wrong answer

¿Por qué la diferencia? ¿Por qué el operador de módulo SÓLO trabaja con el molde explícito?

Nota: "byte" = "uint8_t"


5 - 12 da -7 (un int ). Entonces tu código hace -7 % 64 .

Matemáticamente, esperaríamos que esto diera 57 . Sin embargo, en C y C ++, % para números negativos no hace lo que cabría esperar matemáticamente. En cambio, cumple la siguiente ecuación:

(a/b) * b + a%b == a

Ahora, (-7)/64 da 0 porque C y C ++ usan truncamiento-hacia-cero para la división entera de negativos-positivos. Por lo tanto, -7 % 64 evalúa a -7 .

Finalmente, convirtiendo -7 en uint8_t da 249 .

Cuando escribes (byte)-7 % 64 , en realidad estás haciendo 249 % 64 dando la respuesta esperada.

Con respecto al comportamiento de b2 - b1 : toda la aritmética de números enteros se realiza al menos con precisión int ; para cada operando de - , si es un tipo entero más estrecho que int , primero se promueve a int (dejando el valor sin cambios). Se pueden producir más conversiones si los tipos difieren después de esta promoción (lo que no ocurre en este caso).

En el código, b2 - b1 significa (int)b2 - (int)b1 produciendo un int ; no hay forma de especificar hacer la operación con menor precisión.


Las operaciones aritméticas quieren operar en un int o más grande. Por lo tanto, los byte se promocionan a números enteros antes de que se restan, y es probable que obtengan int reales, lo cual es correcto para C / C ++ porque pueden contener todo el rango de byte .

Si el resultado de la resta se vuelve a convertir en byte , le da el comportamiento de desbordamiento esperado. Sin embargo, si omite el molde en el cálculo de diff2 , está haciendo el módulo en un int negativo . Y, dado que C / C ++ firmó rondas de división hacia cero, el módulo firmado tiene el mismo signo que el dividendo.

El primer paso en falso aquí es esperar que la sustracción actúe directamente en su tipo de byte , o que traduzca su byte sin signo en un int sin signo. El problema de la cascada es pasar por alto el comportamiento de la división con signo de C ++ (lo que es comprensible si no sabes que deberías esperar que la aritmética con signo sea un problema en primer lugar).

Tenga en cuenta que, si su RING_BUFFER_SIZE no fuera una potencia de dos, la división no funcionaría correctamente para casos como este de todos modos. Y, dado que es un poder de dos, tenga en cuenta que:

(b2 - b1)&(RING_BUFFER_SIZE-1)

debería funcionar correctamente

Y finalmente (como se sugiere en el comentario), la forma correcta de hacer un restar el búfer de anillo sería asegurarse de que b1 < RING_BUFFER_SIZE (lo cual tenga sentido para una operación de búfer de anillo), y use algo como:

(b2>b1)? b2 - b1 : RING_BUFFER_SIZE + b2 - b1