How can I make N/0 be equal to N/0?

When does N divided by zero not equal N divided by zero?

When the result is a long or unsigned long, and N is not an unsigned long, and zero is not an unsigned long.

If the result is a byte, int or unsigned int, it doesn't matter what type N or zero are, the results are always the same, but when the result is an unsigned long or a long, the type of the denominator affects what the result will be.

I have written a series of macros and functions which manage to divide expeditiously, however for divide by zero, it doesn't always return the correct result, and I can't figure out how to get the correct result.

I think I need to know the type of the variable (unsigned int versus int, for example) in order to return the correct result.

Here's my code, followed by the results.

Note: divide0_sizeX is my attempt at returning the right value, but it fails with almost the same frequency as does just returning 0xFFFFFFFF.

  #define divide0( numerator ) ( numerator / 0 )

  byte byte_numerator = 12 ;
  byte byte_denominator = 0 ;
  
  int int_numerator = 12 ;
  int int_denominator = 0 ;
  
  unsigned int unsigned_int_numerator = 12 ;
  unsigned int unsigned_int_denominator = 0 ;
  
  long long_numerator = 12 ;
  long long_denominator = 0 ;
  long long_result = 0 ;
  
  unsigned long unsigned_long_numerator = 12 ;
  unsigned long unsigned_long_denominator = 0 ;
  

//=======================================================================================
  Serial.print(F("ulong  byte   byte   "));
  long_result = byte_numerator / byte_denominator ;
  Serial.print(long_result,HEX);
  Serial.print(F("   "));
  long_result = divide0( byte_numerator ) ;
  Serial.print(long_result,HEX);
  Serial.println();
  
  Serial.print(F("ulong  byte   int    "));
  long_result = byte_numerator / int_denominator ;
  Serial.print(long_result,HEX);
  Serial.print(F("   "));
  long_result = divide0( byte_numerator ) ;
  Serial.print(long_result,HEX);
  Serial.println();
  
  Serial.print(F("ulong  byte   uint   "));
  long_result = byte_numerator / unsigned_int_denominator ;
  Serial.print(long_result,HEX);
  Serial.print(F("   "));
  long_result = divide0( byte_numerator ) ;
  Serial.print(long_result,HEX);
  Serial.println();
  
  Serial.print(F("ulong  byte   long   "));
  long_result = byte_numerator / long_denominator ;
  Serial.print(long_result,HEX);
  Serial.print(F("   "));
  long_result = divide0( byte_numerator ) ;
  Serial.print(long_result,HEX);
  Serial.println();
  
  Serial.print(F("ulong  byte   ulong  "));
  long_result = byte_numerator / unsigned_long_denominator ;
  Serial.print(long_result,HEX);
  Serial.print(F("   "));
  long_result = divide0( byte_numerator ) ;
  Serial.print(long_result,HEX);
  Serial.println();
  

  Serial.print(F("ulong  int    byte   "));
  long_result = int_numerator / byte_denominator ;
  Serial.print(long_result,HEX);
  Serial.print(F("   "));
  long_result = divide0( int_numerator ) ;
  Serial.print(long_result,HEX);
  Serial.println();
  
  Serial.print(F("ulong  int    int    "));
  long_result = int_numerator / int_denominator ;
  Serial.print(long_result,HEX);
  Serial.print(F("   "));
  long_result = divide0( int_numerator ) ;
  Serial.print(long_result,HEX);
  Serial.println();
  
  Serial.print(F("ulong  int    uint   "));
  long_result = int_numerator / unsigned_int_denominator ;
  Serial.print(long_result,HEX);
  Serial.print(F("   "));
  long_result = divide0( int_numerator ) ;
  Serial.print(long_result,HEX);
  Serial.println();
  
  Serial.print(F("ulong  int    long   "));
  long_result = int_numerator / long_denominator ;
  Serial.print(long_result,HEX);
  Serial.print(F("   "));
  long_result = divide0( int_numerator ) ;
  Serial.print(long_result,HEX);
  Serial.println();
  
  Serial.print(F("ulong  int    ulong  "));
  long_result = int_numerator / unsigned_long_denominator ;
  Serial.print(long_result,HEX);
  Serial.print(F("   "));
  long_result = divide0( int_numerator ) ;
  Serial.print(long_result,HEX);
  Serial.println();
  

  Serial.print(F("ulong  long   byte   "));
  long_result = long_numerator / byte_denominator ;
  Serial.print(long_result,HEX);
  Serial.print(F("   "));
  long_result = divide0( long_numerator ) ;
  Serial.print(long_result,HEX);
  Serial.println();
  
  Serial.print(F("ulong  long   int    "));
  long_result = long_numerator / int_denominator ;
  Serial.print(long_result,HEX);
  Serial.print(F("   "));
  long_result = divide0( long_numerator ) ;
  Serial.print(long_result,HEX);
  Serial.println();
  
  Serial.print(F("ulong  long   uint   "));
  long_result = long_numerator / unsigned_int_denominator ;
  Serial.print(long_result,HEX);
  Serial.print(F("   "));
  long_result = divide0( long_numerator ) ;
  Serial.print(long_result,HEX);
  Serial.println();
  
  Serial.print(F("ulong  long   long   "));
  long_result = long_numerator / long_denominator ;
  Serial.print(long_result,HEX);
  Serial.print(F("   "));
  long_result = divide0( long_numerator ) ;
  Serial.print(long_result,HEX);
  Serial.println();
  
  Serial.print(F("ulong  uint   ulong  "));
  long_result = unsigned_int_numerator / unsigned_long_denominator ;
  Serial.print(long_result,HEX);
  Serial.print(F("   "));
  long_result = divide0( unsigned_int_numerator ) ;
  Serial.print(long_result,HEX);
  Serial.println();
  

  Serial.print(F("ulong  uint   byte   "));
  long_result = unsigned_int_numerator / byte_denominator ;
  Serial.print(long_result,HEX);
  Serial.print(F("   "));
  long_result = divide0( unsigned_int_numerator ) ;
  Serial.print(long_result,HEX);
  Serial.println();
  
  Serial.print(F("ulong  uint   int    "));
  long_result = unsigned_int_numerator / int_denominator ;
  Serial.print(long_result,HEX);
  Serial.print(F("   "));
  long_result = divide0( unsigned_int_numerator ) ;
  Serial.print(long_result,HEX);
  Serial.println();
  
  Serial.print(F("ulong  uint   uint   "));
  long_result = unsigned_int_numerator / unsigned_int_denominator ;
  Serial.print(long_result,HEX);
  Serial.print(F("   "));
  long_result = divide0( unsigned_int_numerator ) ;
  Serial.print(long_result,HEX);
  Serial.println();
  
  Serial.print(F("ulong  uint   long   "));
  long_result = unsigned_int_numerator / long_denominator ;
  Serial.print(long_result,HEX);
  Serial.print(F("   "));
  long_result = divide0( unsigned_int_numerator ) ;
  Serial.print(long_result,HEX);
  Serial.println();
  
  Serial.print(F("ulong  uint   ulong  "));
  long_result = unsigned_int_numerator / unsigned_long_denominator ;
  Serial.print(long_result,HEX);
  Serial.print(F("   "));
  long_result = divide0( unsigned_int_numerator ) ;
  Serial.print(long_result,HEX);
  Serial.println();
