How can I divide two longs in an accurate double?

I'm programming a CNC on their own, and I'm in the middle of my code, and now I have two variables of type long that, when divided results in an exact fractional value, but the Arduino returning a fractional value of only 2 numbers after the dot.

long x = 99; long y = 9; long z = 9999; long m = 9999;

(double)x/(double)m; //result 0.01 (double)y/(double)m; //result 0.00 (double)z/(double)m; //result 1.00

How I can get more precise values dividing two long?

p.s.:The max result of the division will be 1.00

The precision is okay, but it is default printed with 2 decimal places. https://www.arduino.cc/en/Serial/Print

float x = 1.2345678;
Serial.print( x, 6);

Oh, so is that! Now i can continue the code, thankyou Peter_n! :D

Back when I worked with NC/CNC ot was all G-codes with leading zeros, but how about this way of using integers as it’s like working in thousandths instead of inches except millionths to get more decimal places.
If I need 3 places, I work to 6 and roundoff doesn’t reach my significant digits that do matter.

Also, 32-bit floats (the only kind) on Arduino are over 100x slower than 32-bit longs.
Floats aren’t always clean, what should be 1.0 can come up 0.9999999 just from operating on floats.

This working example shows 6 places, but you can see how to change that if desired?

long sixPlaces = 1000000L;

void printSixPlaces( long val )
{
  Serial.print( val / sixPlaces );
  Serial.print( "." );
  val = val % sixPlaces;
  
  if ( val < 100000L ) // the ladder ain't pretty
  {
    Serial.print( "0" ); // but it's faster than 
    
    if ( val < 10000L )  // a loop with a divide in it.
    {
      Serial.print( "0" );
      
      if ( val < 1000L )
      {
        Serial.print( "0" );
        
        if ( val < 100L )
        {
          Serial.print( "0" );
          
          if ( val < 10L )
          {
            Serial.print( "0" );
          }
        }
      }
    }
  }
  Serial.print( val % sixPlaces );
}

void setup()
{
  // put your setup code here, to run once:
  Serial.begin( 115200 );

  long x = 99;
  long y = 9;
  long z = 9999;
  long m = 9999;

  // (double)x/(double)m; //result 0.01
  // (double)y/(double)m; //result 0.00
  // (double)z/(double)m; //result 1.00

  Serial.println( );
  Serial.println( "This is what you show humans." );
  Serial.println( );
  Serial.print( "  " );
  Serial.print( x );
  Serial.print( " / " );
  Serial.print( m );
  Serial.print( " = " );
  printSixPlaces( sixPlaces * x / m );
  Serial.println( );
  Serial.print( "   " );
  Serial.print( y );
  Serial.print( " / " );
  Serial.print( m );
  Serial.print( " = " );
  printSixPlaces( sixPlaces * y / m );
  Serial.println( );
  Serial.print( z );
  Serial.print( " / " );
  Serial.print( m );
  Serial.print( " = " );
  printSixPlaces( sixPlaces * z / m );
  Serial.println( );

  Serial.println( );
  Serial.println( "These are what your code works with." );
  Serial.println( );
  Serial.println( sixPlaces * x / m );
  Serial.println( sixPlaces * y / m );
  Serial.println( sixPlaces * z / m );
}

void loop() 
{
  // put your main code here, to run repeatedly:

}

For a CNC application, you really don't want to use floating point at all, other than perhaps to generate the displayed value for a DRO. You surely don't want to use them anywhere in the motion calculations, or the rounding errors will accumulate significant errors.

Regards, Ray L.

Be careful: the result of a multiplication by 1000000 could easily exceed the limit of a "long" (signed long: 2,147,483,647; unsigned long: 4,294,967,295). If it does, then you will get garbage.

If I were you, I would implement long division. https://en.wikipedia.org/wiki/Long_division

odometer: Be careful: the result of a multiplication by 1000000 could easily exceed the limit of a "long" (signed long: 2,147,483,647; unsigned long: 4,294,967,295). If it does, then you will get garbage.

If I were you, I would implement long division. https://en.wikipedia.org/wiki/Long_division