ulong  byte   byte   FFFFFFFF   FF
ulong  byte   int    FFFFFFFF   FF
ulong  byte   uint   FFFF   FF
ulong  byte   long   FFFFFFFF   FF
ulong  byte   ulong  FFFFFFFF   FF
ulong  int    byte   FFFFFFFF   FFFFFFFF
ulong  int    int    FFFFFFFF   FFFFFFFF
ulong  int    uint   FFFF   FFFFFFFF
ulong  int    long   FFFFFFFF   FFFFFFFF
ulong  int    ulong  FFFFFFFF   FFFFFFFF
ulong  long   byte   FFFFFFFF   FFFFFFFF
ulong  long   int    FFFFFFFF   FFFFFFFF
ulong  long   uint   FFFFFFFF   FFFFFFFF
ulong  long   long   FFFFFFFF   FFFFFFFF
ulong  uint   ulong  FFFFFFFF   FFFF
ulong  uint   byte   FFFF   FFFF
ulong  uint   int    FFFF   FFFF
ulong  uint   uint   FFFF   FFFF
ulong  uint   long   FFFFFFFF   FFFF
ulong  uint   ulong  FFFFFFFF   FFFF

I was surprised to see that divide by 0 for a long or unsigned long doesn't always result in the same answer, but I think I understand why it's happening, the maths are done before knowing the type of the result.

Normally, I would change the equation to something like: long_result = (unsigned long)unsigned_byte_numerator / unsigned_int_denominator ; But I'm hoping to not restrict these macros to specific types (and can't actually figure out how to restrict them anyway).

I guess the worst one is that unsigned int as a denominator has a different result than int as a denominator, so sizeof() isn't the answer.

Can anyone point me in the right direction to either figure out the variable type, or determine how to handle the exceptions based on the limited information available within the macro?

I can't figure out how to get the correct result

There is no correct result for division by zero.

however for divide by zero, it doesn’t always return the correct result, and I can’t figure out how to get the correct result.

What is the correct result ?

See Problems with zero

No matter how many bits you choose to hold your answer, it will never be enough. Infinity can only be held in infinity bits. Any storage medium you choose will always be too small.

KenF: No matter how many bits you choose to hold your answer, it will never be enough. Infinity can only be held in infinity bits. Any storage medium you choose will always be too small.

However true it may be that to store infinity takes an infinity of bits, it's irrelevant here Ken. Division by 0 is not infinity, it's undefined.

yes, nice smart answers, there actually is no correct result for n/0, true. however if the compiler says n/0 = 0xFFFF then I want to return from the macro with 0xFFFF not 0xFFFFFFFF