If you were me, you'd have to forget what I know about maths first, maths I learned without handheld electronic calculators that only started to get under-$100-cheap during my last year of HS.

You'd have to forget all the business and production code I wrote back before PC's had FPU's. That's over 10 years of getting it right to the penny or other mark for over 10 years.

I can multiply 1 million times 2000 and not overflow a long, so in this case I decided to show 6 places where 3 or 4 would do.

If I wanted to go higher, I'd cast my data as 64-bit long long and do my multiplies and divides there. In fact with Forth that is how the scaling operator, */, works except it does 16 to 32 bit.

Yeah, you do have to take care that your possible results of any step do not exceed your variables. Last time I checked, string.h functions required me to keep my strings inside my array bounds too.

If I wanted to go higher, I’d cast my data as 64-bit long long and do my multiplies and divides there.

That chews up a lot of progmem.

By “long division” I meant:

  • Do integer divide.
  • Show quotient as next digit of result.
  • Multiply remainder by 10, and use the result as the new dividend.
  • Lather, rinse, repeat.

Here is a little C++ program I wrote:

#include <iostream>
using namespace std;

int main() {
  // simulation of long division
  
  // numbers to be divided
 int32_t top = 1000000;
 int32_t bottom = 998; // this not to exceed 200 million!! 
  // how many decimal places?
 int32_t places = 18;

  // do the division
 int32_t piece;
 int32_t remainder;
 cout << top << " / " << bottom << " = ";
  piece = top / bottom; // integer part of quotient
 cout << piece << "."; // output the integer part  
 remainder = top - (piece * bottom); // remainder for next step
  // next we calculate the "decimal" part of the quotient 
 for (int32_t i = 1; i <= places; i++) {
  remainder *= 10; // "bring down" a zero
  piece = remainder / bottom; // next digit of quotient
    cout << piece; // output that digit  
  remainder -= (piece * bottom); // new remainder
 }
  cout << " (to " << places << " decimal places)";
 return 0;
}

Arduino version:

  // simulation of long division
  
  // numbers to be divided
 int32_t top = 1000000;
 int32_t bottom = 998; // this not to exceed 200 million!! 
  // how many decimal places?
 int32_t places = 18;

  // do the division
 int32_t piece;
 int32_t remainder;
  Serial.print(top, 10);
  Serial.print(" / ");
  Serial.print(bottom, 10);
  Serial.print(" = ");
  piece = top / bottom; // integer part of quotient
  Serial.print(piece, 10); // output the integer part  
 remainder = top - (piece * bottom); // remainder for next step
  // next we calculate the "decimal" part of the quotient 
 for (int32_t i = 1; i <= places; i++) {
  remainder *= 10; // "bring down" a zero
  piece = remainder / bottom; // next digit of quotient
    Serial.print(piece,10); // output that digit  
  remainder -= (piece * bottom); // new remainder
 }
  Serial.print(" (to ");
  Serial.print(places, 10);
  Serial.print(" decimal places)");

odometer: That chews up a lot of progmem.

I think we've been talking past each other. The data is 32-bit but the calculations are performed with 64-bit variables.

With scaling as an example; I want to scale 0-1023 analog reads to 0-4995 millivolts values. If I multiply the input by 5000 and then divide that by 1024, not divide then multiply. Integers round down to 1's. If you then multiply you magnify any step effects. Honest, I learned this from Charles Moore via Forth but it can be tested.

By "long division" I meant:

  • Do integer divide.
  • Show quotient as next digit of result.
  • Multiply remainder by 10, and use the result as the new dividend.
  • Lather, rinse, repeat.

Calculations can be done to high precision using BCD or manipulating text digit something like that. It's not going to be super fast but it can be as

If you divide 64-bit integers in your program, then the size of the compiled code becomes much bigger.

odometer: If you divide 64-bit integers in your program, then the size of the compiled code becomes much bigger.

That's the price of precision and speed over a huge range without an FPU.

For most purposes, int data promoted to long during calculations is enough but you have already called 9 places any digit (32-bit integer) limited.

Multiply first preserves accuracy, divide first introduces inaccuracy.

If needs require, use the Big Number library or work in BCD or text.