if I return 0xFFFFFFFF to a byte, then it truncates to 0xFF and that's the same as you'd get if you did the divide without the macro, but for longs you can't predict (in a macro) what the result should be in order to be compatible with the way the compiler answer the question "what's N/0?"

anyway, I've figured out the solution, use overload functions

Thanks for the useless answers, I've grown to expect these types of answers on this board.

@Bob: neat video...thanks for post!

Sorry that you found the truth "useless", but we can't help that. That's your problem, nobody else's.

Definitive evidence of why you can't divide by zero on an arduino.

KenF: Definitive evidence of why you can't divide by zero on an arduino.

No wonder they were so keen to get us to trap division by 0 when I was learning Fortran 40 years ago, since that was on an IBM370 the size of a house. And i daresay the punched cards were a fire risk, too.

There are all sorts of integer promotion and sign extension rules.

I think part of the discrepancy is that the constant 0 is not being promoted to 'int' when doing math with an 'unsigned char' (byte). That is why you get the value FF when dividing an unsigned char by a small numeric constant. When you divide an unsigned char variable by an unsigned char variable they both get promoted to 'int' and the result then gets sign extended.

If you want a consistent result for different types you should cast all your operands to the desired result type. That bypasses the integer promotion and sign extension rules.

But

JimboZA: However true it may be that to store infinity takes an infinity of bits, it's irrelevant here Ken. Division by 0 is not infinity, it's undefined.

Well the problem is, that you'll also need an extra bit for the sign. ;)

Here's the thing....

Let's say we're dividing a by b to get c:

a/b = c

Rearrange:

a = b.c

In our division by 0 case, b = 0 so:

a = 0.c

The right hand side is always 0, since 0 x anything is 0, and that's regardless of the value of c.

If a is non-zero, then we have a non-zero lhs, but a zero rhs FOR ALL c.

But c is the answer we're looking for in a/b = c, so bottom line is, no value of c, not even infinity, will ever make the rhs non-zero for b=0. Thus, there is no answer, not even infinity, to dividing non zero a, by 0.

If a=0, it's even easier. Since the lhs is 0, and the rhs is 0 anyway regardless of c, ALL values of c satisfy the division by 0.

JimboZA: Here's the thing....

Let's say we're dividing a by b to get c:

a/b = c

Rearrange:

a = b.c

In our division by 0 case, b = 0 so:

a = 0.c

The right hand side is always 0, since 0 x anything is 0, and that's regardless of the value of c.

If a is non-zero, then we have a non-zero lhs, but a zero rhs FOR ALL c.

But c is the answer we're looking for in a/b = c, so bottom line is, no value of c, not even infinity, will ever make the rhs non-zero for b=0. Thus, there is no answer, not even infinity, to dividing non zero a, by 0.

If a=0, it's even easier. Since the lhs is 0, and the rhs is 0 anyway regardless of c, ALL values of c satisfy the division by 0.

So what would you get if you divide 3 by infinity?

I have no idea. What's that got to do with dividing by 0?

JimboZA: I have no idea. What's that got to do with dividing by 0?

Well infinity goes into 3, zero times. Which proves that 3 is the product of 0 and infinity ;)

Now I know how a fish feels when someone steals its bicycle.

When the result is a long or unsigned long, and N is not an unsigned long, and zero is not an unsigned long.

I don't think this has much to do with dividing by zero; it's just another example of C's somewhat confusing rules for the promotion of operands. If there are no "long" operands, the math will be performed using int-sized calculations. That seems to yield 0xFFFF for dividing by 0. If you assign that int result to a long, you get 0xFFFFFFFF. If you assign it to an unsigned long, you get 0x0000FFFF. (I can't quite tell whether that matches your test program...)

That seems to yield 0xFFFF for dividing by 0

Since dividing by 0 is mathematically undefined, and therefore not allowed, surely the solution is to trap the fact that division by 0 is imminent and provide the user with some kind of warning?

I have written a series of macros and functions which manage to divide expeditiously, however for divide by zero, it doesn’t always return the correct result, and I can’t figure out how to get the correct result.

There is no correct result. From the C++ standard:

If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined.

If the second operand of / or % is zero the behavior is undefined.

There is no “correct” result for undefined behaviour. The compiler is allowed to do anything, or nothing.

Thanks for the useless answers, I’ve grown to expect these types of answers on this board.

Take your comments elsewhere. You asked a question, if you don’t like the answer, too bad.

however if the compiler says n/0 = 0xFFFF then I want to return from the macro with 0xFFFF not 0xFFFFFFFF

The compiler is not required to do any defined thing.


Since dividing by 0 is mathematically undefined, and therefore not allowed, surely the solution is to trap the fact that division by 0 is imminent and provide the user with some kind of warning?

Exactly.

Mr. This Forum Is Useless should be trapping this situation in his code and replacing it with whatever-behaviour-he-wants.